__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 '
![' + \
                translate['Create a new DM'] + \
                ' | ' + translate['Create a new DM'] + \
                '](/' + \
                'icons/newpost.png) \n'
        else:
            new_post_button_str += \
                '' + \
                ''
    elif box_name in ('tlblogs', 'tlnews', 'tlfeatures'):
        if not icons_as_buttons:
            new_post_button_str += \
                '
\n'
        else:
            new_post_button_str += \
                '' + \
                ''
    elif box_name in ('tlblogs', 'tlnews', 'tlfeatures'):
        if not icons_as_buttons:
            new_post_button_str += \
                '![' + \
                translate['Create a new post'] + ' | ' + \
                translate['Create a new post'] + \
                '](/' + \
                'icons/newpost.png) \n'
        else:
            new_post_button_str += \
                '' + \
                ''
    elif box_name == 'tlshares':
        if not icons_as_buttons:
            new_post_button_str += \
                '' + \
                '
\n'
        else:
            new_post_button_str += \
                '' + \
                ''
    elif box_name == 'tlshares':
        if not icons_as_buttons:
            new_post_button_str += \
                '' + \
                '![' + \
                translate['Create a new shared item'] + ' | ' + \
                translate['Create a new shared item'] + \
                '](/' + \
                'icons/newpost.png) \n'
        else:
            new_post_button_str += \
                '' + \
                ''
    elif box_name == 'tlwanted':
        if not icons_as_buttons:
            new_post_button_str += \
                '' + \
                '
\n'
        else:
            new_post_button_str += \
                '' + \
                ''
    elif box_name == 'tlwanted':
        if not icons_as_buttons:
            new_post_button_str += \
                '' + \
                '![' + \
                translate['Create a new wanted item'] + ' | ' + \
                translate['Create a new wanted item'] + \
                '](/' + \
                'icons/newpost.png) \n'
        else:
            new_post_button_str += \
                '' + \
                ''
    else:
        if not manually_approve_followers:
            if not icons_as_buttons:
                new_post_button_str += \
                    '' + \
                    '
\n'
        else:
            new_post_button_str += \
                '' + \
                ''
    else:
        if not manually_approve_followers:
            if not icons_as_buttons:
                new_post_button_str += \
                    '' + \
                    '![' + \
                    translate['Create a new post'] + ' | ' + \
                    translate['Create a new post'] + \
                    '](/' + \
                    'icons/newpost.png) \n'
            else:
                new_post_button_str += \
                    '' + \
                    ''
        else:
            if not icons_as_buttons:
                new_post_button_str += \
                    '' + \
                    '
\n'
            else:
                new_post_button_str += \
                    '' + \
                    ''
        else:
            if not icons_as_buttons:
                new_post_button_str += \
                    '' + \
                    '![' + \
                    translate['Create a new post'] + \
                    ' | ' + translate['Create a new post'] + \
                    '](/' + \
                    'icons/newpost.png) \n'
            else:
                new_post_button_str += \
                    '' + \
                    ''
    return new_post_button_str
def _html_timeline_moderation_buttons(moderator: bool, box_name: str,
                                      nickname: str,
                                      moderation_action_str: str,
                                      translate: {}) -> str:
    """Returns html for the moderation screen buttons
    """
    tl_str = ''
    if moderator and box_name == 'moderation':
        tl_str += \
            '\n'
    return tl_str
