__filename__ = "webapp_timeline.py" __author__ = "Bob Mottram" __license__ = "AGPL3+" __version__ = "1.2.0" __maintainer__ = "Bob Mottram" __email__ = "bob@freedombone.net" __status__ = "Production" import os import time from shutil import copyfile from utils import dangerousMarkup from utils import getConfigParam from utils import getFullDomain from utils import isEditor from utils import removeIdEnding from follow import followerApprovalActive from person import isPersonSnoozed from webapp_utils import markdownToHtml from webapp_utils import htmlKeyboardNavigation from webapp_utils import htmlHideFromScreenReader from webapp_utils import htmlPostSeparator from webapp_utils import getBannerFile from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlFooter from webapp_utils import sharesTimelineJson from webapp_utils import htmlHighlightLabel from webapp_post import preparePostFromHtmlCache from webapp_post import individualPostAsHtml from webapp_column_left import getLeftColumnContent from webapp_column_right import getRightColumnContent from webapp_headerbuttons import headerButtonsTimeline from posts import isModerator def _logTimelineTiming(enableTimingLog: bool, timelineStartTime, boxName: str, debugId: str) -> None: """Create a log of timings for performance tuning """ if not enableTimingLog: return timeDiff = int((time.time() - timelineStartTime) * 1000) if timeDiff > 100: print('TIMELINE TIMING ' + boxName + ' ' + debugId + ' = ' + str(timeDiff)) def _getHelpForTimeline(baseDir: str, boxName: str) -> str: """Shows help text for the given timeline """ # get the filename for help for this timeline helpFilename = baseDir + '/accounts/help_' + boxName + '.md' if not os.path.isfile(helpFilename): language = \ getConfigParam(baseDir, 'language') if not language: language = 'en' themeName = \ getConfigParam(baseDir, 'theme') defaultFilename = None if themeName: defaultFilename = \ baseDir + '/theme/' + themeName + '/welcome/' + \ 'help_' + boxName + '_' + language + '.md' if not os.path.isfile(defaultFilename): defaultFilename = None if not defaultFilename: defaultFilename = \ baseDir + '/defaultwelcome/' + \ 'help_' + boxName + '_' + language + '.md' if not os.path.isfile(defaultFilename): defaultFilename = \ baseDir + '/defaultwelcome/help_' + boxName + '_en.md' if os.path.isfile(defaultFilename): copyfile(defaultFilename, helpFilename) # show help text if os.path.isfile(helpFilename): instanceTitle = \ getConfigParam(baseDir, 'instanceTitle') if not instanceTitle: instanceTitle = 'Epicyon' with open(helpFilename, 'r') as helpFile: helpText = helpFile.read() if dangerousMarkup(helpText, False): return '' helpText = helpText.replace('INSTANCE', instanceTitle) return '
\n' + \ markdownToHtml(helpText) + '\n' + \ '
\n' return '' def htmlTimeline(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, timelineJson: {}, boxName: str, allowDeletion: bool, httpPrefix: str, projectVersion: str, manuallyApproveFollowers: bool, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, moderator: bool, editor: bool, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, moderationActionStr: str, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the timeline as html """ enableTimingLog = False timelineStartTime = time.time() accountDir = baseDir + '/accounts/' + nickname + '@' + domain # should the calendar icon be highlighted? newCalendarEvent = False calendarImage = 'calendar.png' calendarPath = '/calendar' calendarFile = accountDir + '/.newCalendar' if os.path.isfile(calendarFile): newCalendarEvent = True calendarImage = 'calendar_notify.png' with open(calendarFile, 'r') as calfile: calendarPath = calfile.read().replace('##sent##', '') calendarPath = calendarPath.replace('\n', '').replace('\r', '') # should the DM button be highlighted? newDM = False dmFile = accountDir + '/.newDM' if os.path.isfile(dmFile): newDM = True if boxName == 'dm': os.remove(dmFile) # should the Replies button be highlighted? newReply = False replyFile = accountDir + '/.newReply' if os.path.isfile(replyFile): newReply = True if boxName == 'tlreplies': os.remove(replyFile) # should the Shares button be highlighted? newShare = False newShareFile = accountDir + '/.newShare' if os.path.isfile(newShareFile): newShare = True if boxName == 'tlshares': os.remove(newShareFile) # should the Moderation/reports button be highlighted? newReport = False newReportFile = accountDir + '/.newReport' if os.path.isfile(newReportFile): newReport = True if boxName == 'moderation': os.remove(newReportFile) separatorStr = '' if boxName != 'tlmedia': separatorStr = htmlPostSeparator(baseDir, None) # the css filename cssFilename = baseDir + '/epicyon-profile.css' if os.path.isfile(baseDir + '/epicyon.css'): cssFilename = baseDir + '/epicyon.css' # filename of the banner shown at the top bannerFile, bannerFilename = \ getBannerFile(baseDir, nickname, domain, theme) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '1') # is the user a moderator? if not moderator: moderator = isModerator(baseDir, nickname) # is the user a site editor? if not editor: editor = isEditor(baseDir, nickname) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '2') # the appearance of buttons - highlighted or not inboxButton = 'button' blogsButton = 'button' featuresButton = 'button' newsButton = 'button' dmButton = 'button' if newDM: dmButton = 'buttonhighlighted' repliesButton = 'button' if newReply: repliesButton = 'buttonhighlighted' mediaButton = 'button' bookmarksButton = 'button' # eventsButton = 'button' sentButton = 'button' sharesButton = 'button' if newShare: sharesButton = 'buttonhighlighted' moderationButton = 'button' if newReport: moderationButton = 'buttonhighlighted' if boxName == 'inbox': inboxButton = 'buttonselected' elif boxName == 'tlblogs': blogsButton = 'buttonselected' elif boxName == 'tlfeatures': featuresButton = 'buttonselected' elif boxName == 'tlnews': newsButton = 'buttonselected' elif boxName == 'dm': dmButton = 'buttonselected' if newDM: dmButton = 'buttonselectedhighlighted' elif boxName == 'tlreplies': repliesButton = 'buttonselected' if newReply: repliesButton = 'buttonselectedhighlighted' elif boxName == 'tlmedia': mediaButton = 'buttonselected' elif boxName == 'outbox': sentButton = 'buttonselected' elif boxName == 'moderation': moderationButton = 'buttonselected' if newReport: moderationButton = 'buttonselectedhighlighted' elif boxName == 'tlshares': sharesButton = 'buttonselected' if newShare: sharesButton = 'buttonselectedhighlighted' elif boxName == 'tlbookmarks' or boxName == 'bookmarks': bookmarksButton = 'buttonselected' # elif boxName == 'tlevents': # eventsButton = 'buttonselected' # get the full domain, including any port number fullDomain = getFullDomain(domain, port) usersPath = '/users/' + nickname actor = httpPrefix + '://' + fullDomain + usersPath showIndividualPostIcons = True # show an icon for new follow approvals followApprovals = '' followRequestsFilename = \ baseDir + '/accounts/' + \ nickname + '@' + domain + '/followrequests.txt' if os.path.isfile(followRequestsFilename): with open(followRequestsFilename, 'r') as f: for line in f: if len(line) > 0: # show follow approvals icon followApprovals = \ '' + \ '' + \
                        translate['Approve follow requests'] + \
                        '\n' break _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '3') # moderation / reports button moderationButtonStr = '' if moderator and not minimal: moderationButtonStr = \ '' # shares, bookmarks and events buttons sharesButtonStr = '' bookmarksButtonStr = '' eventsButtonStr = '' if not minimal: sharesButtonStr = \ '' bookmarksButtonStr = \ '' # # eventsButtonStr = \ # '' instanceTitle = \ getConfigParam(baseDir, 'instanceTitle') tlStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '4') # if this is a news instance and we are viewing the news timeline newsHeader = False if defaultTimeline == 'tlfeatures' and boxName == 'tlfeatures': newsHeader = True newPostButtonStr = '' # start of headericons div if not newsHeader: if not iconsAsButtons: newPostButtonStr += '
