__filename__ = "webapp_timeline.py" __author__ = "Bob Mottram" __license__ = "AGPL3+" __version__ = "1.3.0" __maintainer__ = "Bob Mottram" __email__ = "bob@libreserver.org" __status__ = "Production" __module_group__ = "Timeline" import os import time from shutil import copyfile from utils import is_artist from utils import dangerous_markup from utils import get_config_param from utils import get_full_domain from utils import is_editor from utils import remove_id_ending from utils import acct_dir from utils import is_float from utils import local_actor_url from follow import follower_approval_active from person import is_person_snoozed from markdown import markdown_to_html from webapp_utils import html_keyboard_navigation from webapp_utils import html_hide_from_screen_reader from webapp_utils import html_post_separator from webapp_utils import get_banner_file from webapp_utils import html_header_with_external_style from webapp_utils import html_footer from webapp_utils import shares_timeline_json from webapp_utils import html_highlight_label from webapp_post import prepare_post_from_html_cache from webapp_post import individual_post_as_html from webapp_column_left import get_left_column_content from webapp_column_right import get_right_column_content from webapp_headerbuttons import header_buttons_timeline from posts import is_moderator from announce import is_self_announce def _log_timeline_timing(enable_timing_log: bool, timeline_start_time, box_name: str, debug_id: str) -> None: """Create a log of timings for performance tuning """ if not enable_timing_log: return time_diff = int((time.time() - timeline_start_time) * 1000) if time_diff > 100: print('TIMELINE TIMING ' + box_name + ' ' + debug_id + ' = ' + str(time_diff)) def _get_help_for_timeline(base_dir: str, box_name: str) -> str: """Shows help text for the given timeline """ # get the filename for help for this timeline help_filename = base_dir + '/accounts/help_' + box_name + '.md' if not os.path.isfile(help_filename): language = \ get_config_param(base_dir, 'language') if not language: language = 'en' theme_name = \ get_config_param(base_dir, 'theme') default_filename = None if theme_name: default_filename = \ base_dir + '/theme/' + theme_name + '/welcome/' + \ 'help_' + box_name + '_' + language + '.md' if not os.path.isfile(default_filename): default_filename = None if not default_filename: default_filename = \ base_dir + '/defaultwelcome/' + \ 'help_' + box_name + '_' + language + '.md' if not os.path.isfile(default_filename): default_filename = \ base_dir + '/defaultwelcome/help_' + box_name + '_en.md' if os.path.isfile(default_filename): copyfile(default_filename, help_filename) # show help text if os.path.isfile(help_filename): instance_title = \ get_config_param(base_dir, 'instanceTitle') if not instance_title: instance_title = 'Epicyon' with open(help_filename, 'r') as help_file: help_text = help_file.read() if dangerous_markup(help_text, False): return '' help_text = help_text.replace('INSTANCE', instance_title) return '
' + \ left_column_str + ' | \n' # center column containing posts tl_str += '\n'
if not full_width_tl_button_header:
tl_str += \
header_buttons_timeline(default_timeline, box_name, page_number,
translate, users_path, media_button,
blogs_button, features_button,
news_button, inbox_button,
dm_button, new_dm, replies_button,
new_reply, minimal, sent_button,
shares_button_str, wanted_button_str,
bookmarks_button_str,
events_button_str, moderation_button_str,
new_post_button_str, base_dir, nickname,
domain, timeline_start_time,
new_calendar_event, calendar_path,
calendar_image, follow_approvals,
icons_as_buttons, access_keys)
tl_str += ' \n'
# second row of buttons for moderator actions
tl_str += \
_html_timeline_moderation_buttons(moderator, box_name, nickname,
moderation_action_str, translate)
_log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '6')
if box_name == 'tlshares':
max_shares_per_account = items_per_page
return (tl_str +
_html_shares_timeline(translate, page_number, items_per_page,
base_dir, actor, nickname, domain, port,
max_shares_per_account, http_prefix,
shared_items_federated_domains,
'shares') +
_html_timeline_end(base_dir, nickname, domain_full,
http_prefix, translate,
moderator, editor,
newswire, positive_voting,
show_publish_as_icon,
rss_icon_at_top, publish_button_at_top,
authorized, theme,
default_timeline, access_keys,
box_name,
enable_timing_log, timeline_start_time) +
html_footer())
elif box_name == 'tlwanted':
max_shares_per_account = items_per_page
return (tl_str +
_html_shares_timeline(translate, page_number, items_per_page,
base_dir, actor, nickname, domain, port,
max_shares_per_account, http_prefix,
shared_items_federated_domains,
'wanted') +
_html_timeline_end(base_dir, nickname, domain_full,
http_prefix, translate,
moderator, editor,
newswire, positive_voting,
show_publish_as_icon,
rss_icon_at_top, publish_button_at_top,
authorized, theme,
default_timeline, access_keys,
box_name,
enable_timing_log, timeline_start_time) +
html_footer())
_log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '7')
# separator between posts which only appears in shell browsers
# such as Lynx and is not read by screen readers
if box_name != 'tlmedia':
text_mode_separator = \
' ' + \ _page_number_buttons(users_path, box_name, page_number) tl_str += \ ' ' tl_str += ' \n'
# show each post in the timeline
for item in timeline_json['orderedItems']:
if item['type'] == 'Create' or \
item['type'] == 'Announce':
# is the actor who sent this post snoozed?
if is_person_snoozed(base_dir, nickname, domain,
item['actor']):
continue
if is_self_announce(item):
continue
# is the post in the memory cache of recent ones?
curr_tl_str = None
if box_name != 'tlmedia' and recent_posts_cache.get('html'):
post_id = remove_id_ending(item['id']).replace('/', '#')
if recent_posts_cache['html'].get(post_id):
curr_tl_str = recent_posts_cache['html'][post_id]
curr_tl_str = \
prepare_post_from_html_cache(nickname,
curr_tl_str,
box_name,
page_number)
_log_timeline_timing(enable_timing_log,
timeline_start_time,
box_name, '10')
if not curr_tl_str:
_log_timeline_timing(enable_timing_log,
timeline_start_time,
box_name, '11')
mitm = False
if item.get('mitm'):
mitm = True
# read the post from disk
curr_tl_str = \
individual_post_as_html(signing_priv_key_pem,
False, recent_posts_cache,
max_recent_posts,
translate, page_number,
base_dir, session,
cached_webfingers,
person_cache,
nickname, domain, port,
item, None, True,
allow_deletion,
http_prefix, project_version,
box_name,
yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
peertube_instances,
allow_local_network_access,
theme, system_language,
max_like_count,
box_name != 'dm',
show_individual_post_icons,
manually_approve_followers,
False, True, use_cache_only,
cw_lists, lists_enabled,
timezone, mitm,
bold_reading)
_log_timeline_timing(enable_timing_log,
timeline_start_time, box_name, '12')
if curr_tl_str:
if curr_tl_str not in tl_str:
item_ctr += 1
tl_str += text_mode_separator + curr_tl_str
if separator_str:
tl_str += separator_str
if box_name == 'tlmedia':
tl_str += ' \n'
if item_ctr < 3:
print('Items added to html timeline ' + box_name + ': ' +
str(item_ctr) + ' ' + str(timeline_json['orderedItems']))
# page down arrow
if item_ctr > 0:
tl_str += text_mode_separator
tl_str += \
' \n' + \ ' \n'
profile_str += \
'\n'
profile_str += \
'\n\n'
profile_str += ' \n'
return profile_str
def _html_shares_timeline(translate: {}, page_number: int, items_per_page: int,
base_dir: str, actor: str,
nickname: str, domain: str, port: int,
max_shares_per_account: int, http_prefix: str,
shared_items_federated_domains: [],
sharesFileType: str) -> str:
"""Show shared items timeline as html
"""
shares_json, lastPage = \
shares_timeline_json(actor, page_number, items_per_page,
base_dir, domain, nickname,
max_shares_per_account,
shared_items_federated_domains, sharesFileType)
domain_full = get_full_domain(domain, port)
actor = local_actor_url(http_prefix, nickname, domain_full)
admin_nickname = get_config_param(base_dir, 'admin')
admin_actor = ''
if admin_nickname:
admin_actor = \
local_actor_url(http_prefix, admin_nickname, domain_full)
timeline_str = ''
if page_number > 1:
timeline_str += ' \n'
if shared_item.get('imageUrl'):
profile_str += '' + shared_item['summary'] + ' \n'
if shared_item.get('itemQty'):
if shared_item['itemQty'] > 1:
profile_str += \
'' + translate['Quantity'] + ': ' + \
str(shared_item['itemQty']) + ' ' + \ '' + \ '\n' profile_str += \ '\n' if remove_button and domain in share_id: if sharesFileType == 'shares': profile_str += \ ' \n' else: profile_str += \ ' \n' profile_str += ' ' + \ _page_number_buttons(actor, 'tl' + sharesFileType, page_number) timeline_str += \ ' |