refactor: tags

dev
gravel 1 year ago
parent 378589e8ac
commit 6d04a5a480
Signed by: gravel
GPG Key ID: C0538F3C906B308F

@ -170,11 +170,10 @@ async function onLoad() {
* @param {boolean} [inMainView] Whether to apply styles for tags in outside view. * @param {boolean} [inMainView] Whether to apply styles for tags in outside view.
* @returns HTMLElement * @returns HTMLElement
*/ */
const tagBody = ({text, type, description}, inMainView = false) => element.span({ const tagBody = ({text, type, description = ""}, inMainView = false) => element.span({
// todo: truncate textContent: text.slice(0, 16),
textContent: text,
className: `room-label room-label-view-${inMainView ? 'main' : 'secondary'} room-label-${type} badge`, className: `room-label room-label-view-${inMainView ? 'main' : 'secondary'} room-label-${type} badge`,
title: description title: description || `Tag: ${text}`
}); });
/** /**

@ -111,8 +111,14 @@
// Custom properties // Custom properties
/** /**
* @var string[] $tags * @var string[] $string_tags
* String tags applied to room by creator or submitter. * String tags originally applied to room.
*/
private array $string_tags = [];
/**
* @var CommunityTag[] $tags
* Tag-based information from multiple sources.
* *
* Custom attribute. * Custom attribute.
*/ */
@ -126,7 +132,11 @@
*/ */
private ?string $language_flag; private ?string $language_flag;
private function __construct(CommunityServer $server, array $details) { private function __construct(
CommunityServer $server,
array $details,
bool $suppress_processing = false
) {
$this->server = $server; $this->server = $server;
$this->active_users = $details['active_users']; $this->active_users = $details['active_users'];
$this->active_users_cutoff = $details['active_users_cutoff']; $this->active_users_cutoff = $details['active_users_cutoff'];
@ -142,15 +152,25 @@
$this->read = $details['read']; $this->read = $details['read'];
$this->write = $details['write']; $this->write = $details['write'];
$this->upload = $details['upload']; $this->upload = $details['upload'];
$this->string_tags = $details['string_tags'] ?? [];
if ($suppress_processing) return;
$this->extract_tags_from_description(); $this->extract_tags_from_description();
$this->tags = isset($details['tags'])
? CommunityTag::from_details_array($details['tags'])
: $this->get_derived_tags();
} }
/** /**
* Regular expression matching tags specified in the Community description. * Create incomplete CommunityRoom instance from intermediate data.
*/ */
private const DESCRIPTION_TAGS_SPECIFICATION = '/(#[^#()@., ]+(?:,?\s*|\s+|$))+\s*.?$/'; public static function _from_intermediate_data(
CommunityServer $server,
array $details
) {
return new CommunityRoom($server, $details, suppress_processing: true);
}
/** /**
* Return an optional Unicode emoji of region matching * Return an optional Unicode emoji of region matching
@ -178,6 +198,11 @@
return ""; return "";
} }
/**
* Regular expression matching tags specified in the Community description.
*/
private const DESCRIPTION_TAGS_SPECIFICATION = '/(#[^#()@., ]+(?:,?\s*|\s+|$))+\s*.?$/';
/** /**
* Pre-processes SOGS data by treating description-trailing hashtags as room tags. * Pre-processes SOGS data by treating description-trailing hashtags as room tags.
*/ */
@ -218,9 +243,6 @@
public function jsonSerialize(): array { public function jsonSerialize(): array {
$details = get_object_vars($this); $details = get_object_vars($this);
unset($details['server']); unset($details['server']);
$details['tags'] = $this->get_raw_tags();
$details['tags_custom'] = $this->get_derived_tags();
$details['language_flag'] = $this->get_language_flag();
return $details; return $details;
} }
@ -232,12 +254,13 @@
unset($details['server']); unset($details['server']);
unset($details['tags']); unset($details['tags']);
unset($details['language_flag']); unset($details['language_flag']);
unset($details['string_tags']);
return array( return array(
"room" => $details, "room" => $details,
"room_extra" => array( "room_extra" => array(
"join_url" => $this->get_join_url(), "join_url" => $this->get_join_url(),
"language_flag" => $this->get_language_flag(), "language_flag" => $this->language_flag,
"tags" => $this->get_raw_tags() "tags" => $this->string_tags
) )
); );
} }
@ -247,15 +270,7 @@
* @param CommunityServer $server * @param CommunityServer $server
*/ */
public static function from_details($server, array $details) { public static function from_details($server, array $details) {
$room = new CommunityRoom($server, $details); return new CommunityRoom($server, $details);
$has_tags = isset($details['tags']);
if ($has_tags) {
$room->add_tags($details['tags']);
}
if (isset($details['language_flag'])) {
$room->language_flag = $details['language_flag'];
}
return $room;
} }
/** /**
@ -394,11 +409,17 @@
* @param string[] $tags * @param string[] $tags
*/ */
public function add_tags(array $tags) { public function add_tags(array $tags) {
$new_string_tags = [];
foreach ($tags as $tag) { foreach ($tags as $tag) {
if (!$this->parse_language_tag($tag)) { $this->string_tags[] = $tag;
$this->tags[] = $tag; if ($this->parse_language_tag($tag)) continue;
} $new_string_tags[] = $tag;
} }
$new_tags = CommunityTag::from_user_tags($new_string_tags);
$this->tags = CommunityTag::dedupe_tags([
...$this->tags,
...$new_tags
]);
} }
/** /**
@ -537,8 +558,8 @@
* Return the string tags associated with this Community * Return the string tags associated with this Community
* @return string[] Array of unique string tags. * @return string[] Array of unique string tags.
*/ */
private function get_raw_tags(): array { private function get_string_tags(): array {
return array_unique(array_values($this->tags)); return $this->string_tags;
} }
/** /**
@ -553,92 +574,38 @@
*/ */
$derived_tags = []; $derived_tags = [];
$CHECK_MARK = CommunityTag::CHECK_MARK;
$WARNING = CommunityTag::WARNING_ICON;
$USERS_PER_STAFF = CommunityRoom::USERS_PER_STAFF;
$USERS_PER_STAFF_WARNING = CommunityRoom::USERS_PER_STAFF_WARNING; $USERS_PER_STAFF_WARNING = CommunityRoom::USERS_PER_STAFF_WARNING;
if ($this->is_official_room()) { if ($this->is_official_room()) {
$derived_tags[] = new CommunityTag( $derived_tags[] = ReservedTags::official();
"official",
TagType::RESERVED_TAG,
"This Community is maintained by the Session team. $CHECK_MARK"
);
} }
if ($this->rated_nsfw()) { if ($this->rated_nsfw()) {
$derived_tags[] = $derived_tags[] = ReservedTags::nsfw();
new CommunityTag(
"nsfw",
TagType::WARNING_TAG,
"This Community may contain adult material. $WARNING"
);
} }
if ($this->write && $this->has_good_staff_rating()) { if ($this->write && $this->has_good_staff_rating()) {
$derived_tags[] = $derived_tags[] = ReservedTags::moderated(CommunityRoom::USERS_PER_STAFF);
new CommunityTag(
"moderated",
TagType::RESERVED_TAG,
"This Community has at least 1 staff per $USERS_PER_STAFF active users. $CHECK_MARK"
);
}
/*
if ($this->write && $this->has_poor_staff_rating()) {
$derived_tags[] =
new CommunityTag(
"not modded",
TagType::WARNING_TAG,
"This Community has less than 1 staff per $USERS_PER_STAFF_WARNING active users. $WARNING"
);
} }
*/
if (!$this->write) { if (!$this->write) {
$derived_tags[] = $derived_tags[] = ReservedTags::read_only();
new CommunityTag(
"read-only",
TagType::RESERVED_TAG,
"This Community is read-only."
);
} }
if ($this->write && !$this->upload) { if ($this->write && !$this->upload) {
$derived_tags[] = $derived_tags[] = ReservedTags::no_upload_permission();
new CommunityTag(
"uploads off",
TagType::RESERVED_TAG,
"This Community does not support uploading files or link previews."
);
} }
if ($this->created && $this->created > strtotime("-4 week")) { if ($this->created && $this->created > strtotime("-4 week")) {
$derived_tags[] = $derived_tags[] = ReservedTags::recently_created();
new CommunityTag(
"new",
TagType::RESERVED_TAG,
"This Community was created recently."
);
} }
if (in_array($this->get_room_identifier(), $ROOMS_USED_BY_PROJECT)) { if (in_array($this->get_room_identifier(), $ROOMS_USED_BY_PROJECT)) {
$derived_tags[] = $derived_tags[] = ReservedTags::used_by_project();
new CommunityTag(
"we're here",
TagType::RESERVED_TAG,
"The sessioncommunities.online maintainer(s) can post updates "
. "or respond to feedback in this Community."
);
} }
if (in_array("test", $this->tags) || $this->matched_by_list($TESTING_INCLUDE)) { if ($this->is_testing_room()) {
$derived_tags[] = $derived_tags[] = ReservedTags::testing();
new CommunityTag(
"test",
TagType::RESERVED_TAG,
"This Community is intended for testing only."
);
} }
return $derived_tags; return $derived_tags;
@ -649,8 +616,7 @@
* @return CommunityTag[] Array of tags. * @return CommunityTag[] Array of tags.
*/ */
function get_room_tags(): array { function get_room_tags(): array {
$user_tags = CommunityTag::from_user_tags($this->tags, remove_redundant: true); return $this->tags;
return [...$this->get_derived_tags(), ...$user_tags];
} }
} }