' # what screen to go to when a new post is created if boxName == 'dm': if not iconsAsButtons: newPostButtonStr += \ '| ' + translate['Create a new DM'] + \
                '\n' else: newPostButtonStr += \ '' + \ '' elif (boxName == 'tlblogs' or boxName == 'tlnews' or boxName == 'tlfeatures'): if not iconsAsButtons: newPostButtonStr += \ '| ' + \
                translate['Create a new post'] + \
                '\n' else: newPostButtonStr += \ '' + \ '' elif boxName == 'tlevents': if not iconsAsButtons: newPostButtonStr += \ '| ' + \
                translate['Create a new event'] + \
                '\n' else: newPostButtonStr += \ '' + \ '' elif boxName == 'tlshares': if not iconsAsButtons: newPostButtonStr += \ '| ' + \
                translate['Create a new shared item'] + \
                '\n' else: newPostButtonStr += \ '' + \ '' else: if not manuallyApproveFollowers: if not iconsAsButtons: newPostButtonStr += \ '| ' + \
                    translate['Create a new post'] + \
                    '\n' else: newPostButtonStr += \ '' + \ '' else: if not iconsAsButtons: newPostButtonStr += \ '| ' + translate['Create a new post'] + \
                    '\n' else: newPostButtonStr += \ '' + \ '' # keyboard navigation calendarStr = translate['Calendar'] if newCalendarEvent: calendarStr = '' + calendarStr + '' dmStr = translate['DM'] if newDM: dmStr = '' + dmStr + '' repliesStr = translate['Replies'] if newReply: repliesStr = '' + repliesStr + '' sharesStr = translate['Shares'] if newShare: sharesStr = '' + sharesStr + '' menuProfile = \ htmlHideFromScreenReader('👤') + ' ' + \ translate['Switch to profile view'] menuInbox = \ htmlHideFromScreenReader('📥') + ' ' + translate['Inbox'] menuOutbox = \ htmlHideFromScreenReader('📤') + ' ' + translate['Sent'] menuSearch = \ htmlHideFromScreenReader('🔍') + ' ' + \ translate['Search and follow'] menuCalendar = \ htmlHideFromScreenReader('📅') + ' ' + calendarStr menuDM = \ htmlHideFromScreenReader('📩') + ' ' + dmStr menuReplies = \ htmlHideFromScreenReader('📨') + ' ' + repliesStr menuBookmarks = \ htmlHideFromScreenReader('🔖') + ' ' + \ translate['Bookmarks'] menuShares = \ htmlHideFromScreenReader('🤝') + ' ' + sharesStr # menuEvents = \ # htmlHideFromScreenReader('🎫') + ' ' + translate['Events'] menuBlogs = \ htmlHideFromScreenReader('📝') + ' ' + translate['Blogs'] menuNewswire = \ htmlHideFromScreenReader('📰') + ' ' + translate['Newswire'] menuLinks = \ htmlHideFromScreenReader('🔗') + ' ' + translate['Links'] menuNewPost = \ htmlHideFromScreenReader('➕') + ' ' + \ translate['Create a new post'] menuModeration = \ htmlHideFromScreenReader('⚡️') + ' ' + \ translate['Mod'] navLinks = { menuProfile: '/users/' + nickname, menuInbox: usersPath + '/inbox#timelineposts', menuSearch: usersPath + '/search', menuNewPost: usersPath + '/newpost', menuCalendar: usersPath + '/calendar', menuDM: usersPath + '/dm#timelineposts', menuReplies: usersPath + '/tlreplies#timelineposts', menuOutbox: usersPath + '/inbox#timelineposts', menuBookmarks: usersPath + '/tlbookmarks#timelineposts', menuShares: usersPath + '/tlshares#timelineposts', menuBlogs: usersPath + '/tlblogs#timelineposts', # menuEvents: usersPath + '/tlevents#timelineposts', menuNewswire: usersPath + '/newswiremobile', menuLinks: usersPath + '/linksmobile' } if moderator: navLinks[menuModeration] = usersPath + '/moderation#modtimeline' tlStr += htmlKeyboardNavigation(textModeBanner, navLinks, None, usersPath, translate, followApprovals) # banner and row of buttons tlStr += \ '