def _html_timeline_keyboard(moderator: bool, text_mode_banner: str,
                            users_path: str,
                            nickname: str, new_calendar_event: bool,
                            new_dm: bool, new_reply: bool,
                            new_share: bool, new_wanted: bool,
                            follow_approvals: bool,
                            access_keys: {}, translate: {}) -> str:
    """Returns html for timeline keyboard navigation
    """
    calendar_str = translate['Calendar']
    if new_calendar_event:
        calendar_str = '' + calendar_str + ''
    dm_str = translate['DM']
    if new_dm:
        dm_str = '' + dm_str + ''
    replies_str = translate['Replies']
    if new_reply:
        replies_str = '' + replies_str + ''
    shares_str = translate['Shares']
    if new_share:
        shares_str = '' + shares_str + ''
    wanted_str = translate['Wanted']
    if new_wanted:
        wanted_str = '' + wanted_str + ''
    menu_profile = \
        html_hide_from_screen_reader('π€') + ' ' + \
        translate['Switch to profile view']
    menu_inbox = \
        html_hide_from_screen_reader('π₯') + ' ' + translate['Inbox']
    menu_outbox = \
        html_hide_from_screen_reader('π€') + ' ' + translate['Sent']
    menu_search = \
        html_hide_from_screen_reader('π') + ' ' + \
        translate['Search and follow']
    menu_calendar = \
        html_hide_from_screen_reader('π
') + ' ' + calendar_str
    menu_dm = \
        html_hide_from_screen_reader('π©') + ' ' + dm_str
    menu_replies = \
        html_hide_from_screen_reader('π¨') + ' ' + replies_str
    menu_bookmarks = \
        html_hide_from_screen_reader('π') + ' ' + translate['Bookmarks']
    menu_shares = \
        html_hide_from_screen_reader('π€') + ' ' + shares_str
    menu_wanted = \
        html_hide_from_screen_reader('β±') + ' ' + wanted_str
    menu_blogs = \
        html_hide_from_screen_reader('π') + ' ' + translate['Blogs']
    menu_newswire = \
        html_hide_from_screen_reader('π°') + ' ' + translate['Newswire']
    menu_links = \
        html_hide_from_screen_reader('π') + ' ' + translate['Links']
    menu_new_post = \
        html_hide_from_screen_reader('β') + ' ' + \
        translate['Create a new post']
    menu_moderation = \
        html_hide_from_screen_reader('β‘οΈ') + ' ' + translate['Mod']
    nav_links = {
        menu_profile: '/users/' + nickname,
        menu_inbox: users_path + '/inbox#timelineposts',
        menu_search: users_path + '/search',
        menu_new_post: users_path + '/newpost',
        menu_calendar: users_path + '/calendar',
        menu_dm: users_path + '/dm#timelineposts',
        menu_replies: users_path + '/tlreplies#timelineposts',
        menu_outbox: users_path + '/outbox#timelineposts',
        menu_bookmarks: users_path + '/tlbookmarks#timelineposts',
        menu_shares: users_path + '/tlshares#timelineposts',
        menu_wanted: users_path + '/tlwanted#timelineposts',
        menu_blogs: users_path + '/tlblogs#timelineposts',
        menu_newswire: users_path + '/newswiremobile',
        menu_links: users_path + '/linksmobile'
    }
    nav_access_keys = {}
    for variable_name, key in access_keys.items():
        if not locals().get(variable_name):
            continue
        nav_access_keys[locals()[variable_name]] = key
    if moderator:
        nav_links[menu_moderation] = users_path + '/moderation#modtimeline'
    return html_keyboard_navigation(text_mode_banner, nav_links,
                                    nav_access_keys,
                                    None, users_path, translate,
                                    follow_approvals)
def _html_timeline_end(base_dir: str, nickname: str, domain_full: str,
                       http_prefix: str, translate: {},
                       moderator: bool, editor: bool,
                       newswire: {}, positive_voting: bool,
                       show_publish_as_icon: bool,
                       rss_icon_at_top: bool, publish_button_at_top: bool,
                       authorized: bool, theme: str,
                       default_timeline: str, access_keys: {},
                       box_name: str,
                       enable_timing_log: bool, timeline_start_time) -> str:
    """Ending of the timeline, containing the right column
    """
    # end of timeline-posts
    tl_str = '  \n'
    # end of column-center
    tl_str += '  \n'
    # right column
    right_column_str = \
        get_right_column_content(base_dir, nickname, domain_full,
                                 http_prefix, translate,
                                 moderator, editor,
                                 newswire, positive_voting,
                                 False, None, True,
                                 show_publish_as_icon,
                                 rss_icon_at_top,
                                 publish_button_at_top,
                                 authorized, True, theme,
                                 default_timeline, access_keys)
    tl_str += '
\n'
            else:
                new_post_button_str += \
                    '' + \
                    ''
    return new_post_button_str
def _html_timeline_moderation_buttons(moderator: bool, box_name: str,
                                      nickname: str,
                                      moderation_action_str: str,
                                      translate: {}) -> str:
    """Returns html for the moderation screen buttons
    """
    tl_str = ''
    if moderator and box_name == 'moderation':
        tl_str += \
            '\n'
    return tl_str
def _html_timeline_keyboard(moderator: bool, text_mode_banner: str,
                            users_path: str,
                            nickname: str, new_calendar_event: bool,
                            new_dm: bool, new_reply: bool,
                            new_share: bool, new_wanted: bool,
                            follow_approvals: bool,
                            access_keys: {}, translate: {}) -> str:
    """Returns html for timeline keyboard navigation
    """
    calendar_str = translate['Calendar']
    if new_calendar_event:
        calendar_str = '' + calendar_str + ''
    dm_str = translate['DM']
    if new_dm:
        dm_str = '' + dm_str + ''
    replies_str = translate['Replies']
    if new_reply:
        replies_str = '' + replies_str + ''
    shares_str = translate['Shares']
    if new_share:
        shares_str = '' + shares_str + ''
    wanted_str = translate['Wanted']
    if new_wanted:
        wanted_str = '' + wanted_str + ''
    menu_profile = \
        html_hide_from_screen_reader('π€') + ' ' + \
        translate['Switch to profile view']
    menu_inbox = \
        html_hide_from_screen_reader('π₯') + ' ' + translate['Inbox']
    menu_outbox = \
        html_hide_from_screen_reader('π€') + ' ' + translate['Sent']
    menu_search = \
        html_hide_from_screen_reader('π') + ' ' + \
        translate['Search and follow']
    menu_calendar = \
        html_hide_from_screen_reader('π
') + ' ' + calendar_str
    menu_dm = \
        html_hide_from_screen_reader('π©') + ' ' + dm_str
    menu_replies = \
        html_hide_from_screen_reader('π¨') + ' ' + replies_str
    menu_bookmarks = \
        html_hide_from_screen_reader('π') + ' ' + translate['Bookmarks']
    menu_shares = \
        html_hide_from_screen_reader('π€') + ' ' + shares_str
    menu_wanted = \
        html_hide_from_screen_reader('β±') + ' ' + wanted_str
    menu_blogs = \
        html_hide_from_screen_reader('π') + ' ' + translate['Blogs']
    menu_newswire = \
        html_hide_from_screen_reader('π°') + ' ' + translate['Newswire']
    menu_links = \
        html_hide_from_screen_reader('π') + ' ' + translate['Links']
    menu_new_post = \
        html_hide_from_screen_reader('β') + ' ' + \
        translate['Create a new post']
    menu_moderation = \
        html_hide_from_screen_reader('β‘οΈ') + ' ' + translate['Mod']
    nav_links = {
        menu_profile: '/users/' + nickname,
        menu_inbox: users_path + '/inbox#timelineposts',
        menu_search: users_path + '/search',
        menu_new_post: users_path + '/newpost',
        menu_calendar: users_path + '/calendar',
        menu_dm: users_path + '/dm#timelineposts',
        menu_replies: users_path + '/tlreplies#timelineposts',
        menu_outbox: users_path + '/outbox#timelineposts',
        menu_bookmarks: users_path + '/tlbookmarks#timelineposts',
        menu_shares: users_path + '/tlshares#timelineposts',
        menu_wanted: users_path + '/tlwanted#timelineposts',
        menu_blogs: users_path + '/tlblogs#timelineposts',
        menu_newswire: users_path + '/newswiremobile',
        menu_links: users_path + '/linksmobile'
    }
    nav_access_keys = {}
    for variable_name, key in access_keys.items():
        if not locals().get(variable_name):
            continue
        nav_access_keys[locals()[variable_name]] = key
    if moderator:
        nav_links[menu_moderation] = users_path + '/moderation#modtimeline'
    return html_keyboard_navigation(text_mode_banner, nav_links,
                                    nav_access_keys,
                                    None, users_path, translate,
                                    follow_approvals)
def _html_timeline_end(base_dir: str, nickname: str, domain_full: str,
                       http_prefix: str, translate: {},
                       moderator: bool, editor: bool,
                       newswire: {}, positive_voting: bool,
                       show_publish_as_icon: bool,
                       rss_icon_at_top: bool, publish_button_at_top: bool,
                       authorized: bool, theme: str,
                       default_timeline: str, access_keys: {},
                       box_name: str,
                       enable_timing_log: bool, timeline_start_time) -> str:
    """Ending of the timeline, containing the right column
    """
    # end of timeline-posts
    tl_str = '  \n'
    # end of column-center
    tl_str += '  \n'
    # right column
    right_column_str = \
        get_right_column_content(base_dir, nickname, domain_full,
                                 http_prefix, translate,
                                 moderator, editor,
                                 newswire, positive_voting,
                                 False, None, True,
                                 show_publish_as_icon,
                                 rss_icon_at_top,
                                 publish_button_at_top,
                                 authorized, True, theme,
                                 default_timeline, access_keys)
    tl_str += '  ![' + translate['Approve follow requests'] + \
                        ' ' + \
                        translate['Approve follow requests'] + \
                        '](/icons/person.png) \n'
                    break
    _log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '3')
    # moderation / reports button
    moderation_button_str = ''
    if moderator and not minimal:
        moderation_button_str = \
            ''
    # shares, bookmarks and events buttons
    shares_button_str = ''
    wanted_button_str = ''
    bookmarks_button_str = ''
    events_button_str = ''
    if not minimal:
        shares_button_str = \
            ''
        wanted_button_str = \
            ''
        bookmarks_button_str = \
            ''
    instance_title = \
        get_config_param(base_dir, 'instanceTitle')
    tl_str = \
        html_header_with_external_style(css_filename, instance_title, None)
    _log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '4')
    # if this is a news instance and we are viewing the news timeline
    news_header = False
    if default_timeline == 'tlfeatures' and box_name == 'tlfeatures':
        news_header = True
    new_post_button_str = ''
    # start of headericons div
    if not news_header:
        if not icons_as_buttons:
            new_post_button_str += '
