[1mdiff --git a/50_bubble.py b/50_bubble.py[m
[1mindex a1fc72f..a0a236c 100644[m
[1m--- a/50_bubble.py[m
[1m+++ b/50_bubble.py[m
[36m@@ -249,6 +249,13 @@[m [mBubble is open source:[m
[m
return f'=> /dashboard {self.user.avatar} {self.user.name}{notifs}{mode}\n'[m
[m
[32m+[m[32m def feed_flair(self, post, context):[m
[32m+[m[32m flair = User.render_flair(post.poster_flair,[m
[32m+[m[32m context,[m
[32m+[m[32m abbreviate=True,[m
[32m+[m[32m user_mod=post.user in self.context_mod_ids)[m
[32m+[m[32m return f' [{flair}]' if flair else ''[m
[32m+[m
def feed_entry(self, post, context=None, omit_rotate_info=False, is_activity_feed=False):[m
is_issue_tracker = self.is_context_tracker[m
is_comment = post.parent != 0 # Flat feeds intermingle comments with posts.[m
[36m@@ -276,7 +283,6 @@[m [mBubble is open source:[m
else:[m
age = post.age(tz=self.tz)[m
bell = ' ๐' if post.num_notifs else ''[m
[31m- flair = f' [{post.poster_flair}]' if post.poster_flair else ''[m
[m
SHORT_PREVIEW_LEN = 160[m
[m
[36m@@ -320,7 +326,7 @@[m [mBubble is open source:[m
post_icon = post.poster_avatar[m
post_label = post.poster_name[m
if not (is_user_post and context and context.id == post.subspace):[m
[31m- post_label += flair[m
[32m+[m[32m post_label += self.feed_flair(post, context)[m
post_path = f'/u/{post.poster_name}'[m
meta_icon = '๐ฌ'[m
[m
[36m@@ -352,7 +358,7 @@[m [mBubble is open source:[m
# Last line in the metadata.[m
meta = [][m
if is_comment or (sub and not is_activity_feed):[m
[31m- meta.append(post.poster_name + flair)[m
[32m+[m[32m meta.append(post.poster_name + self.feed_flair(post, context))[m
if cmt:[m
meta.append(cmt)[m
if likes:[m
[1mdiff --git a/db-migrate.sql b/db-migrate.sql[m
[1mindex 5728e8a..cc2fc41 100644[m
[1m--- a/db-migrate.sql[m
[1m+++ b/db-migrate.sql[m
[36m@@ -32,4 +32,7 @@[m [mUPDATE users SET notif=notif|0x040000;[m
UPDATE users SET notif=notif|0x100000;[m
[m
-- Migration from v7 to v8 --[m
[31m-ALTER TABLE users ADD COLUMN flair VARCHAR(30) DEFAULT '';[m
\ No newline at end of file[m
[32m+[m[32mALTER TABLE users ADD COLUMN flair VARCHAR(30) DEFAULT '';[m
[32m+[m
[32m+[m[32m-- Migration from v8.0 to v8.1 --[m
[32m+[m[32mALTER TABLE users MODIFY COLUMN flair VARCHAR(1000) DEFAULT '';[m
[1mdiff --git a/feeds.py b/feeds.py[m
[1mindex 66e86a4..5111fa7 100644[m
[1m--- a/feeds.py[m
[1m+++ b/feeds.py[m
[36m@@ -298,7 +298,10 @@[m [mdef make_post_page_or_configure_feed(session):[m
if session.c_user:[m
page = f'# {session.c_user.avatar} {session.c_user.name}\n'[m
if session.c_user.flair:[m
[31m- page += f"[{session.c_user.flair}]\n"[m
[32m+[m[32m flair = User.render_flair(session.c_user.flair, session.context,[m
[32m+[m[32m long_form=True, db=session.db)[m
[32m+[m[32m if flair:[m
[32m+[m[32m page += f"\n{flair}\n"[m
else:[m
page = f'# {subspace.title()}\n'[m
page += f'=> /{subspace.title()} {subspace.title()}\n'[m
[36m@@ -345,13 +348,13 @@[m [mdef make_post_page(session, post):[m
page = ''[m
focused_cmt = None[m
[m
[31m- def commenter_flair(cmt, post):[m
[31m- cmt_flair = [cmt.poster_flair] if cmt.poster_flair else [][m
[31m- if post and cmt.user == post.user:[m
[31m- cmt_flair = ['op'] + cmt_flair[m
[31m- elif cmt.user in session.context_mod_ids:[m
[31m- cmt_flair = ['mod'] + cmt_flair[m
[31m- return f" [{', '.join(cmt_flair)}]" if cmt_flair else ""[m
[32m+[m[32m def commenter_flair(cmt, post, abbreviate, with_context=None):[m
[32m+[m[32m flair = User.render_flair(cmt.poster_flair,[m
[32m+[m[32m with_context if with_context else session.context,[m
[32m+[m[32m abbreviate=abbreviate,[m
[32m+[m[32m user_mod=cmt.user in session.context_mod_ids,[m
[32m+[m[32m user_op=post and cmt.user == post.user)[m
[32m+[m[32m return f' [{flair}]' if flair else ''[m
[m
if is_comment_page:[m
# Switch to the parent post, but display it in preview mode.[m
[36m@@ -370,7 +373,9 @@[m [mdef make_post_page(session, post):[m
return 51, 'Not found'[m
page += f'=> /help/deleted-post ๐ Comment on a deleted post (ID:{post_id})\n\n'[m
page += session.render_post(focused_cmt)[m
[31m- flair = commenter_flair(focused_cmt, post)[m
[32m+[m[32m flair = commenter_flair(focused_cmt, post,[m
[32m+[m[32m abbreviate=False,[m
[32m+[m[32m with_context=db.get_subspace(post.subspace))[m
page += f'\n=> /u/{focused_cmt.poster_name} {focused_cmt.poster_avatar} {focused_cmt.poster_name}{flair}\n'[m
page += f'{focused_cmt.age()}\n'[m
[m
[36m@@ -449,7 +454,13 @@[m [mdef make_post_page(session, post):[m
page += '\n'[m
if post.tags:[m
page += '### ' + post.tags + '\n'[m
[31m- flair = f" [{post.poster_flair}]" if post.poster_flair else ""[m
[32m+[m[32m #flair = f" [{post.poster_flair}]" if post.poster_flair else ""[m
[32m+[m
[32m+[m[32m flair = User.render_flair(post.poster_flair,[m
[32m+[m[32m session.context,[m
[32m+[m[32m user_mod=post.user in session.context_mod_ids)[m
[32m+[m[32m if flair: flair = f" [{flair}]"[m
[32m+[m
poster_link = f'=> /u/{post.poster_name} {post.poster_avatar} {post.poster_name}{flair}\n'[m
if session.is_context_tracker:[m
page += f'=> /{session.context.title()} ๐ Issue #{post.issueid} in {session.context.title()}\n'[m
[36m@@ -590,7 +601,7 @@[m [mdef make_post_page(session, post):[m
cmt.ymd_hm(tz=session.tz, date_fmt='%b %d', time_prefix='at ') if elapsed_hours < 24 * 180 else \[m
cmt.ymd_hm(tz=session.tz, time_prefix='at ')[m
[m
[31m- cmt_flair = commenter_flair(cmt, post)[m
[32m+[m[32m cmt_flair = commenter_flair(cmt, post, abbreviate=True)[m
if not session.is_archive:[m
src = f'=> /u/{cmt.poster_name}/{cmt.id} {cmt.poster_avatar} {cmt.poster_name}{cmt_flair} ยท {comment_age}:\n'[m
else:[m
[36m@@ -715,13 +726,14 @@[m [mdef make_feed_page(session):[m
elif not context:[m
topinfo += f"{session.bubble.site_info if session.user else session.bubble.site_info_nouser}\n"[m
else:[m
[31m- if c_user and (c_user.info or c_user.url):[m
[32m+[m[32m if c_user and (c_user.info or c_user.url or c_user.flair):[m
if c_user.info:[m
topinfo += c_user.info + '\n'[m
[31m- if c_user.flair:[m
[31m- topinfo += f"[{c_user.flair}]\n"[m
if c_user.url:[m
topinfo += f'=> {c_user.url}\n'[m
[32m+[m[32m if c_user.flair:[m
[32m+[m[32m flair = User.render_flair(c_user.flair, context=None, long_form=True, db=session.db)[m
[32m+[m[32m topinfo += f'\n{flair}'[m
elif context:[m
if context.info:[m
topinfo += context.info + '\n'[m
[1mdiff --git a/model.py b/model.py[m
[1mindex e839ec4..1330c77 100644[m
[1m--- a/model.py[m
[1m+++ b/model.py[m
[36m@@ -233,6 +233,15 @@[m [mclass User:[m
# Roles:[m
BASIC, ADMIN, LIMITED = range(3)[m
[m
[32m+[m[32m # Flair types:[m
[32m+[m[32m FLAIRS = {[m
[32m+[m[32m 'โก': 'Self description',[m
[32m+[m[32m '๐๏ธ': 'Absence',[m
[32m+[m[32m 'โ๏ธ': 'Writing style',[m
[32m+[m[32m '๐ฃ๏ธ': 'Interaction style',[m
[32m+[m[32m '๐': 'Note from moderator',[m
[32m+[m[32m }[m
[32m+[m
# Sort modes:[m
SORT_POST_RECENT = 'r'[m
SORT_POST_HOTNESS = 'h'[m
[36m@@ -289,6 +298,90 @@[m [mclass User:[m
[m
return None[m
[m
[32m+[m[32m def render_flair(user_flair, context, abbreviate=False, long_form=False, db=None,[m
[32m+[m[32m user_mod=False, user_op=False):[m
[32m+[m[32m """[m
[32m+[m[32m Arguments:[m
[32m+[m[32m db (Database): database object for looking up subspace names[m
[32m+[m[32m in the long form.[m
[32m+[m[32m context (Subspace): where the flair is being shown. If None,[m
[32m+[m[32m the flair is being shown in the home feed.[m
[32m+[m[32m abbreviate (bool): user-provided text is omitted and only[m
[32m+[m[32m icons are shown.[m
[32m+[m[32m long_form (bool): a description of the flair type is included[m
[32m+[m[32m and the output is formatted onto multiple lines instead[m
[32m+[m[32m of being a single line.[m
[32m+[m[32m """[m
[32m+[m[32m if not user_flair.strip():[m
[32m+[m[32m return ''[m
[32m+[m
[32m+[m[32m out = ''[m
[32m+[m
[32m+[m[32m for flair in user_flair.split('\n'):[m
[32m+[m[32m pos = flair.find(':')[m
[32m+[m[32m scope = flair[:pos].strip() if pos >= 0 else None[m
[32m+[m[32m is_admin_assigned = (scope and scope.startswith('*'))[m
[32m+[m[32m if is_admin_assigned:[m
[32m+[m[32m scope = scope[1:][m
[32m+[m[32m label = flair[pos + 1:].strip() if pos >= 0 else flair[m
[32m+[m[32m icon = ''[m
[32m+[m[32m if len(label):[m
[32m+[m[32m for key in User.FLAIRS:[m
[32m+[m[32m if label.startswith(key):[m
[32m+[m[32m icon = key[m
[32m+[m[32m label = label[len(key):].strip()[m
[32m+[m[32m break[m
[32m+[m
[32m+[m[32m #print(user_flair, icon, label, scope)[m
[32m+[m
[32m+[m[32m has_abbrev = False[m
[32m+[m
[32m+[m[32m if long_form:[m
[32m+[m[32m # Show everything in the long form.[m
[32m+[m[32m if icon:[m
[32m+[m[32m out += icon + ' '[m
[32m+[m[32m if scope:[m
[32m+[m[32m subspace = db.get_subspace(id=abs(int(scope)))[m
[32m+[m[32m if subspace:[m
[32m+[m[32m scope = f" (in {subspace.title()})"[m
[32m+[m[32m else:[m
[32m+[m[32m scope = " (in a deleted subspace)"[m
[32m+[m[32m else:[m
[32m+[m[32m scope = ''[m
[32m+[m[32m if icon:[m
[32m+[m[32m out += f"{User.FLAIRS[icon]}: "[m
[32m+[m[32m elif not scope and is_admin_assigned:[m
[32m+[m[32m out += '๐ Assigned flair: '[m
[32m+[m[32m else:[m
[32m+[m[32m out += '๐ Personal flair: '[m
[32m+[m[32m out += f"{label}{scope}{' (set by admin)' if is_admin_assigned else ''}\n"[m
[32m+[m
[32m+[m[32m elif not scope or (context and int(scope) == context.id):[m
[32m+[m[32m # A global flair is displayed everywhere, otherwise the scope must match[m
[32m+[m[32m # current context.[m
[32m+[m[32m if len(out): out += ' ' if abbreviate else ', '[m
[32m+[m[32m out += icon[m
[32m+[m[32m if not abbreviate or not scope:[m
[32m+[m[32m if len(out): out += ' '[m
[32m+[m[32m out += label[m
[32m+[m[32m elif not icon:[m
[32m+[m[32m has_abbrev = True[m
[32m+[m
[32m+[m[32m if not long_form:[m
[32m+[m[32m if user_op or user_mod:[m
[32m+[m[32m if len(out):[m
[32m+[m[32m out = ', ' + out[m
[32m+[m[32m if user_op and user_mod:[m
[32m+[m[32m out = 'OP/mod' + out[m
[32m+[m[32m elif user_op:[m
[32m+[m[32m out = 'OP' + out[m
[32m+[m[32m elif user_mod:[m
[32m+[m[32m out = 'mod' + out[m
[32m+[m[32m if has_abbrev:[m
[32m+[m[32m out += '...'[m
[32m+[m
[32m+[m[32m return out[m
[32m+[m
[m
class Subspace:[m
OMIT_FROM_ALL_FLAG = 0x1[m
[36m@@ -500,7 +593,7 @@[m [mclass Database:[m
db.execute("""CREATE TABLE IF NOT EXISTS users ([m
id INT PRIMARY KEY AUTO_INCREMENT,[m
name VARCHAR(30) UNIQUE,[m
[31m- flair VARCHAR(30) DEFAULT '',[m
[32m+[m[32m flair VARCHAR(1000) DEFAULT '',[m
info VARCHAR(1000) DEFAULT '',[m
url VARCHAR(1000) DEFAULT '',[m
recovery VARCHAR(1000) DEFAULT '',[m
text/plain
This content has been proxied by September (ba2dc).