\n' + \ '\n' tlStr += '\n' + \ '
\n' if fullWidthTimelineButtonHeader: tlStr += \ headerButtonsTimeline(defaultTimeline, boxName, pageNumber, translate, usersPath, mediaButton, blogsButton, featuresButton, newsButton, inboxButton, dmButton, newDM, repliesButton, newReply, minimal, sentButton, sharesButtonStr, bookmarksButtonStr, eventsButtonStr, moderationButtonStr, newPostButtonStr, baseDir, nickname, domain, timelineStartTime, newCalendarEvent, calendarPath, calendarImage, followApprovals, iconsAsButtons) # start the timeline tlStr += '\n' tlStr += ' \n' tlStr += ' \n' tlStr += ' \n' tlStr += ' \n' tlStr += ' \n' tlStr += ' \n' tlStr += ' \n' domainFull = getFullDomain(domain, port) # left column leftColumnStr = \ getLeftColumnContent(baseDir, nickname, domainFull, httpPrefix, translate, editor, False, None, rssIconAtTop, True, False, theme) tlStr += ' \n' # center column containing posts tlStr += ' \n' # right column rightColumnStr = getRightColumnContent(baseDir, nickname, domainFull, httpPrefix, translate, moderator, editor, newswire, positiveVoting, False, None, True, showPublishAsIcon, rssIconAtTop, publishButtonAtTop, authorized, True, theme, defaultTimeline) tlStr += ' \n' tlStr += ' \n' _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '9') tlStr += ' \n' tlStr += '
' + \ leftColumnStr + ' \n' if not fullWidthTimelineButtonHeader: tlStr += \ headerButtonsTimeline(defaultTimeline, boxName, pageNumber, translate, usersPath, mediaButton, blogsButton, featuresButton, newsButton, inboxButton, dmButton, newDM, repliesButton, newReply, minimal, sentButton, sharesButtonStr, bookmarksButtonStr, eventsButtonStr, moderationButtonStr, newPostButtonStr, baseDir, nickname, domain, timelineStartTime, newCalendarEvent, calendarPath, calendarImage, followApprovals, iconsAsButtons) tlStr += '
\n' # second row of buttons for moderator actions if moderator and boxName == 'moderation': tlStr += \ '
' tlStr += '
\n' idx = 'Nickname or URL. Block using *@domain or nickname@domain' tlStr += \ ' ' + translate[idx] + '
\n' if moderationActionStr: tlStr += '
\n' else: tlStr += '
\n' tlStr += \ ' \n' tlStr += \ ' \n' tlStr += \ ' \n' tlStr += \ ' \n' tlStr += \ ' \n' tlStr += \ ' \n' tlStr += \ ' \n' tlStr += \ ' \n' tlStr += '
\n
\n' _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '6') if boxName == 'tlshares': maxSharesPerAccount = itemsPerPage return (tlStr + _htmlSharesTimeline(translate, pageNumber, itemsPerPage, baseDir, actor, nickname, domain, port, maxSharesPerAccount, httpPrefix) + htmlFooter()) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '7') # separator between posts which only appears in shell browsers # such as Lynx and is not read by screen readers if boxName != 'tlmedia': textModeSeparator = \ '

