__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 removeHtml 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') 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() helpText = helpText.replace('INSTANCE', instanceTitle) return markdownToHtml(removeHtml(helpText)) 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 = \ '' + \ '\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 += '
' + 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 += \ '