\n'
                    break
    _log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '3')
    # moderation / reports button
    moderation_button_str = ''
    if moderator and not minimal:
        moderation_button_str = \
            ''
    # shares, bookmarks and events buttons
    shares_button_str = ''
    wanted_button_str = ''
    bookmarks_button_str = ''
    events_button_str = ''
    if not minimal:
        shares_button_str = \
            ''
        wanted_button_str = \
            ''
        bookmarks_button_str = \
            ''
    instance_title = \
        get_config_param(base_dir, 'instanceTitle')
    tl_str = \
        html_header_with_external_style(css_filename, instance_title, None)
    _log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '4')
    # if this is a news instance and we are viewing the news timeline
    news_header = False
    if default_timeline == 'tlfeatures' and box_name == 'tlfeatures':
        news_header = True
    new_post_button_str = ''
    # start of headericons div
    if not news_header:
        if not icons_as_buttons:
            new_post_button_str += '| ' + \ 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 += \ ' ![' + \
            translate['Page up'] + ' ' + \
            translate['Page up'] + '](/' + \
            'icons/pageup.png) \n' + \
            ' ' 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' + \ ' ![' + \
            translate['Page down'] + ' ' + \
            translate['Page down'] + '](/' + \
            'icons/pagedown.png) \n' + \
            ' \n'
    profile_str += \
        '\n'
    if shared_item.get('imageUrl'):
        profile_str += '\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 += ' ' + 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 += \ ' ![' + translate['Page up'] + \
            ' ' + translate['Page up'] + '](/' + \
            'icons/pageup.png) \n' + \
            ' ![' + translate['Page down'] + \
            ' ' + translate['Page down'] + '](/' + \
            'icons/pagedown.png) \n' + \
            ' |