Language mapped preferred username

main
bashrc 2026-05-08 10:34:31 +01:00
parent 480544debb
commit e39a73b625
12 changed files with 69 additions and 28 deletions

View File

@ -453,7 +453,8 @@ def daemon_http_get(self) -> None:
if show_vcard(self, self.server.base_dir, if show_vcard(self, self.server.base_dir,
self.path, calling_domain, referer_domain, self.path, calling_domain, referer_domain,
self.server.domain, self.server.translate): self.server.domain, self.server.translate,
self.server.system_language):
return return
# getting the public key for an account # getting the public key for an account

View File

@ -22,7 +22,8 @@ from data import is_a_file
def show_vcard(self, base_dir: str, path: str, calling_domain: str, def show_vcard(self, base_dir: str, path: str, calling_domain: str,
referer_domain: str, domain: str, translate: {}) -> bool: referer_domain: str, domain: str, translate: {},
system_language: str) -> bool:
"""Returns a vcard for the given account """Returns a vcard for the given account
""" """
if not has_accept(self, calling_domain): if not has_accept(self, calling_domain):
@ -68,10 +69,12 @@ def show_vcard(self, base_dir: str, path: str, calling_domain: str,
self.server.vcard_is_active = False self.server.vcard_is_active = False
return True return True
if 'application/vcard+xml' in accept_str: if 'application/vcard+xml' in accept_str:
vcard_str = actor_to_vcard_xml(actor_json, domain, translate) vcard_str = \
actor_to_vcard_xml(actor_json, domain, translate, system_language)
header_type = 'application/vcard+xml; charset=utf-8' header_type = 'application/vcard+xml; charset=utf-8'
else: else:
vcard_str = actor_to_vcard(actor_json, domain, translate) vcard_str = \
actor_to_vcard(actor_json, domain, translate, system_language)
header_type = 'text/vcard; charset=utf-8' header_type = 'text/vcard; charset=utf-8'
if vcard_str: if vcard_str:
msg = vcard_str.encode('utf-8') msg = vcard_str.encode('utf-8')

View File

@ -180,7 +180,8 @@ def _profile_post_save_actor(base_dir: str, http_prefix: str,
curr_session, proxy_type: str, curr_session, proxy_type: str,
send_move_activity: bool, send_move_activity: bool,
self, cached_webfingers: {}, self, cached_webfingers: {},
person_cache: {}, project_version: str) -> None: person_cache: {}, project_version: str,
system_language: str) -> None:
""" HTTP POST save actor json file within accounts """ HTTP POST save actor json file within accounts
""" """
add_name_emojis_to_tags(base_dir, http_prefix, add_name_emojis_to_tags(base_dir, http_prefix,
@ -198,7 +199,7 @@ def _profile_post_save_actor(base_dir: str, http_prefix: str,
actor_json['featured'] = actor_json['id'] + '/collections/featured' actor_json['featured'] = actor_json['id'] + '/collections/featured'
if not actor_json.get('featuredTags'): if not actor_json.get('featuredTags'):
actor_json['featuredTags'] = actor_json['id'] + '/collections/tags' actor_json['featuredTags'] = actor_json['id'] + '/collections/tags'
randomize_actor_images(actor_json) randomize_actor_images(actor_json, system_language)
add_actor_update_timestamp(actor_json) add_actor_update_timestamp(actor_json)
# save the actor # save the actor
save_json(actor_json, actor_filename) save_json(actor_json, actor_filename)
@ -3361,7 +3362,8 @@ def profile_edit(self, calling_domain: str, cookie: str,
curr_session, proxy_type, curr_session, proxy_type,
send_move_activity, send_move_activity,
self, cached_webfingers, self, cached_webfingers,
person_cache, project_version) person_cache, project_version,
system_language)
if _profile_post_deactivate_account(base_dir, nickname, domain, if _profile_post_deactivate_account(base_dir, nickname, domain,
calling_domain, calling_domain,

View File

@ -21,6 +21,7 @@ from utils import lines_in_file
from utils import data_dir from utils import data_dir
from utils import account_is_indexable from utils import account_is_indexable
from utils import is_yggdrasil_address from utils import is_yggdrasil_address
from utils import get_preferred_username
from data import load_list from data import load_list
from data import load_string from data import load_string
from data import is_a_file from data import is_a_file
@ -75,9 +76,8 @@ def _meta_data_instance_v1(show_accounts: bool,
elif admin_actor['type'] != 'Person': elif admin_actor['type'] != 'Person':
is_bot = True is_bot = True
url = \ preferred_username = get_preferred_username(admin_actor, system_language)
http_prefix + '://' + domain_full + '/@' + \ url = http_prefix + '://' + domain_full + '/@' + preferred_username
admin_actor['preferredUsername']
if show_accounts: if show_accounts:
active_accounts: int = no_of_accounts(base_dir) active_accounts: int = no_of_accounts(base_dir)
@ -112,7 +112,7 @@ def _meta_data_instance_v1(show_accounts: bool,
'locked': admin_actor['manuallyApprovesFollowers'], 'locked': admin_actor['manuallyApprovesFollowers'],
'note': '<p>Admin of ' + domain + '</p>', 'note': '<p>Admin of ' + domain + '</p>',
'url': url, 'url': url,
'username': admin_actor['preferredUsername'] 'username': preferred_username
}, },
'description': instance_description, 'description': instance_description,
'languages': [system_language], 'languages': [system_language],

View File

@ -19,6 +19,7 @@ from utils import lines_in_file
from utils import data_dir from utils import data_dir
from utils import account_is_indexable from utils import account_is_indexable
from utils import is_yggdrasil_address from utils import is_yggdrasil_address
from utils import get_preferred_username
from formats import get_image_mime_type from formats import get_image_mime_type
from formats import get_image_extensions from formats import get_image_extensions
from formats import get_audio_extensions from formats import get_audio_extensions
@ -82,9 +83,8 @@ def _meta_data_instance_v2(show_accounts: bool,
elif admin_actor['type'] != 'Person': elif admin_actor['type'] != 'Person':
is_bot = True is_bot = True
url = \ preferred_username = get_preferred_username(admin_actor, system_language)
http_prefix + '://' + domain_full + '/@' + \ url = http_prefix + '://' + domain_full + '/@' + preferred_username
admin_actor['preferredUsername']
if show_accounts: if show_accounts:
active_accounts: int = no_of_accounts(base_dir) active_accounts: int = no_of_accounts(base_dir)

View File

@ -40,6 +40,7 @@ from media import process_meta_data
from flags import is_image_file from flags import is_image_file
from timeFunctions import date_utcnow from timeFunctions import date_utcnow
from timeFunctions import get_current_time_int from timeFunctions import get_current_time_int
from utils import get_preferred_username
from utils import string_starts_with from utils import string_starts_with
from utils import is_yggdrasil_address from utils import is_yggdrasil_address
from utils import get_person_icon from utils import get_person_icon
@ -201,7 +202,7 @@ def _account_exists(base_dir: str, nickname: str, domain: str) -> bool:
is_a_dir(base_dir + '/deactivated/' + nickname + '@' + domain) is_a_dir(base_dir + '/deactivated/' + nickname + '@' + domain)
def randomize_actor_images(person_json: {}) -> None: def randomize_actor_images(person_json: {}, system_language: str) -> None:
"""Randomizes the filenames for avatar image and background """Randomizes the filenames for avatar image and background
This causes other instances to update their cached avatar image This causes other instances to update their cached avatar image
""" """
@ -213,7 +214,7 @@ def randomize_actor_images(person_json: {}) -> None:
# secure names # secure names
rand_str = str(randint(10000000000000, 99999999999999)) # nosec rand_str = str(randint(10000000000000, 99999999999999)) # nosec
base_url = person_id.split('/users/')[0] base_url = person_id.split('/users/')[0]
nickname = person_json['preferredUsername'] nickname = get_preferred_username(person_json, system_language)
person_json['icon']['url'] = \ person_json['icon']['url'] = \
base_url + '/system/accounts/avatars/' + nickname + \ base_url + '/system/accounts/avatars/' + nickname + \
'/avatar' + rand_str + '.' + existing_extension '/avatar' + rand_str + '.' + existing_extension
@ -246,7 +247,7 @@ def get_actor_update_json(actor_json: {}) -> {}:
if actor_json.get('attributionDomains'): if actor_json.get('attributionDomains'):
if isinstance(actor_json['attributionDomains'], list): if isinstance(actor_json['attributionDomains'], list):
attribution_domains = actor_json['attributionDomains'] attribution_domains = actor_json['attributionDomains']
return { actor_update: dict = {
'@context': [ '@context': [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1", "https://w3id.org/security/v1",
@ -362,6 +363,10 @@ def get_actor_update_json(actor_json: {}) -> {}:
"publicKey": actor_json['publicKey'] "publicKey": actor_json['publicKey']
} }
} }
if 'preferredUsernameMap' in actor_json:
actor_update['preferredUsernameMap'] = \
actor_json['preferredUsernameMap']
return actor_update
def get_actor_move_json(actor_json: {}) -> {}: def get_actor_move_json(actor_json: {}) -> {}:
@ -2175,6 +2180,14 @@ def valid_sending_actor(session, base_dir: str,
sending_actor + ' ' + bio_str) sending_actor + ' ' + bio_str)
return False return False
bio_str += ' ' + remove_html(actor_json['preferredUsername']) bio_str += ' ' + remove_html(actor_json['preferredUsername'])
# include the username map
if 'preferredUsernameMap' in actor_json:
username_map = actor_json['preferredUsernameMap']
if isinstance(username_map, dict):
for _, preferred_username in username_map.items():
if not isinstance(preferred_username, str):
continue
bio_str += ' ' + remove_html(preferred_username)
if actor_json.get('attachment'): if actor_json.get('attachment'):
if isinstance(actor_json['attachment'], list): if isinstance(actor_json['attachment'], list):

18
pgp.py
View File

@ -14,6 +14,7 @@ from person import get_actor_json
from flags import is_pgp_encrypted from flags import is_pgp_encrypted
from flags import contains_pgp_public_key from flags import contains_pgp_public_key
from occupation import get_occupation_skills from occupation import get_occupation_skills
from utils import get_preferred_username
from utils import get_url_from_post from utils import get_url_from_post
from utils import safe_system_string from utils import safe_system_string
from utils import get_full_domain from utils import get_full_domain
@ -889,15 +890,17 @@ def pgp_public_key_upload(base_dir: str, session,
return actor_update return actor_update
def actor_to_vcard(actor: {}, domain: str, translate: {}) -> str: def actor_to_vcard(actor: {}, domain: str, translate: {},
system_language: str) -> str:
"""Returns a vcard for a given actor """Returns a vcard for a given actor
""" """
actor_url_str = get_url_from_post(actor['url']) actor_url_str = get_url_from_post(actor['url'])
preferred_username = get_preferred_username(actor, system_language)
vcard_str = 'BEGIN:VCARD\n' vcard_str = 'BEGIN:VCARD\n'
vcard_str += 'VERSION:4.0\n' vcard_str += 'VERSION:4.0\n'
vcard_str += 'REV:' + actor['published'] + '\n' vcard_str += 'REV:' + actor['published'] + '\n'
vcard_str += 'FN:' + remove_html(actor['name']) + '\n' vcard_str += 'FN:' + remove_html(actor['name']) + '\n'
vcard_str += 'NICKNAME:' + actor['preferredUsername'] + '\n' vcard_str += 'NICKNAME:' + preferred_username + '\n'
vcard_str += 'NOTE:' + remove_html(actor['summary']) + '\n' vcard_str += 'NOTE:' + remove_html(actor['summary']) + '\n'
url_str = get_url_from_post(actor['icon']['url']) url_str = get_url_from_post(actor['icon']['url'])
if url_str: if url_str:
@ -909,8 +912,7 @@ def actor_to_vcard(actor: {}, domain: str, translate: {}) -> str:
email_address = get_email_address(actor) email_address = get_email_address(actor)
if email_address: if email_address:
vcard_str += 'EMAIL;TYPE=internet:' + email_address + '\n' vcard_str += 'EMAIL;TYPE=internet:' + email_address + '\n'
vcard_str += 'IMPP:fediverse:' + \ vcard_str += 'IMPP:fediverse:' + preferred_username + '@' + domain + '\n'
actor['preferredUsername'] + '@' + domain + '\n'
if actor.get('vcard:bday'): if actor.get('vcard:bday'):
birthday_str = actor['vcard:bday'] birthday_str = actor['vcard:bday']
if '-' in birthday_str: if '-' in birthday_str:
@ -1004,16 +1006,18 @@ def actor_to_vcard(actor: {}, domain: str, translate: {}) -> str:
return vcard_str return vcard_str
def actor_to_vcard_xml(actor: {}, domain: str, translate: {}) -> str: def actor_to_vcard_xml(actor: {}, domain: str, translate: {},
system_language: str) -> str:
"""Returns a xml formatted vcard for a given actor """Returns a xml formatted vcard for a given actor
""" """
preferred_username = get_preferred_username(actor, system_language)
vcard_str = '<?xml version="1.0" encoding="UTF-8"?>\n' vcard_str = '<?xml version="1.0" encoding="UTF-8"?>\n'
vcard_str += '<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0">\n' vcard_str += '<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0">\n'
vcard_str += ' <vcard>\n' vcard_str += ' <vcard>\n'
vcard_str += ' <fn><text>' + \ vcard_str += ' <fn><text>' + \
remove_html(actor['name']) + '</text></fn>\n' remove_html(actor['name']) + '</text></fn>\n'
vcard_str += ' <nickname><text>' + \ vcard_str += ' <nickname><text>' + \
actor['preferredUsername'] + '</text></nickname>\n' preferred_username + '</text></nickname>\n'
vcard_str += ' <note><text>' + \ vcard_str += ' <note><text>' + \
remove_html(actor['summary']) + '</text></note>\n' remove_html(actor['summary']) + '</text></note>\n'
email_address = get_email_address(actor) email_address = get_email_address(actor)
@ -1021,7 +1025,7 @@ def actor_to_vcard_xml(actor: {}, domain: str, translate: {}) -> str:
vcard_str += ' <email><text>' + email_address + '</text></email>\n' vcard_str += ' <email><text>' + email_address + '</text></email>\n'
vcard_str += ' <impp>' + \ vcard_str += ' <impp>' + \
'<parameters><type><text>fediverse</text></type></parameters>' + \ '<parameters><type><text>fediverse</text></type></parameters>' + \
'<text>' + actor['preferredUsername'] + '@' + domain + \ '<text>' + preferred_username + '@' + domain + \
'</text></impp>\n' '</text></impp>\n'
if actor.get('vcard:bday'): if actor.get('vcard:bday'):
birthday_str = actor['vcard:bday'] birthday_str = actor['vcard:bday']

View File

@ -5227,6 +5227,7 @@ def _novel_fields_for_person(nickname: str, domain: str,
'name', 'name',
'outbox', 'outbox',
'preferredUsername', 'preferredUsername',
'preferredUsernameMap',
'publicKey', 'publicKey',
'summary', 'summary',
'tag', 'tag',

View File

@ -4343,3 +4343,16 @@ def is_private_browser(ua_str: str) -> bool:
('librewolf', 'privacy', 'private')): ('librewolf', 'privacy', 'private')):
return True return True
return False return False
def get_preferred_username(actor: {}, system_language: str) -> str:
"""Returns the preferred username from the given actor
"""
if 'preferredUsernameMap' in actor:
if isinstance(actor['preferredUsernameMap'], dict):
if actor['preferredUsernameMap'].get(system_language):
username: str = \
actor['preferredUsernameMap'][system_language]
if isinstance(username, str):
return username
return actor['preferredUsername']

View File

@ -11,6 +11,7 @@ from flags import is_system_account
from utils import get_mutuals_of_person from utils import get_mutuals_of_person
from utils import get_domain_from_actor from utils import get_domain_from_actor
from utils import get_config_param from utils import get_config_param
from utils import get_preferred_username
from timeFunctions import get_account_timezone from timeFunctions import get_account_timezone
from person import person_box_json from person import person_box_json
from webapp_utils import html_header_with_external_style from webapp_utils import html_header_with_external_style
@ -161,7 +162,7 @@ def html_front_screen(signing_priv_key_pem: str,
"""Show the news instance front screen """Show the news instance front screen
""" """
bold_reading: bool = False bold_reading: bool = False
nickname = profile_json['preferredUsername'] nickname: str = get_preferred_username(profile_json, system_language)
if not nickname: if not nickname:
return "" return ""
if not is_system_account(nickname): if not is_system_account(nickname):
@ -169,7 +170,7 @@ def html_front_screen(signing_priv_key_pem: str,
domain, port = get_domain_from_actor(profile_json['id']) domain, port = get_domain_from_actor(profile_json['id'])
if not domain: if not domain:
return "" return ""
domain_full = domain domain_full: str = domain
if port: if port:
domain_full = domain + ':' + str(port) domain_full = domain + ':' + str(port)

View File

@ -22,6 +22,7 @@ from textmode import text_mode_removals
from unicodetext import uninvert_text from unicodetext import uninvert_text
from unicodetext import standardize_text from unicodetext import standardize_text
from occupation import get_occupation_name from occupation import get_occupation_name
from utils import get_preferred_username
from utils import is_private_browser from utils import is_private_browser
from utils import replace_embedded_map_with_link from utils import replace_embedded_map_with_link
from utils import is_yggdrasil_address from utils import is_yggdrasil_address
@ -1198,7 +1199,7 @@ def html_profile(signing_priv_key_pem: str,
if is_a_file(moved_accounts_filename): if is_a_file(moved_accounts_filename):
show_moved_accounts = True show_moved_accounts = True
nickname: str = profile_json['preferredUsername'] nickname: str = get_preferred_username(profile_json, system_language)
if not nickname: if not nickname:
return "" return ""
if is_system_account(nickname): if is_system_account(nickname):

View File

@ -15,6 +15,7 @@ from session import get_json_valid
from flags import is_float from flags import is_float
from flags import is_moderator from flags import is_moderator
from formats import media_file_mime_type from formats import media_file_mime_type
from utils import get_preferred_username
from utils import replace_embedded_map_with_link from utils import replace_embedded_map_with_link
from utils import chatbot_nicknames from utils import chatbot_nicknames
from utils import replace_strings from utils import replace_strings
@ -901,7 +902,8 @@ def html_header_with_person_markup(css_filename: str, instance_title: str,
description = remove_html(actor_json['summary']) description = remove_html(actor_json['summary'])
name_str: str = remove_html(actor_json['name']) name_str: str = remove_html(actor_json['name'])
domain_full: str = actor_json['id'].split('://')[1].split('/')[0] domain_full: str = actor_json['id'].split('://')[1].split('/')[0]
handle: str = actor_json['preferredUsername'] + '@' + domain_full handle: str = \
get_preferred_username(actor_json, lang) + '@' + domain_full
url_str: str = get_url_from_post(actor_json['icon']['url']) url_str: str = get_url_from_post(actor_json['icon']['url'])
icon_url: str = remove_html(url_str) icon_url: str = remove_html(url_str)