forked from indymedia/epicyon
433 lines
14 KiB
Python
433 lines
14 KiB
Python
__filename__ = "webapp_column_left.py"
|
|
__author__ = "Bob Mottram"
|
|
__license__ = "AGPL3+"
|
|
__version__ = "1.1.0"
|
|
__maintainer__ = "Bob Mottram"
|
|
__email__ = "bob@freedombone.net"
|
|
__status__ = "Production"
|
|
|
|
import os
|
|
from utils import getConfigParam
|
|
from utils import getNicknameFromActor
|
|
from utils import isEditor
|
|
from posts import isModerator
|
|
from webapp_utils import sharesTimelineJson
|
|
from webapp_utils import htmlPostSeparator
|
|
from webapp_utils import getLeftImageFile
|
|
from webapp_utils import headerButtonsFrontScreen
|
|
from webapp_utils import htmlHeaderWithExternalStyle, htmlHeaderWithExternalStyles
|
|
from webapp_utils import htmlHeaderBanner
|
|
from webapp_utils import htmlFooter
|
|
from webapp_utils import getBannerFile
|
|
|
|
|
|
def _linksExist(baseDir: str) -> bool:
|
|
"""Returns true if links have been created
|
|
"""
|
|
linksFilename = baseDir + '/accounts/links.txt'
|
|
return os.path.isfile(linksFilename)
|
|
|
|
|
|
def _getLeftColumnShares(baseDir: str,
|
|
httpPrefix: str, domainFull: str,
|
|
nickname: str,
|
|
maxSharesInLeftColumn: int,
|
|
translate: {}) -> []:
|
|
"""get any shares and turn them into the left column links format
|
|
"""
|
|
pageNumber = 1
|
|
actor = httpPrefix + '://' + domainFull + '/users/' + nickname
|
|
sharesJson, lastPage = \
|
|
sharesTimelineJson(actor, pageNumber,
|
|
maxSharesInLeftColumn,
|
|
baseDir, maxSharesInLeftColumn)
|
|
if not sharesJson:
|
|
return []
|
|
|
|
linksList = []
|
|
ctr = 0
|
|
for published, item in sharesJson.items():
|
|
sharedesc = item['displayName']
|
|
if '<' in sharedesc or '?' in sharedesc:
|
|
continue
|
|
contactActor = item['actor']
|
|
shareLink = actor + \
|
|
'?replydm=sharedesc:' + \
|
|
sharedesc.replace(' ', '_') + \
|
|
'?mention=' + contactActor
|
|
linksList.append(sharedesc + ' ' + shareLink)
|
|
ctr += 1
|
|
if ctr >= maxSharesInLeftColumn:
|
|
break
|
|
|
|
if linksList:
|
|
linksList = ['* ' + translate['Shares']] + linksList
|
|
return linksList
|
|
|
|
|
|
def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|
httpPrefix: str, translate: {},
|
|
editor: bool,
|
|
showBackButton: bool, timelinePath: str,
|
|
rssIconAtTop: bool, showHeaderImage: bool,
|
|
frontPage: bool, theme: str) -> str:
|
|
"""Returns html content for the left column
|
|
"""
|
|
htmlStr = ''
|
|
|
|
separatorStr = htmlPostSeparator(baseDir, 'left')
|
|
domain = domainFull
|
|
if ':' in domain:
|
|
domain = domain.split(':')
|
|
|
|
editImageClass = ''
|
|
|
|
# show a column header, eg. title of the theme or newswire banner
|
|
htmlStr += '<h1>Links</h1>\n'
|
|
|
|
if showBackButton:
|
|
htmlStr += \
|
|
' <div>' + \
|
|
' <a href="' + timelinePath + '">' + \
|
|
'<button class="cancelbtn">' + \
|
|
translate['Go Back'] + '</button></a>\n'
|
|
|
|
if (editor or rssIconAtTop):
|
|
htmlStr += '<div class="columnIcons">'
|
|
|
|
if editImageClass == 'leftColEdit':
|
|
htmlStr += '\n <center>\n'
|
|
|
|
if editor:
|
|
# show the edit icon
|
|
htmlStr += \
|
|
' <a href="' + \
|
|
'/users/' + nickname + '/editlinks">' + \
|
|
'<img class="' + editImageClass + \
|
|
'" loading="lazy" alt="' + \
|
|
translate['Edit Links'] + '" title="' + \
|
|
translate['Edit Links'] + '" src="/' + \
|
|
'icons/edit.png" /></a>\n'
|
|
|
|
# RSS icon
|
|
if nickname != 'news':
|
|
# rss feed for this account
|
|
rssUrl = httpPrefix + '://' + domainFull + \
|
|
'/blog/' + nickname + '/rss.xml'
|
|
else:
|
|
# rss feed for all accounts on the instance
|
|
rssUrl = httpPrefix + '://' + domainFull + '/blog/rss.xml'
|
|
if not frontPage:
|
|
rssTitle = translate['RSS feed for your blog']
|
|
else:
|
|
rssTitle = translate['RSS feed for this site']
|
|
rssIconStr = \
|
|
' <a href="' + rssUrl + '">' + \
|
|
'<img class="' + editImageClass + \
|
|
'" loading="lazy" alt="' + rssTitle + \
|
|
'" title="' + rssTitle + \
|
|
'" src="/icons/logorss.png" /></a>\n'
|
|
if rssIconAtTop:
|
|
htmlStr += rssIconStr
|
|
|
|
if editImageClass == 'leftColEdit':
|
|
htmlStr += ' </center>\n'
|
|
|
|
if (editor or rssIconAtTop):
|
|
htmlStr += '</div><br>'
|
|
|
|
# if showHeaderImage:
|
|
# htmlStr += '<br>'
|
|
|
|
# flag used not to show the first separator
|
|
firstSeparatorAdded = False
|
|
|
|
linksFilename = baseDir + '/accounts/links.txt'
|
|
linksFileContainsEntries = False
|
|
linksList = None
|
|
if os.path.isfile(linksFilename):
|
|
with open(linksFilename, "r") as f:
|
|
linksList = f.readlines()
|
|
|
|
if not frontPage:
|
|
# show a number of shares
|
|
maxSharesInLeftColumn = 3
|
|
sharesList = \
|
|
_getLeftColumnShares(baseDir,
|
|
httpPrefix, domainFull, nickname,
|
|
maxSharesInLeftColumn, translate)
|
|
if linksList and sharesList:
|
|
linksList = sharesList + linksList
|
|
|
|
if linksList:
|
|
# Bool to enable clean <div> creation for sub-lists
|
|
firstList = True
|
|
|
|
for lineStr in linksList:
|
|
if ' ' not in lineStr:
|
|
if '#' not in lineStr:
|
|
if '*' not in lineStr:
|
|
continue
|
|
lineStr = lineStr.strip()
|
|
words = lineStr.split(' ')
|
|
# get the link
|
|
linkStr = None
|
|
for word in words:
|
|
if word == '#':
|
|
continue
|
|
if word == '*':
|
|
continue
|
|
if '://' in word:
|
|
linkStr = word
|
|
break
|
|
if linkStr:
|
|
lineStr = lineStr.replace(linkStr, '').strip()
|
|
# avoid any dubious scripts being added
|
|
if '<' not in lineStr:
|
|
# remove trailing comma if present
|
|
if lineStr.endswith(','):
|
|
lineStr = lineStr[:len(lineStr)-1]
|
|
# add link to the returned html
|
|
htmlStr += \
|
|
' <a href="' + linkStr + '">' + \
|
|
lineStr + '</a>\n'
|
|
linksFileContainsEntries = True
|
|
else:
|
|
if lineStr.startswith('#') or lineStr.startswith('*'):
|
|
if not firstList:
|
|
htmlStr += \
|
|
' </div>'
|
|
firstList = False
|
|
lineStr = lineStr[1:].strip()
|
|
if firstSeparatorAdded:
|
|
htmlStr += separatorStr
|
|
firstSeparatorAdded = True
|
|
htmlStr += \
|
|
' <div class="links-list">'
|
|
htmlStr += \
|
|
' <h3 class="linksHeader">' + \
|
|
lineStr + '</h3>\n'
|
|
else:
|
|
htmlStr += \
|
|
' <p>' + lineStr + '</p>\n'
|
|
linksFileContainsEntries = True
|
|
# Close of last links-list
|
|
htmlStr += \
|
|
' </div>'
|
|
|
|
if firstSeparatorAdded:
|
|
htmlStr += separatorStr
|
|
htmlStr += \
|
|
'<div class="login-text">\n' + \
|
|
'<a href="/about">' + \
|
|
translate['About this Instance'] + \
|
|
'</a>\n' + \
|
|
'<a href="/terms">' + \
|
|
translate['Terms of Service'] + \
|
|
'</a>\n' + \
|
|
'</div>\n'
|
|
|
|
if linksFileContainsEntries and not rssIconAtTop:
|
|
htmlStr += '<br><div class="columnIcons">' + rssIconStr + '</div>'
|
|
|
|
return htmlStr
|
|
|
|
|
|
def htmlLinksMobile(cssCache: {}, baseDir: str,
|
|
nickname: str, domainFull: str,
|
|
httpPrefix: str, translate,
|
|
timelinePath: str, authorized: bool,
|
|
rssIconAtTop: bool,
|
|
iconsAsButtons: bool,
|
|
defaultTimeline: str,
|
|
theme: str) -> str:
|
|
"""Show the left column links within mobile view
|
|
"""
|
|
htmlStr = ''
|
|
|
|
cssFiles = []
|
|
|
|
# the css filename
|
|
cssFiles.append(baseDir + '/epicyon-profile.css')
|
|
if os.path.isfile(baseDir + '/epicyon.css'):
|
|
cssFiles[0] = baseDir + '/epicyon.css'
|
|
|
|
# TODO: Clean up and remove this override
|
|
cssFiles[0] = 'base.css'
|
|
|
|
# Get theme-specific css if exists - must be named '<theme-name>.css'
|
|
themeName = getConfigParam(baseDir, 'theme')
|
|
|
|
themePath = f'{baseDir}/theme/{themeName}.css'
|
|
if os.path.isfile(themePath):
|
|
cssFiles.append('theme/' + themeName + '.css')
|
|
|
|
# is the user a site editor?
|
|
if nickname == 'news':
|
|
editor = False
|
|
moderator = False
|
|
else:
|
|
# is the user a moderator?
|
|
moderator = isModerator(baseDir, nickname)
|
|
|
|
# is the user a site editor?
|
|
editor = isEditor(baseDir, nickname)
|
|
|
|
domain = domainFull
|
|
if ':' in domain:
|
|
domain = domain.split(':')[0]
|
|
|
|
htmlStr = htmlHeaderWithExternalStyles(cssFiles)
|
|
|
|
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain, theme)
|
|
usersPath = '/users/' + nickname
|
|
|
|
# Certain Epciyon pages should only be accessible via the 'User' page for News instances
|
|
userPages = ['inbox', 'outbox', 'dm', 'tlreplies', 'tlblogs', 'tlmedia', 'tlshares', \
|
|
'tlsaves', 'tlevents', 'tlbookmarks', 'moderation', 'search', \
|
|
'followers', 'newfollowers']
|
|
|
|
htmlStr += htmlHeaderBanner(defaultTimeline, 'linksmobile', baseDir, usersPath,
|
|
authorized, translate, userPages, bannerFile)
|
|
|
|
htmlStr += '<div class="col-left-mobile">\n'
|
|
htmlStr += '<center>' + \
|
|
headerButtonsFrontScreen(translate, nickname,
|
|
'links', authorized,
|
|
iconsAsButtons) + '</center>'
|
|
if _linksExist(baseDir):
|
|
htmlStr += \
|
|
getLeftColumnContent(baseDir, nickname, domainFull,
|
|
httpPrefix, translate,
|
|
editor,
|
|
False, timelinePath,
|
|
rssIconAtTop, False, False,
|
|
theme)
|
|
else:
|
|
if editor:
|
|
htmlStr += '<br><br><br>\n'
|
|
htmlStr += '<center>\n '
|
|
htmlStr += translate['Select the edit icon to add web links']
|
|
htmlStr += '\n</center>\n'
|
|
|
|
# end of col-left-mobile
|
|
htmlStr += '</div>\n'
|
|
|
|
htmlStr += '</div>\n' + htmlFooter()
|
|
return htmlStr
|
|
|
|
|
|
def htmlEditLinks(cssCache: {}, translate: {}, baseDir: str, path: str,
|
|
domain: str, port: int, httpPrefix: str,
|
|
defaultTimeline: str, theme: str) -> str:
|
|
"""Shows the edit links screen
|
|
"""
|
|
if '/users/' not in path:
|
|
return ''
|
|
path = path.replace('/inbox', '').replace('/outbox', '')
|
|
path = path.replace('/shares', '')
|
|
|
|
nickname = getNicknameFromActor(path)
|
|
if not nickname:
|
|
return ''
|
|
|
|
# is the user a moderator?
|
|
if not isEditor(baseDir, nickname):
|
|
return ''
|
|
|
|
cssFilename = baseDir + '/epicyon-links.css'
|
|
if os.path.isfile(baseDir + '/links.css'):
|
|
cssFilename = baseDir + '/links.css'
|
|
|
|
# filename of the banner shown at the top
|
|
bannerFile, bannerFilename = \
|
|
getBannerFile(baseDir, nickname, domain, theme)
|
|
|
|
editLinksForm = htmlHeaderWithExternalStyle(cssFilename)
|
|
|
|
# top banner
|
|
editLinksForm += \
|
|
'<header>\n' + \
|
|
'<a href="/users/' + nickname + '/' + defaultTimeline + '" title="' + \
|
|
translate['Switch to timeline view'] + '" alt="' + \
|
|
translate['Switch to timeline view'] + '">\n'
|
|
editLinksForm += '<img loading="lazy" class="timeline-banner" src="' + \
|
|
'/users/' + nickname + '/' + bannerFile + '" /></a>\n' + \
|
|
'</header>\n'
|
|
|
|
editLinksForm += \
|
|
'<form enctype="multipart/form-data" method="POST" ' + \
|
|
'accept-charset="UTF-8" action="' + path + '/linksdata">\n'
|
|
editLinksForm += \
|
|
' <div class="vertical-center">\n'
|
|
editLinksForm += \
|
|
' <div class="containerSubmitNewPost">\n'
|
|
editLinksForm += \
|
|
' <h1>' + translate['Edit Links'] + '</h1>'
|
|
editLinksForm += \
|
|
' <input type="submit" name="submitLinks" value="' + \
|
|
translate['Submit'] + '">\n'
|
|
editLinksForm += \
|
|
' </div>\n'
|
|
|
|
linksFilename = baseDir + '/accounts/links.txt'
|
|
linksStr = ''
|
|
if os.path.isfile(linksFilename):
|
|
with open(linksFilename, 'r') as fp:
|
|
linksStr = fp.read()
|
|
|
|
editLinksForm += \
|
|
'<div class="container">'
|
|
editLinksForm += \
|
|
' ' + \
|
|
translate['One link per line. Description followed by the link.'] + \
|
|
'<br>'
|
|
editLinksForm += \
|
|
' <textarea id="message" name="editedLinks" style="height:80vh">' + \
|
|
linksStr + '</textarea>'
|
|
editLinksForm += \
|
|
'</div>'
|
|
|
|
# the admin can edit terms of service and about text
|
|
adminNickname = getConfigParam(baseDir, 'admin')
|
|
if adminNickname:
|
|
if nickname == adminNickname:
|
|
aboutFilename = baseDir + '/accounts/about.txt'
|
|
aboutStr = ''
|
|
if os.path.isfile(aboutFilename):
|
|
with open(aboutFilename, 'r') as fp:
|
|
aboutStr = fp.read()
|
|
|
|
editLinksForm += \
|
|
'<div class="container">'
|
|
editLinksForm += \
|
|
' ' + \
|
|
translate['About this Instance'] + \
|
|
'<br>'
|
|
editLinksForm += \
|
|
' <textarea id="message" name="editedAbout" ' + \
|
|
'style="height:100vh">' + aboutStr + '</textarea>'
|
|
editLinksForm += \
|
|
'</div>'
|
|
|
|
TOSFilename = baseDir + '/accounts/tos.txt'
|
|
TOSStr = ''
|
|
if os.path.isfile(TOSFilename):
|
|
with open(TOSFilename, 'r') as fp:
|
|
TOSStr = fp.read()
|
|
|
|
editLinksForm += \
|
|
'<div class="container">'
|
|
editLinksForm += \
|
|
' ' + \
|
|
translate['Terms of Service'] + \
|
|
'<br>'
|
|
editLinksForm += \
|
|
' <textarea id="message" name="editedTOS" ' + \
|
|
'style="height:100vh">' + TOSStr + '</textarea>'
|
|
editLinksForm += \
|
|
'</div>'
|
|
|
|
editLinksForm += htmlFooter()
|
|
return editLinksForm
|