' else: textModeSeparator = '' # page up arrow if pageNumber > 1: tlStr += textModeSeparator tlStr += \ '
\n' + \ ' ' + \
            translate['Page up'] + '\n' + \ '
\n' # show the posts itemCtr = 0 if timelineJson: # if this is the media timeline then add an extra gallery container if boxName == 'tlmedia': if pageNumber > 1: tlStr += '
' tlStr += '
\n' # show each post in the timeline for item in timelineJson['orderedItems']: if item['type'] == 'Create' or \ item['type'] == 'Announce': # is the actor who sent this post snoozed? if isPersonSnoozed(baseDir, nickname, domain, item['actor']): continue # is the post in the memory cache of recent ones? currTlStr = None if boxName != 'tlmedia' and \ recentPostsCache.get('index'): postId = \ removeIdEnding(item['id']).replace('/', '#') if postId in recentPostsCache['index']: if not item.get('muted'): if recentPostsCache['html'].get(postId): currTlStr = recentPostsCache['html'][postId] currTlStr = \ preparePostFromHtmlCache(nickname, currTlStr, boxName, pageNumber) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '10') else: print('Muted post in timeline ' + boxName) if not currTlStr: _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '11') # read the post from disk currTlStr = \ individualPostAsHtml(False, recentPostsCache, maxRecentPosts, translate, pageNumber, baseDir, session, cachedWebfingers, personCache, nickname, domain, port, item, None, True, allowDeletion, httpPrefix, projectVersion, boxName, YTReplacementDomain, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, boxName != 'dm', showIndividualPostIcons, manuallyApproveFollowers, theme, False, True) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '12') if currTlStr: itemCtr += 1 tlStr += textModeSeparator + currTlStr if separatorStr: tlStr += separatorStr if boxName == 'tlmedia': tlStr += '
\n' if itemCtr < 3: print('Items added to html timeline ' + boxName + ': ' + str(itemCtr) + ' ' + str(timelineJson['orderedItems'])) # page down arrow if itemCtr > 2: tlStr += textModeSeparator tlStr += \ '
\n' + \ ' ' + \
            translate['Page down'] + '\n' + \ '
