epicyon/webapp_headerbuttons.py

398 lines
14 KiB
Python

__filename__ = "webapp_headerbuttons.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.1.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
import os
import time
from datetime import datetime
from happening import todaysEventsCheck
from happening import thisWeeksEventsCheck
from webapp_utils import htmlHighlightLabel
def headerButtonsTimeline(defaultTimeline: str,
boxName: str,
pageNumber: int,
translate: {},
usersPath: str,
minimal: bool,
moderator: bool,
manuallyApproveFollowers: bool,
baseDir: str,
nickname: str, domain: str,
timelineStartTime,
iconsAsButtons: bool,
userPages: []) -> str:
"""Returns the header at the top of the timeline, containing
buttons for inbox, outbox, search, calendar, etc
"""
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)
# start of the button header with inbox, outbox, etc
# TODO: [rename] containerHeader -> menu (or similar)
tlStr = '\t<div class="containerHeader">\n'
# TODO: Group _all_ items that are hidden in 'minimal' mode ?
# - Are there others elsewhere in the code ?
# All menu items default to text buttons
# Add class "icon-button" to overwrite default behaviour
# NOTE: Currently handled further down in loops over navButtonList and navIconList
# Menu buttons are split into two sections
# - Navigation buttons : Informational pages, e.g. Inbox
# - Action buttons : More clear action, e.g. Search, Create Post
# default appearance text; use CSS class(es) to re-style
# A list of buttons to be rendered as html,
# each item being a tuple containing
# - ref name
# - dictionary of unique config
# Each dict has:
# - pageRef : the url snippet
# - translationText : text to be displayed
# - highlightLabel : boolean to determine highlight
# - isIcon : boolean to trigger specific CSS class addition(s) for icon buttons
# - class : string of class(es) to append
# - iconClass : string of class(es) to append, only regarding icons
# Buttons should be added in the order you wish them to appear in the menu
navButtonList = []
navIconList = []
# Use this to selectively append to one or other of the above lists
activeButtonList = navButtonList
# Too many buttons - some visibility can be toggled
# TODO: Show/Hide menu buttons should not require page reload!
# Implement checkbox method
# Requires grouping the buttons that are shown/hidden ?
# TODO: The 'minimal' attribute could be done using CSS class ?
# Minimal View only shows:
# - Inbox
# - Outbox
# - DM
# - Replies
# - News # TODO: Should this be in minimal view? 'Media' and 'Blogs' are not ...
# NOTE: "Action" buttons (icons in default Epicyon) are always visible
activeButtonList.append(('inbox',
{'pageRef': '/inbox',
'translateText': 'Inbox'}
))
activeButtonList.append(('outbox',
{'pageRef': '/outbox',
'translateText': 'Outbox'}
))
activeButtonList.append(('dm',
{'pageRef': '/dm',
'translateText': 'DM',
'highlightLabel': newDM}
))
activeButtonList.append(('tlreplies',
{'pageRef': '/tlreplies',
'translateText': 'Replies',
'highlightLabel': newReply}
))
if (defaultTimeline == 'tlfeatures' and boxName in userPages) \
or (not defaultTimeline == 'tlfeatures' and not minimal):
activeButtonList.append(('tlblogs',
{'pageRef': '/tlblogs',
'translateText': 'Blogs'}
))
# The blog translation text should be 'Article' for News instances
if defaultTimeline == 'tlfeatures':
activeButtonList[-1][1]['translateText'] = 'Article'
activeButtonList.append(('tlmedia',
{'pageRef': '/tlmedia',
'translateText': 'Media'}
))
activeButtonList.append(('tlshares',
{'pageRef': '/tlshares',
'translateText': 'Shares',
'highlightLabel': newShare}
))
activeButtonList.append(('tlbookmarks',
{'pageRef': '/tlbookmarks',
'translateText': 'Bookmarks'}
))
activeButtonList.append(('tlevents',
{'pageRef': '/tlevents',
'translateText': 'Events'}
))
if moderator:
activeButtonList.append(('moderation',
{'pageRef': '/moderation',
'translateText': 'Mod',
'highlightLabel': newReport}
))
# Single out the "instance-type" button, and move to front of list
# NOTE: Current instance types are: 'tlblogs', 'tlmedia', 'tlfeatures'
for i, (name, config) in enumerate(activeButtonList):
if name == defaultTimeline:
tmp = activeButtonList.pop(i)
activeButtonList.insert(0, tmp)
break
# start of headericons list
# Override iconsAsButtons setting for News instance
if defaultTimeline == 'tlfeatures':
iconsAsButtons = True
# Only append to iconList if necessary
if not iconsAsButtons:
activeButtonList = navIconList
# show an icon for new follow approvals
followApprovals = False
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 = True
break
# NOTE: Certain buttons only appear when relevant
# TODO: Determine if approvals (currently appended at end of activeButtonList)
# and events should be in a separate list;
# e.g. notificationButtonList
# Having its' own <div> for instance would allow more versatile styling
if followApprovals:
activeButtonList.append(('followers',
{'pageRef': '/followers',
'translateText': 'Approve follow requests',
'iconClass': 'icon-newfollow'}
))
# Only show todays events buttons on the first inbox page
if boxName == 'inbox' and pageNumber == 1:
if todaysEventsCheck(baseDir, nickname, domain):
now = datetime.now()
todayRef = '/calendar?year=' + str(now.year) + \
'?month=' + str(now.month) + \
'?day=' + str(now.day)
activeButtonList.append(('today-event',
{'pageRef': todayRef,
'translateText': 'Happening Today',
'class': 'button-event'}
))
if thisWeeksEventsCheck(baseDir, nickname, domain):
activeButtonList.append(('week-event',
{'pageRef': '/calendar',
'translateText': 'Happening Today',
'class': 'button-event'}
))
# NOTE: CSS used to show or hide these based on screen size
activeButtonList.append(('newswire',
{'pageRef': '/newswiremobile',
'translateText': 'Newswire',
'class': 'mobile-view',
'iconClass': 'icon-newswire'}
))
activeButtonList.append(('links',
{'pageRef': '/linksmobile',
'translateText': 'Links',
'class': 'mobile-view',
'iconClass': 'icon-links'}
))
# what screen to go to when a new post is created
# The following produces one button/icon for "new post" dependent on current viewed page
if boxName == 'dm':
activeButtonList.append(('newdm',
{'pageRef': '/newdm',
'translateText': 'Create a new DM'}
))
elif boxName == 'tlblogs' or boxName == 'tlfeatures':
activeButtonList.append(('newblog',
{'pageRef': '/newblog',
'translateText': 'Create a new post'}
))
elif boxName == 'tlevents':
activeButtonList.append(('newevent',
{'pageRef': '/newevent',
'translateText': 'Create a new event'}
))
else:
if not manuallyApproveFollowers:
activeButtonList.append(('newpost',
{'pageRef': '/newpost',
'translateText': 'Create a new post'}
))
else:
activeButtonList.append(('newfollowers',
{'pageRef': '/newfollowers',
'translateText': 'Create a new post'}
))
# 'icon-<type>' is solely used for the CSS to load appropriate icon
activeButtonList[-1][1]['iconClass'] = 'icon-newpost'
activeButtonList.append(('calendar',
{'pageRef': calendarPath,
'translateText': 'Calendar',
'iconClass': 'icon-calendar'}
))
activeButtonList.append(('search',
{'pageRef': '/search',
'translateText': 'Search',
'iconClass': 'icon-search'}
))
# TODO: Less hacky solution - or make sure "settings" button has an icon
if defaultTimeline == 'tlfeatures':
activeButtonList.append(('editprofile',
{'pageRef': '/editprofile',
'translateText': 'Settings'}
))
else:
activeButtonList.append(('minimal',
{'pageRef': '/minimal',
'translateText': 'Show/Hide Buttons',
'iconClass': 'icon-showhide'}
))
# Generate HTML lists
navButtonStr = ""
if navButtonList:
navButtonStr += '\t\t<div class="navbuttons">\n'
navButtonStr += '\t\t\t<ul class="button-bar">\n'
for name, config in navButtonList:
classStr = 'button'
textStr = ''
if 'class' in config:
classStr += ' ' + config['class']
if 'highlightLabel' in config and config['highlightLabel']:
if name == boxName:
classStr += ' button-selected-highlighted'
else:
classStr += ' button-highlighted'
# TODO: Replace 'config' dereference with 'True' as it always will be by this point ?
textStr = htmlHighlightLabel(translate[config['translateText']], \
config['highlightLabel'])
else:
if name == boxName:
classStr += ' button-selected'
textStr += translate[config['translateText']]
navButtonStr += (f"\t\t\t\t<a class=\"{classStr}\" href=\"{usersPath}{config['pageRef']}\">\n"
f'\t\t\t\t\t<li>{textStr}</li>\n'
f'\t\t\t\t</a>\n')
navButtonStr += '\t\t\t</ul>\n\t\t</div>\n'
navIconStr = ""
if navIconList:
navIconStr += '\t\t<div class="actionbuttons">\n'
navIconStr += '\t\t\t<ul class="button-bar">\n'
# TODO: [rename] 'timelineicon' should maybe be more generic, e.g 'icon-button'
# Generate HTML list
for name, config in navIconList:
if iconsAsButtons:
classStr = 'button'
else:
# Currently 'timelineicon' denotes an icon instead of text button
classStr = 'timelineicon'
# NOTE: If iconClass is missing, expect some kind of failed output
# CSS alone is responsible for what image will be displayed, etc
if 'iconClass' in config:
classStr += ' ' + config['iconClass']
if 'class' in config:
classStr += ' ' + config['class']
if 'highlightLabel' in config:
textStr = htmlHighlightLabel(translate[config['translateText']], config['highlightLabel'])
else:
textStr = translate[config['translateText']]
navIconStr += (f"\t\t\t\t<a class=\"{classStr}\" href=\"{usersPath}{config['pageRef']}\">\n"
f'\t\t\t\t\t<li>{textStr}</li>\n'
f'\t\t\t\t</a>\n')
navIconStr += '\t\t\t</ul>\n\t\t</div>\n'
# benchmark 5
if timelineStartTime:
timeDiff = int((time.time() - timelineStartTime) * 1000)
if timeDiff > 100:
print('TIMELINE TIMING ' + boxName + ' 5 = ' + str(timeDiff))
# Compile HTML parts
tlStr += navButtonStr + navIconStr
# End header button section
tlStr += '\t</div>\n'
return tlStr