@ -14,15 +14,15 @@
/** /**
* Specifies custom tag added by Community maintainer or Community source. * Specifies custom tag added by Community maintainer or Community source.
*/ */
const USER_TAG = 0; const USER_TAG = 'user';
/** /**
* Specifies basic type of tag reserved for assignment by our aggregator. * Specifies basic type of tag reserved for assignment by our aggregator.
*/ */
const RESERVED_TAG = 1; const RESERVED_TAG = 'reserved';
/** /**
* Specifies warning tag reserved for assignment by our aggregator. * Specifies warning tag reserved for assignment by our aggregator.
*/ */
const WARNING_TAG = 2; const WARNING_TAG = 'warning';
} }
/** /**
@ -32,25 +32,24 @@
/** /**
* Create a new CommunityTag instance. * Create a new CommunityTag instance.
* @param string $text Text the tag should read. * @param string $text Text the tag should read.
* @param int $tag_type Numeric {@link TagType} value. * @param string $tag_type {@link TagType} enumeration value.
* @param string|null $description [optional] Brief explanation of tag. * @param string|null $description [optional] Brief explanation of tag.
*/ */
public function __construct( public function __construct(
string $text, string $text,
int $tag_type = TagType::USER_TAG, string $tag_type = TagType::USER_TAG,
?string $description = "" ?string $description = ""
) { ) {
$this->text = $text; $this->text = CommunityTag::preprocess_tag($text);
$this->type = $tag_type; $this->type = $tag_type;
$this->description = $this->description = $description;
empty($description) ? "Tag: $text" : $description;
} }
/** /**
* @var int $type * @var string $type
* Tag type as given by a {@link TagType} value. * Tag type as given by a {@link TagType} value.
*/ */
public readonly int $type; public readonly string $type;
/** /**
* @var string $text * @var string $text
@ -78,15 +77,30 @@
return strtolower($this->text); 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. * Produce data used to serialize the tag.
*/ */
public function jsonSerialize(): mixed { public function jsonSerialize(): mixed {
// Only used for passing to DOM $details = [];
$details = get_object_vars($this); $details['text'] = $this->get_text();
$details['text'] = html_sanitize($this->get_text());
$details['description'] = html_sanitize($details['description']);
$details['type'] = $this->get_tag_type(); $details['type'] = $this->get_tag_type();
if (!empty($this->description)) {
$details['description'] = $this->description;
}
return $details; return $details;
} }
@ -97,30 +111,28 @@
return $tag; return $tag;
} }
$tag = html_sanitize(html_entity_decode($tag)); $tag = html_entity_decode($tag);
if ($tag[0] == '#') { if ($tag[0] == '#') {
return substr($tag, 1); return substr($tag, 1);
} }
return $tag; return strtolower($tag);
} }
/** /**
* @param string[] $tag_array * @param string[] $tag_array
* @return CommunityTag[] * @return CommunityTag[]
*/ */
private static function from_tag_array(array $tag_array) { private static function from_string_tags(array $tag_array) {
$tags = array_map(function(?string $tag) {
return CommunityTag::preprocess_tag($tag);
}, $tag_array);
$tags = array_filter( $tags = array_filter(
$tags, function(?string $tag) { $tag_array, function(?string $tag) {
return strlen($tag) != 0; return strlen($tag) != 0;
} }
); );
$tags = CommunityTag::dedupe_tags($tags);
return array_map(function(string $tag) { return array_map(function(string $tag) {
return new CommunityTag($tag); return new CommunityTag($tag);
}, $tags); }, $tags);
@ -129,38 +141,54 @@
/** /**
* Constructs the tags given, removing any reserved tags. * Constructs the tags given, removing any reserved tags.
* @param string[] $tags * @param string[] $tags
* @param bool $remove_redundant Removes duplicate and obvious tags. * @param bool $remove_redundant Removes meaningless tags.
* @return CommunityTag[] * @return CommunityTag[]
*/ */
public static function from_user_tags( public static function from_user_tags(
array $tags, bool $remove_redundant = false array $tags,
bool $remove_redundant = true
): array { ): array {
$tags_user = array_filter( $tags_user = array_values(array_filter(
$tags, $tags,
function($tag) { function($tag) {
return !CommunityTag::is_reserved_tag($tag); return !CommunityTag::is_reserved_tag($tag);
} }
); ));
$tags_built = CommunityTag::from_tag_array($tags_user); $tags_built = CommunityTag::from_string_tags($tags_user);
if ($remove_redundant) { if ($remove_redundant) {
$tags_built = CommunityTag::dedupe_tags($tags_built); $tags_built = array_values(
$tags_built = array_filter($tags_built, function(CommunityTag $tag) { array_filter($tags_built, function(CommunityTag $tag) {
$text = strtolower($tag->text); return !in_array($tag->get_text(), CommunityTag::REDUNDANT_TAGS);
return !in_array($text, CommunityTag::REDUNDANT_TAGS); })
}); );
} }
return $tags_built; return $tags_built;
} }
/** /**
* @param string[] $details_array Array of string tags. * @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[] * @return CommunityTag[]
*/ */
public static function from_details_array(array $details_array): array { public static function from_details_array(array $details_array): array {
return CommunityTag::from_user_tags($details_array); return array_map(function($details) {
return CommunityTag::from_details($details);
}, $details_array);
} }
/** /**
@ -168,7 +196,7 @@
* @return CommunityTag[] * @return CommunityTag[]
*/ */
public static function dedupe_tags(array $tags) { public static function dedupe_tags(array $tags) {
return array_unique($tags); return array_values(array_unique($tags));
} }
/** /**
@ -223,11 +251,6 @@
private const REDUNDANT_TAGS = ["session"]; private const REDUNDANT_TAGS = ["session"];
public const NSFW_KEYWORDS = ["nsfw", "porn", "erotic", "18+", "sex"]; public const NSFW_KEYWORDS = ["nsfw", "porn", "erotic", "18+", "sex"];
public const CHECK_MARK = "✅";
public const WARNING_ICON = "⚠️";
/** /**
* Check whether the given user tag is reserved by our aggregator. * Check whether the given user tag is reserved by our aggregator.
*/ */
@ -249,4 +272,87 @@
return in_array(strtolower($tag), CommunityTag::HIGHLIGHTED_TAGS); 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) {
$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) {
$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."
);
}
}
?> ?>

@ -103,9 +103,9 @@
<?php foreach ($room->get_room_tags() as $tag): if (CommunityTag::is_showcased_tag($tag->text)): ?> <?php foreach ($room->get_room_tags() as $tag): if (CommunityTag::is_showcased_tag($tag->text)): ?>
<span <span
class="room-label room-label-view-main <?=$tag->get_tag_classname()?> badge" class="room-label room-label-view-main <?=$tag->get_tag_classname()?> badge"
title="<?=$tag->description?>" title="<?=$tag->get_description_sanitized()?>"
><?= ><?=
truncate($tag->get_text(), 16) truncate($tag->get_text_sanitized(), 16)
?></span> ?></span>
<?php endif; endforeach; ?> <?php endif; endforeach; ?>
<!--/noindex--></span> <!--/noindex--></span>

Loading…
Cancel
Save