\n' tlStr += textModeSeparator elif itemCtr == 0: tlStr += _getHelpForTimeline(baseDir, boxName) # end of timeline-posts tlStr += '
\n' # end of column-center tlStr += '
' + \ rightColumnStr + '
\n' tlStr += htmlFooter() return tlStr def htmlIndividualShare(actor: str, item: {}, translate: {}, showContact: bool, removeButton: bool) -> str: """Returns an individual shared item as html """ profileStr = '
\n' profileStr += '\n' if item.get('imageUrl'): profileStr += '\n' profileStr += \ '' + translate['Item image'] + '\n\n' profileStr += '

' + item['summary'] + '

\n' profileStr += \ '

' + translate['Type'] + ': ' + item['itemType'] + ' ' profileStr += \ '' + translate['Category'] + ': ' + item['category'] + ' ' profileStr += \ '' + translate['Location'] + ': ' + item['location'] + '

\n' sharedesc = item['displayName'] if '<' not in sharedesc and '?' not in sharedesc: if showContact: contactActor = item['actor'] profileStr += \ '

\n' if removeButton: profileStr += \ ' \n' profileStr += '

\n' return profileStr def _htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int, baseDir: str, actor: str, nickname: str, domain: str, port: int, maxSharesPerAccount: int, httpPrefix: str) -> str: """Show shared items timeline as html """ sharesJson, lastPage = \ sharesTimelineJson(actor, pageNumber, itemsPerPage, baseDir, maxSharesPerAccount) domainFull = getFullDomain(domain, port) actor = httpPrefix + '://' + domainFull + '/users/' + nickname timelineStr = '' if pageNumber > 1: timelineStr += \ '
\n' + \ ' ' + translate['Page up'] + '\n' + \ '
\n' separatorStr = htmlPostSeparator(baseDir, None) ctr = 0 for published, item in sharesJson.items(): showContactButton = False if item['actor'] != actor: showContactButton = True showRemoveButton = False if item['actor'] == actor: showRemoveButton = True timelineStr += \ htmlIndividualShare(actor, item, translate, showContactButton, showRemoveButton) timelineStr += separatorStr ctr += 1 if ctr == 0: timelineStr += _getHelpForTimeline(baseDir, 'tlshares') if not lastPage: timelineStr += \ '
\n' + \ ' ' + translate['Page down'] + '\n' + \ '
\n' return timelineStr def htmlShares(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, allowDeletion: bool, httpPrefix: str, projectVersion: str, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the shares timeline as html """ manuallyApproveFollowers = \ followerApprovalActive(baseDir, nickname, domain) return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, None, 'tlshares', allowDeletion, httpPrefix, projectVersion, manuallyApproveFollowers, False, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlInbox(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, inboxJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the inbox as html """ manuallyApproveFollowers = \ followerApprovalActive(baseDir, nickname, domain) return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, inboxJson, 'inbox', allowDeletion, httpPrefix, projectVersion, manuallyApproveFollowers, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlBookmarks(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, bookmarksJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the bookmarks as html """ manuallyApproveFollowers = \ followerApprovalActive(baseDir, nickname, domain) return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, bookmarksJson, 'tlbookmarks', allowDeletion, httpPrefix, projectVersion, manuallyApproveFollowers, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlEvents(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, bookmarksJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the events as html """ manuallyApproveFollowers = \ followerApprovalActive(baseDir, nickname, domain) return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, bookmarksJson, 'tlevents', allowDeletion, httpPrefix, projectVersion, manuallyApproveFollowers, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlInboxDMs(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, inboxJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the DM timeline as html """ return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, inboxJson, 'dm', allowDeletion, httpPrefix, projectVersion, False, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlInboxReplies(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, inboxJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the replies timeline as html """ return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, inboxJson, 'tlreplies', allowDeletion, httpPrefix, projectVersion, False, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlInboxMedia(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, inboxJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the media timeline as html """ return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, inboxJson, 'tlmedia', allowDeletion, httpPrefix, projectVersion, False, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlInboxBlogs(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, inboxJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the blogs timeline as html """ return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, inboxJson, 'tlblogs', allowDeletion, httpPrefix, projectVersion, False, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlInboxFeatures(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, inboxJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the features timeline as html """ return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, inboxJson, 'tlfeatures', allowDeletion, httpPrefix, projectVersion, False, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlInboxNews(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, inboxJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, moderator: bool, editor: bool, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the news timeline as html """ return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, inboxJson, 'tlnews', allowDeletion, httpPrefix, projectVersion, False, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, moderator, editor, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner) def htmlOutbox(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, itemsPerPage: int, session, baseDir: str, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, outboxJson: {}, allowDeletion: bool, httpPrefix: str, projectVersion: str, minimal: bool, YTReplacementDomain: str, showPublishedDateOnly: bool, newswire: {}, positiveVoting: bool, showPublishAsIcon: bool, fullWidthTimelineButtonHeader: bool, iconsAsButtons: bool, rssIconAtTop: bool, publishButtonAtTop: bool, authorized: bool, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str) -> str: """Show the Outbox as html """ manuallyApproveFollowers = \ followerApprovalActive(baseDir, nickname, domain) return htmlTimeline(cssCache, defaultTimeline, recentPostsCache, maxRecentPosts, translate, pageNumber, itemsPerPage, session, baseDir, cachedWebfingers, personCache, nickname, domain, port, outboxJson, 'outbox', allowDeletion, httpPrefix, projectVersion, manuallyApproveFollowers, minimal, YTReplacementDomain, showPublishedDateOnly, newswire, False, False, positiveVoting, showPublishAsIcon, fullWidthTimelineButtonHeader, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner)