mirror of https://gitlab.com/bashrc2/epicyon
Split up webapp into smaller modules
parent
4a3a2c2319
commit
2f82d2281c
46
daemon.py
46
daemon.py
|
@ -114,46 +114,46 @@ from blog import htmlBlogPage
|
||||||
from blog import htmlBlogPost
|
from blog import htmlBlogPost
|
||||||
from blog import htmlEditBlog
|
from blog import htmlEditBlog
|
||||||
from webapp_utils import setBlogAddress
|
from webapp_utils import setBlogAddress
|
||||||
|
from webapp_utils import getBlogAddress
|
||||||
from webapp_calendar import htmlCalendarDeleteConfirm
|
from webapp_calendar import htmlCalendarDeleteConfirm
|
||||||
from webapp_calendar import htmlCalendar
|
from webapp_calendar import htmlCalendar
|
||||||
from webapp import htmlCitations
|
|
||||||
from webapp import htmlFollowingList
|
from webapp import htmlFollowingList
|
||||||
from webapp import getBlogAddress
|
|
||||||
from webapp import htmlDeletePost
|
from webapp import htmlDeletePost
|
||||||
from webapp import htmlAbout
|
from webapp import htmlAbout
|
||||||
from webapp import htmlRemoveSharedItem
|
from webapp import htmlRemoveSharedItem
|
||||||
from webapp import htmlInboxDMs
|
|
||||||
from webapp import htmlInboxReplies
|
|
||||||
from webapp import htmlInboxMedia
|
|
||||||
from webapp import htmlInboxBlogs
|
|
||||||
from webapp import htmlInboxNews
|
|
||||||
from webapp import htmlUnblockConfirm
|
from webapp import htmlUnblockConfirm
|
||||||
from webapp import htmlPersonOptions
|
from webapp_person_options import htmlPersonOptions
|
||||||
from webapp import htmlIndividualPost
|
from webapp_timeline import htmlShares
|
||||||
from webapp import htmlProfile
|
from webapp_timeline import htmlInbox
|
||||||
from webapp import htmlInbox
|
from webapp_timeline import htmlBookmarks
|
||||||
from webapp import htmlBookmarks
|
from webapp_timeline import htmlEvents
|
||||||
from webapp import htmlEvents
|
from webapp_timeline import htmlInboxDMs
|
||||||
from webapp import htmlShares
|
from webapp_timeline import htmlInboxReplies
|
||||||
from webapp import htmlOutbox
|
from webapp_timeline import htmlInboxMedia
|
||||||
from webapp import htmlModeration
|
from webapp_timeline import htmlInboxBlogs
|
||||||
from webapp import htmlPostReplies
|
from webapp_timeline import htmlInboxNews
|
||||||
|
from webapp_timeline import htmlOutbox
|
||||||
|
from webapp_timeline import htmlModeration
|
||||||
from webapp import htmlLogin
|
from webapp import htmlLogin
|
||||||
from webapp import htmlSuspended
|
from webapp import htmlSuspended
|
||||||
from webapp import htmlGetLoginCredentials
|
from webapp import htmlGetLoginCredentials
|
||||||
from webapp import htmlNewPost
|
from webapp import htmlNewPost
|
||||||
from webapp import htmlFollowConfirm
|
from webapp import htmlFollowConfirm
|
||||||
from webapp import htmlNewswireMobile
|
|
||||||
from webapp import htmlLinksMobile
|
|
||||||
from webapp import htmlUnfollowConfirm
|
from webapp import htmlUnfollowConfirm
|
||||||
from webapp import htmlProfileAfterSearch
|
|
||||||
from webapp import htmlEditProfile
|
|
||||||
from webapp import htmlEditLinks
|
|
||||||
from webapp import htmlEditNewswire
|
|
||||||
from webapp import htmlEditNewsPost
|
from webapp import htmlEditNewsPost
|
||||||
from webapp import htmlTermsOfService
|
from webapp import htmlTermsOfService
|
||||||
from webapp import htmlModerationInfo
|
from webapp import htmlModerationInfo
|
||||||
from webapp import htmlHashtagBlocked
|
from webapp import htmlHashtagBlocked
|
||||||
|
from webapp_post import htmlPostReplies
|
||||||
|
from webapp_post import htmlIndividualPost
|
||||||
|
from webapp_profile import htmlEditProfile
|
||||||
|
from webapp_profile import htmlProfileAfterSearch
|
||||||
|
from webapp_profile import htmlProfile
|
||||||
|
from webapp_column_left import htmlLinksMobile
|
||||||
|
from webapp_column_left import htmlEditLinks
|
||||||
|
from webapp_column_right import htmlNewswireMobile
|
||||||
|
from webapp_column_right import htmlEditNewswire
|
||||||
|
from webapp_column_right import htmlCitations
|
||||||
from webapp_search import htmlSkillsSearch
|
from webapp_search import htmlSkillsSearch
|
||||||
from webapp_search import htmlHistorySearch
|
from webapp_search import htmlHistorySearch
|
||||||
from webapp_search import htmlHashtagSearch
|
from webapp_search import htmlHashtagSearch
|
||||||
|
|
|
@ -0,0 +1,317 @@
|
||||||
|
__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 shutil import copyfile
|
||||||
|
from utils import getConfigParam
|
||||||
|
from utils import getCSS
|
||||||
|
from utils import getNicknameFromActor
|
||||||
|
from posts import isEditor
|
||||||
|
from webapp_utils import htmlPostSeparator
|
||||||
|
from webapp_utils import getLeftImageFile
|
||||||
|
from webapp_utils import getImageFile
|
||||||
|
from webapp_utils import headerButtonsFrontScreen
|
||||||
|
from webapp_utils import getIconsDir
|
||||||
|
from webapp_utils import htmlHeader
|
||||||
|
from webapp_utils import htmlFooter
|
||||||
|
from webapp_utils import getBannerFile
|
||||||
|
|
||||||
|
|
||||||
|
def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
||||||
|
httpPrefix: str, translate: {},
|
||||||
|
iconsDir: str, editor: bool,
|
||||||
|
showBackButton: bool, timelinePath: str,
|
||||||
|
rssIconAtTop: bool, showHeaderImage: bool,
|
||||||
|
frontPage: bool) -> str:
|
||||||
|
"""Returns html content for the left column
|
||||||
|
"""
|
||||||
|
htmlStr = ''
|
||||||
|
|
||||||
|
separatorStr = htmlPostSeparator(baseDir, 'left')
|
||||||
|
domain = domainFull
|
||||||
|
if ':' in domain:
|
||||||
|
domain = domain.split(':')
|
||||||
|
|
||||||
|
editImageClass = ''
|
||||||
|
if showHeaderImage:
|
||||||
|
leftImageFile, leftColumnImageFilename = \
|
||||||
|
getLeftImageFile(baseDir, nickname, domain)
|
||||||
|
if not os.path.isfile(leftColumnImageFilename):
|
||||||
|
theme = getConfigParam(baseDir, 'theme').lower()
|
||||||
|
if theme == 'default':
|
||||||
|
theme = ''
|
||||||
|
else:
|
||||||
|
theme = '_' + theme
|
||||||
|
themeLeftImageFile, themeLeftColumnImageFilename = \
|
||||||
|
getImageFile(baseDir, 'left_col_image', baseDir + '/img',
|
||||||
|
nickname, domain)
|
||||||
|
if os.path.isfile(themeLeftColumnImageFilename):
|
||||||
|
leftColumnImageFilename = \
|
||||||
|
baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/' + themeLeftImageFile
|
||||||
|
copyfile(themeLeftColumnImageFilename,
|
||||||
|
leftColumnImageFilename)
|
||||||
|
leftImageFile = themeLeftImageFile
|
||||||
|
|
||||||
|
# show the image at the top of the column
|
||||||
|
editImageClass = 'leftColEdit'
|
||||||
|
if os.path.isfile(leftColumnImageFilename):
|
||||||
|
editImageClass = 'leftColEditImage'
|
||||||
|
htmlStr += \
|
||||||
|
'\n <center>\n' + \
|
||||||
|
' <img class="leftColImg" ' + \
|
||||||
|
'loading="lazy" src="/users/' + \
|
||||||
|
nickname + '/' + leftImageFile + '" />\n' + \
|
||||||
|
' </center>\n'
|
||||||
|
|
||||||
|
if showBackButton:
|
||||||
|
htmlStr += \
|
||||||
|
' <div>' + \
|
||||||
|
' <a href="' + timelinePath + '">' + \
|
||||||
|
'<button class="cancelbtn">' + \
|
||||||
|
translate['Go Back'] + '</button></a>\n'
|
||||||
|
|
||||||
|
if (editor or rssIconAtTop) and not showHeaderImage:
|
||||||
|
htmlStr += '<div class="columnIcons">'
|
||||||
|
|
||||||
|
if editImageClass == 'leftColEdit':
|
||||||
|
htmlStr += '\n <center>\n'
|
||||||
|
|
||||||
|
htmlStr += ' <div class="leftColIcons">\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="/' + \
|
||||||
|
iconsDir + '/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="/' + iconsDir + '/logorss.png" /></a>\n'
|
||||||
|
if rssIconAtTop:
|
||||||
|
htmlStr += rssIconStr
|
||||||
|
htmlStr += ' </div>\n'
|
||||||
|
|
||||||
|
if editImageClass == 'leftColEdit':
|
||||||
|
htmlStr += ' </center>\n'
|
||||||
|
|
||||||
|
if (editor or rssIconAtTop) and not showHeaderImage:
|
||||||
|
htmlStr += '</div><br>'
|
||||||
|
|
||||||
|
# if showHeaderImage:
|
||||||
|
# htmlStr += '<br>'
|
||||||
|
|
||||||
|
linksFilename = baseDir + '/accounts/links.txt'
|
||||||
|
linksFileContainsEntries = False
|
||||||
|
if os.path.isfile(linksFilename):
|
||||||
|
linksList = None
|
||||||
|
with open(linksFilename, "r") as f:
|
||||||
|
linksList = f.readlines()
|
||||||
|
if linksList:
|
||||||
|
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 += \
|
||||||
|
' <p><a href="' + linkStr + '">' + \
|
||||||
|
lineStr + '</a></p>\n'
|
||||||
|
linksFileContainsEntries = True
|
||||||
|
else:
|
||||||
|
if lineStr.startswith('#') or lineStr.startswith('*'):
|
||||||
|
lineStr = lineStr[1:].strip()
|
||||||
|
htmlStr += separatorStr
|
||||||
|
htmlStr += \
|
||||||
|
' <h3 class="linksHeader">' + \
|
||||||
|
lineStr + '</h3>\n'
|
||||||
|
else:
|
||||||
|
htmlStr += \
|
||||||
|
' <p>' + lineStr + '</p>\n'
|
||||||
|
linksFileContainsEntries = True
|
||||||
|
|
||||||
|
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) -> str:
|
||||||
|
"""Show the left column links within mobile view
|
||||||
|
"""
|
||||||
|
htmlStr = ''
|
||||||
|
|
||||||
|
# the css filename
|
||||||
|
cssFilename = baseDir + '/epicyon-profile.css'
|
||||||
|
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||||
|
cssFilename = baseDir + '/epicyon.css'
|
||||||
|
|
||||||
|
profileStyle = getCSS(baseDir, cssFilename, cssCache)
|
||||||
|
if profileStyle:
|
||||||
|
# replace any https within the css with whatever prefix is needed
|
||||||
|
if httpPrefix != 'https':
|
||||||
|
profileStyle = \
|
||||||
|
profileStyle.replace('https://', httpPrefix + '://')
|
||||||
|
|
||||||
|
iconsDir = getIconsDir(baseDir)
|
||||||
|
|
||||||
|
# is the user a site editor?
|
||||||
|
if nickname == 'news':
|
||||||
|
editor = False
|
||||||
|
else:
|
||||||
|
editor = isEditor(baseDir, nickname)
|
||||||
|
|
||||||
|
domain = domainFull
|
||||||
|
if ':' in domain:
|
||||||
|
domain = domain.split(':')[0]
|
||||||
|
|
||||||
|
htmlStr = htmlHeader(cssFilename, profileStyle)
|
||||||
|
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain)
|
||||||
|
htmlStr += \
|
||||||
|
'<a href="/users/' + nickname + '/' + defaultTimeline + '">' + \
|
||||||
|
'<img loading="lazy" class="timeline-banner" ' + \
|
||||||
|
'src="/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
||||||
|
|
||||||
|
htmlStr += '<center>' + \
|
||||||
|
headerButtonsFrontScreen(translate, nickname,
|
||||||
|
'links', authorized,
|
||||||
|
iconsAsButtons, iconsDir) + '</center>'
|
||||||
|
htmlStr += \
|
||||||
|
getLeftColumnContent(baseDir, nickname, domainFull,
|
||||||
|
httpPrefix, translate,
|
||||||
|
iconsDir, editor,
|
||||||
|
False, timelinePath,
|
||||||
|
rssIconAtTop, False, False)
|
||||||
|
htmlStr += '</div>\n' + htmlFooter()
|
||||||
|
return htmlStr
|
||||||
|
|
||||||
|
|
||||||
|
def htmlEditLinks(cssCache: {}, translate: {}, baseDir: str, path: str,
|
||||||
|
domain: str, port: int, httpPrefix: str,
|
||||||
|
defaultTimeline: 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'
|
||||||
|
|
||||||
|
editCSS = getCSS(baseDir, cssFilename, cssCache)
|
||||||
|
if editCSS:
|
||||||
|
if httpPrefix != 'https':
|
||||||
|
editCSS = \
|
||||||
|
editCSS.replace('https://', httpPrefix + '://')
|
||||||
|
|
||||||
|
# filename of the banner shown at the top
|
||||||
|
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain)
|
||||||
|
|
||||||
|
editLinksForm = htmlHeader(cssFilename, editCSS)
|
||||||
|
|
||||||
|
# top banner
|
||||||
|
editLinksForm += \
|
||||||
|
'<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'
|
||||||
|
|
||||||
|
editLinksForm += \
|
||||||
|
'<form enctype="multipart/form-data" method="POST" ' + \
|
||||||
|
'accept-charset="UTF-8" action="' + path + '/linksdata">\n'
|
||||||
|
editLinksForm += \
|
||||||
|
' <div class="vertical-center">\n'
|
||||||
|
editLinksForm += \
|
||||||
|
' <p class="new-post-text">' + translate['Edit Links'] + '</p>'
|
||||||
|
editLinksForm += \
|
||||||
|
' <div class="container">\n'
|
||||||
|
# editLinksForm += \
|
||||||
|
# ' <a href="' + pathOriginal + '"><button class="cancelbtn">' + \
|
||||||
|
# translate['Go Back'] + '</button></a>\n'
|
||||||
|
editLinksForm += \
|
||||||
|
' <center>\n' + \
|
||||||
|
' <input type="submit" name="submitLinks" value="' + \
|
||||||
|
translate['Submit'] + '">\n' + \
|
||||||
|
' </center>\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>'
|
||||||
|
|
||||||
|
editLinksForm += htmlFooter()
|
||||||
|
return editLinksForm
|
|
@ -0,0 +1,567 @@
|
||||||
|
__filename__ = "webapp_column_right.py"
|
||||||
|
__author__ = "Bob Mottram"
|
||||||
|
__license__ = "AGPL3+"
|
||||||
|
__version__ = "1.1.0"
|
||||||
|
__maintainer__ = "Bob Mottram"
|
||||||
|
__email__ = "bob@freedombone.net"
|
||||||
|
__status__ = "Production"
|
||||||
|
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from shutil import copyfile
|
||||||
|
from content import removeLongWords
|
||||||
|
from utils import getCSS
|
||||||
|
from utils import getConfigParam
|
||||||
|
from utils import votesOnNewswireItem
|
||||||
|
from utils import getNicknameFromActor
|
||||||
|
from posts import isEditor
|
||||||
|
from posts import isModerator
|
||||||
|
from webapp_utils import getRightImageFile
|
||||||
|
from webapp_utils import getImageFile
|
||||||
|
from webapp_utils import htmlHeader
|
||||||
|
from webapp_utils import htmlFooter
|
||||||
|
from webapp_utils import getBannerFile
|
||||||
|
from webapp_utils import htmlPostSeparator
|
||||||
|
from webapp_utils import headerButtonsFrontScreen
|
||||||
|
from webapp_utils import getIconsDir
|
||||||
|
|
||||||
|
|
||||||
|
def votesIndicator(totalVotes: int, positiveVoting: bool) -> str:
|
||||||
|
"""Returns an indicator of the number of votes on a newswire item
|
||||||
|
"""
|
||||||
|
if totalVotes <= 0:
|
||||||
|
return ''
|
||||||
|
totalVotesStr = ' '
|
||||||
|
for v in range(totalVotes):
|
||||||
|
if positiveVoting:
|
||||||
|
totalVotesStr += '✓'
|
||||||
|
else:
|
||||||
|
totalVotesStr += '✗'
|
||||||
|
return totalVotesStr
|
||||||
|
|
||||||
|
|
||||||
|
def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
|
||||||
|
httpPrefix: str, translate: {},
|
||||||
|
iconsDir: str, moderator: bool, editor: bool,
|
||||||
|
newswire: {}, positiveVoting: bool,
|
||||||
|
showBackButton: bool, timelinePath: str,
|
||||||
|
showPublishButton: bool,
|
||||||
|
showPublishAsIcon: bool,
|
||||||
|
rssIconAtTop: bool,
|
||||||
|
publishButtonAtTop: bool,
|
||||||
|
authorized: bool,
|
||||||
|
showHeaderImage: bool) -> str:
|
||||||
|
"""Returns html content for the right column
|
||||||
|
"""
|
||||||
|
htmlStr = ''
|
||||||
|
|
||||||
|
domain = domainFull
|
||||||
|
if ':' in domain:
|
||||||
|
domain = domain.split(':')
|
||||||
|
|
||||||
|
if authorized:
|
||||||
|
# only show the publish button if logged in, otherwise replace it with
|
||||||
|
# a login button
|
||||||
|
publishButtonStr = \
|
||||||
|
' <a href="' + \
|
||||||
|
'/users/' + nickname + '/newblog" ' + \
|
||||||
|
'title="' + translate['Publish a news article'] + '">' + \
|
||||||
|
'<button class="publishbtn">' + \
|
||||||
|
translate['Publish'] + '</button></a>\n'
|
||||||
|
else:
|
||||||
|
# if not logged in then replace the publish button with
|
||||||
|
# a login button
|
||||||
|
publishButtonStr = \
|
||||||
|
' <a href="/login"><button class="publishbtn">' + \
|
||||||
|
translate['Login'] + '</button></a>\n'
|
||||||
|
|
||||||
|
# show publish button at the top if needed
|
||||||
|
if publishButtonAtTop:
|
||||||
|
htmlStr += '<center>' + publishButtonStr + '</center>'
|
||||||
|
|
||||||
|
# show a column header image, eg. title of the theme or newswire banner
|
||||||
|
editImageClass = ''
|
||||||
|
if showHeaderImage:
|
||||||
|
rightImageFile, rightColumnImageFilename = \
|
||||||
|
getRightImageFile(baseDir, nickname, domain)
|
||||||
|
if not os.path.isfile(rightColumnImageFilename):
|
||||||
|
theme = getConfigParam(baseDir, 'theme').lower()
|
||||||
|
if theme == 'default':
|
||||||
|
theme = ''
|
||||||
|
else:
|
||||||
|
theme = '_' + theme
|
||||||
|
themeRightImageFile, themeRightColumnImageFilename = \
|
||||||
|
getImageFile(baseDir, 'right_col_image', baseDir + '/img',
|
||||||
|
nickname, domain)
|
||||||
|
if os.path.isfile(themeRightColumnImageFilename):
|
||||||
|
rightColumnImageFilename = \
|
||||||
|
baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/' + themeRightImageFile
|
||||||
|
copyfile(themeRightColumnImageFilename,
|
||||||
|
rightColumnImageFilename)
|
||||||
|
rightImageFile = themeRightImageFile
|
||||||
|
|
||||||
|
# show the image at the top of the column
|
||||||
|
editImageClass = 'rightColEdit'
|
||||||
|
if os.path.isfile(rightColumnImageFilename):
|
||||||
|
editImageClass = 'rightColEditImage'
|
||||||
|
htmlStr += \
|
||||||
|
'\n <center>\n' + \
|
||||||
|
' <img class="rightColImg" ' + \
|
||||||
|
'loading="lazy" src="/users/' + \
|
||||||
|
nickname + '/' + rightImageFile + '" />\n' + \
|
||||||
|
' </center>\n'
|
||||||
|
|
||||||
|
if (showPublishButton or editor or rssIconAtTop) and not showHeaderImage:
|
||||||
|
htmlStr += '<div class="columnIcons">'
|
||||||
|
|
||||||
|
if editImageClass == 'rightColEdit':
|
||||||
|
htmlStr += '\n <center>\n'
|
||||||
|
|
||||||
|
# whether to show a back icon
|
||||||
|
# This is probably going to be osolete soon
|
||||||
|
if showBackButton:
|
||||||
|
htmlStr += \
|
||||||
|
' <a href="' + timelinePath + '">' + \
|
||||||
|
'<button class="cancelbtn">' + \
|
||||||
|
translate['Go Back'] + '</button></a>\n'
|
||||||
|
|
||||||
|
if showPublishButton and not publishButtonAtTop:
|
||||||
|
if not showPublishAsIcon:
|
||||||
|
htmlStr += publishButtonStr
|
||||||
|
|
||||||
|
# show the edit icon
|
||||||
|
if editor:
|
||||||
|
if os.path.isfile(baseDir + '/accounts/newswiremoderation.txt'):
|
||||||
|
# show the edit icon highlighted
|
||||||
|
htmlStr += \
|
||||||
|
' <a href="' + \
|
||||||
|
'/users/' + nickname + '/editnewswire">' + \
|
||||||
|
'<img class="' + editImageClass + \
|
||||||
|
'" loading="lazy" alt="' + \
|
||||||
|
translate['Edit newswire'] + '" title="' + \
|
||||||
|
translate['Edit newswire'] + '" src="/' + \
|
||||||
|
iconsDir + '/edit_notify.png" /></a>\n'
|
||||||
|
else:
|
||||||
|
# show the edit icon
|
||||||
|
htmlStr += \
|
||||||
|
' <a href="' + \
|
||||||
|
'/users/' + nickname + '/editnewswire">' + \
|
||||||
|
'<img class="' + editImageClass + \
|
||||||
|
'" loading="lazy" alt="' + \
|
||||||
|
translate['Edit newswire'] + '" title="' + \
|
||||||
|
translate['Edit newswire'] + '" src="/' + \
|
||||||
|
iconsDir + '/edit.png" /></a>\n'
|
||||||
|
|
||||||
|
# show the RSS icon
|
||||||
|
rssIconStr = \
|
||||||
|
' <a href="/newswire.xml">' + \
|
||||||
|
'<img class="' + editImageClass + \
|
||||||
|
'" loading="lazy" alt="' + \
|
||||||
|
translate['Newswire RSS Feed'] + '" title="' + \
|
||||||
|
translate['Newswire RSS Feed'] + '" src="/' + \
|
||||||
|
iconsDir + '/logorss.png" /></a>\n'
|
||||||
|
if rssIconAtTop:
|
||||||
|
htmlStr += rssIconStr
|
||||||
|
|
||||||
|
# show publish icon at top
|
||||||
|
if showPublishButton:
|
||||||
|
if showPublishAsIcon:
|
||||||
|
htmlStr += \
|
||||||
|
' <a href="' + \
|
||||||
|
'/users/' + nickname + '/newblog">' + \
|
||||||
|
'<img class="' + editImageClass + \
|
||||||
|
'" loading="lazy" alt="' + \
|
||||||
|
translate['Publish a news article'] + '" title="' + \
|
||||||
|
translate['Publish a news article'] + '" src="/' + \
|
||||||
|
iconsDir + '/publish.png" /></a>\n'
|
||||||
|
|
||||||
|
if editImageClass == 'rightColEdit':
|
||||||
|
htmlStr += ' </center>\n'
|
||||||
|
else:
|
||||||
|
if showHeaderImage:
|
||||||
|
htmlStr += ' <br>\n'
|
||||||
|
|
||||||
|
if (showPublishButton or editor or rssIconAtTop) and not showHeaderImage:
|
||||||
|
htmlStr += '</div><br>'
|
||||||
|
|
||||||
|
# show the newswire lines
|
||||||
|
newswireContentStr = \
|
||||||
|
htmlNewswire(baseDir, newswire, nickname, moderator, translate,
|
||||||
|
positiveVoting, iconsDir)
|
||||||
|
htmlStr += newswireContentStr
|
||||||
|
|
||||||
|
# show the rss icon at the bottom, typically on the right hand side
|
||||||
|
if newswireContentStr and not rssIconAtTop:
|
||||||
|
htmlStr += '<br><div class="columnIcons">' + rssIconStr + '</div>'
|
||||||
|
return htmlStr
|
||||||
|
|
||||||
|
|
||||||
|
def htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
|
||||||
|
translate: {}, positiveVoting: bool, iconsDir: str) -> str:
|
||||||
|
"""Converts a newswire dict into html
|
||||||
|
"""
|
||||||
|
separatorStr = htmlPostSeparator(baseDir, 'right')
|
||||||
|
htmlStr = ''
|
||||||
|
for dateStr, item in newswire.items():
|
||||||
|
publishedDate = \
|
||||||
|
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
|
||||||
|
dateShown = publishedDate.strftime("%Y-%m-%d %H:%M")
|
||||||
|
|
||||||
|
dateStrLink = dateStr.replace('T', ' ')
|
||||||
|
dateStrLink = dateStrLink.replace('Z', '')
|
||||||
|
moderatedItem = item[5]
|
||||||
|
htmlStr += separatorStr
|
||||||
|
if moderatedItem and 'vote:' + nickname in item[2]:
|
||||||
|
totalVotesStr = ''
|
||||||
|
totalVotes = 0
|
||||||
|
if moderator:
|
||||||
|
totalVotes = votesOnNewswireItem(item[2])
|
||||||
|
totalVotesStr = \
|
||||||
|
votesIndicator(totalVotes, positiveVoting)
|
||||||
|
|
||||||
|
title = removeLongWords(item[0], 16, []).replace('\n', '<br>')
|
||||||
|
htmlStr += '<p class="newswireItemVotedOn">' + \
|
||||||
|
'<a href="' + item[1] + '">' + \
|
||||||
|
'<span class="newswireItemVotedOn">' + title + \
|
||||||
|
'</span></a>' + totalVotesStr
|
||||||
|
if moderator:
|
||||||
|
htmlStr += \
|
||||||
|
' ' + dateShown + '<a href="/users/' + nickname + \
|
||||||
|
'/newswireunvote=' + dateStrLink + '" ' + \
|
||||||
|
'title="' + translate['Remove Vote'] + '">'
|
||||||
|
htmlStr += '<img loading="lazy" class="voteicon" src="/' + \
|
||||||
|
iconsDir + '/vote.png" /></a></p>\n'
|
||||||
|
else:
|
||||||
|
htmlStr += ' <span class="newswireDateVotedOn">'
|
||||||
|
htmlStr += dateShown + '</span></p>\n'
|
||||||
|
else:
|
||||||
|
totalVotesStr = ''
|
||||||
|
totalVotes = 0
|
||||||
|
if moderator:
|
||||||
|
if moderatedItem:
|
||||||
|
totalVotes = votesOnNewswireItem(item[2])
|
||||||
|
# show a number of ticks or crosses for how many
|
||||||
|
# votes for or against
|
||||||
|
totalVotesStr = \
|
||||||
|
votesIndicator(totalVotes, positiveVoting)
|
||||||
|
|
||||||
|
title = removeLongWords(item[0], 16, []).replace('\n', '<br>')
|
||||||
|
if moderator and moderatedItem:
|
||||||
|
htmlStr += '<p class="newswireItemModerated">' + \
|
||||||
|
'<a href="' + item[1] + '">' + \
|
||||||
|
title + '</a>' + totalVotesStr
|
||||||
|
htmlStr += ' ' + dateShown
|
||||||
|
htmlStr += '<a href="/users/' + nickname + \
|
||||||
|
'/newswirevote=' + dateStrLink + '" ' + \
|
||||||
|
'title="' + translate['Vote'] + '">'
|
||||||
|
htmlStr += '<img class="voteicon" src="/' + \
|
||||||
|
iconsDir + '/vote.png" /></a>'
|
||||||
|
htmlStr += '</p>\n'
|
||||||
|
else:
|
||||||
|
htmlStr += '<p class="newswireItem">' + \
|
||||||
|
'<a href="' + item[1] + '">' + \
|
||||||
|
title + '</a>' + \
|
||||||
|
totalVotesStr
|
||||||
|
htmlStr += ' <span class="newswireDate">'
|
||||||
|
htmlStr += dateShown + '</span></p>\n'
|
||||||
|
return htmlStr
|
||||||
|
|
||||||
|
|
||||||
|
def htmlCitations(baseDir: str, nickname: str, domain: str,
|
||||||
|
httpPrefix: str, defaultTimeline: str,
|
||||||
|
translate: {}, newswire: {}, cssCache: {},
|
||||||
|
blogTitle: str, blogContent: str,
|
||||||
|
blogImageFilename: str,
|
||||||
|
blogImageAttachmentMediaType: str,
|
||||||
|
blogImageDescription: str) -> str:
|
||||||
|
"""Show the citations screen when creating a blog
|
||||||
|
"""
|
||||||
|
htmlStr = ''
|
||||||
|
|
||||||
|
# create a list of dates for citations
|
||||||
|
# these can then be used to re-select checkboxes later
|
||||||
|
citationsFilename = \
|
||||||
|
baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/.citations.txt'
|
||||||
|
citationsSelected = []
|
||||||
|
if os.path.isfile(citationsFilename):
|
||||||
|
citationsSeparator = '#####'
|
||||||
|
with open(citationsFilename, "r") as f:
|
||||||
|
citations = f.readlines()
|
||||||
|
for line in citations:
|
||||||
|
if citationsSeparator not in line:
|
||||||
|
continue
|
||||||
|
sections = line.strip().split(citationsSeparator)
|
||||||
|
if len(sections) != 3:
|
||||||
|
continue
|
||||||
|
dateStr = sections[0]
|
||||||
|
citationsSelected.append(dateStr)
|
||||||
|
|
||||||
|
# the css filename
|
||||||
|
cssFilename = baseDir + '/epicyon-profile.css'
|
||||||
|
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||||
|
cssFilename = baseDir + '/epicyon.css'
|
||||||
|
|
||||||
|
profileStyle = getCSS(baseDir, cssFilename, cssCache)
|
||||||
|
if profileStyle:
|
||||||
|
# replace any https within the css with whatever prefix is needed
|
||||||
|
if httpPrefix != 'https':
|
||||||
|
profileStyle = \
|
||||||
|
profileStyle.replace('https://', httpPrefix + '://')
|
||||||
|
|
||||||
|
# iconsDir = getIconsDir(baseDir)
|
||||||
|
|
||||||
|
htmlStr = htmlHeader(cssFilename, profileStyle)
|
||||||
|
|
||||||
|
# top banner
|
||||||
|
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain)
|
||||||
|
htmlStr += \
|
||||||
|
'<a href="/users/' + nickname + '/newblog" title="' + \
|
||||||
|
translate['Go Back'] + '" alt="' + \
|
||||||
|
translate['Go Back'] + '">\n'
|
||||||
|
htmlStr += '<img loading="lazy" class="timeline-banner" src="' + \
|
||||||
|
'/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
||||||
|
|
||||||
|
htmlStr += \
|
||||||
|
'<form enctype="multipart/form-data" method="POST" ' + \
|
||||||
|
'accept-charset="UTF-8" action="/users/' + nickname + \
|
||||||
|
'/citationsdata">\n'
|
||||||
|
htmlStr += ' <center>\n'
|
||||||
|
htmlStr += translate['Choose newswire items ' +
|
||||||
|
'referenced in your article'] + '<br>'
|
||||||
|
if blogTitle is None:
|
||||||
|
blogTitle = ''
|
||||||
|
htmlStr += \
|
||||||
|
' <input type="hidden" name="blogTitle" value="' + \
|
||||||
|
blogTitle + '">\n'
|
||||||
|
if blogContent is None:
|
||||||
|
blogContent = ''
|
||||||
|
htmlStr += \
|
||||||
|
' <input type="hidden" name="blogContent" value="' + \
|
||||||
|
blogContent + '">\n'
|
||||||
|
# submit button
|
||||||
|
htmlStr += \
|
||||||
|
' <input type="submit" name="submitCitations" value="' + \
|
||||||
|
translate['Submit'] + '">\n'
|
||||||
|
htmlStr += ' </center>\n'
|
||||||
|
|
||||||
|
citationsSeparator = '#####'
|
||||||
|
|
||||||
|
# list of newswire items
|
||||||
|
if newswire:
|
||||||
|
ctr = 0
|
||||||
|
for dateStr, item in newswire.items():
|
||||||
|
# should this checkbox be selected?
|
||||||
|
selectedStr = ''
|
||||||
|
if dateStr in citationsSelected:
|
||||||
|
selectedStr = ' checked'
|
||||||
|
|
||||||
|
publishedDate = \
|
||||||
|
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
|
||||||
|
dateShown = publishedDate.strftime("%Y-%m-%d %H:%M")
|
||||||
|
|
||||||
|
title = removeLongWords(item[0], 16, []).replace('\n', '<br>')
|
||||||
|
link = item[1]
|
||||||
|
|
||||||
|
citationValue = \
|
||||||
|
dateStr + citationsSeparator + \
|
||||||
|
title + citationsSeparator + \
|
||||||
|
link
|
||||||
|
htmlStr += \
|
||||||
|
'<input type="checkbox" name="newswire' + str(ctr) + \
|
||||||
|
'" value="' + citationValue + '"' + selectedStr + '/>' + \
|
||||||
|
'<a href="' + link + '"><cite>' + title + '</cite></a> '
|
||||||
|
htmlStr += '<span class="newswireDate">' + \
|
||||||
|
dateShown + '</span><br>\n'
|
||||||
|
ctr += 1
|
||||||
|
|
||||||
|
htmlStr += '</form>\n'
|
||||||
|
return htmlStr + htmlFooter()
|
||||||
|
|
||||||
|
|
||||||
|
def htmlNewswireMobile(cssCache: {}, baseDir: str, nickname: str,
|
||||||
|
domain: str, domainFull: str,
|
||||||
|
httpPrefix: str, translate: {},
|
||||||
|
newswire: {},
|
||||||
|
positiveVoting: bool,
|
||||||
|
timelinePath: str,
|
||||||
|
showPublishAsIcon: bool,
|
||||||
|
authorized: bool,
|
||||||
|
rssIconAtTop: bool,
|
||||||
|
iconsAsButtons: bool,
|
||||||
|
defaultTimeline: str) -> str:
|
||||||
|
"""Shows the mobile version of the newswire right column
|
||||||
|
"""
|
||||||
|
htmlStr = ''
|
||||||
|
|
||||||
|
# the css filename
|
||||||
|
cssFilename = baseDir + '/epicyon-profile.css'
|
||||||
|
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||||
|
cssFilename = baseDir + '/epicyon.css'
|
||||||
|
|
||||||
|
profileStyle = getCSS(baseDir, cssFilename, cssCache)
|
||||||
|
if profileStyle:
|
||||||
|
# replace any https within the css with whatever prefix is needed
|
||||||
|
if httpPrefix != 'https':
|
||||||
|
profileStyle = \
|
||||||
|
profileStyle.replace('https://',
|
||||||
|
httpPrefix + '://')
|
||||||
|
|
||||||
|
iconsDir = getIconsDir(baseDir)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
showPublishButton = editor
|
||||||
|
|
||||||
|
htmlStr = htmlHeader(cssFilename, profileStyle)
|
||||||
|
|
||||||
|
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain)
|
||||||
|
htmlStr += \
|
||||||
|
'<a href="/users/' + nickname + '/' + defaultTimeline + '">' + \
|
||||||
|
'<img loading="lazy" class="timeline-banner" ' + \
|
||||||
|
'src="/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
||||||
|
|
||||||
|
htmlStr += '<center>' + \
|
||||||
|
headerButtonsFrontScreen(translate, nickname,
|
||||||
|
'newswire', authorized,
|
||||||
|
iconsAsButtons, iconsDir) + '</center>'
|
||||||
|
htmlStr += \
|
||||||
|
getRightColumnContent(baseDir, nickname, domainFull,
|
||||||
|
httpPrefix, translate,
|
||||||
|
iconsDir, moderator, editor,
|
||||||
|
newswire, positiveVoting,
|
||||||
|
False, timelinePath, showPublishButton,
|
||||||
|
showPublishAsIcon, rssIconAtTop, False,
|
||||||
|
authorized, False)
|
||||||
|
htmlStr += htmlFooter()
|
||||||
|
return htmlStr
|
||||||
|
|
||||||
|
|
||||||
|
def htmlEditNewswire(cssCache: {}, translate: {}, baseDir: str, path: str,
|
||||||
|
domain: str, port: int, httpPrefix: str,
|
||||||
|
defaultTimeline: str) -> str:
|
||||||
|
"""Shows the edit newswire 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 isModerator(baseDir, nickname):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
cssFilename = baseDir + '/epicyon-links.css'
|
||||||
|
if os.path.isfile(baseDir + '/links.css'):
|
||||||
|
cssFilename = baseDir + '/links.css'
|
||||||
|
|
||||||
|
editCSS = getCSS(baseDir, cssFilename, cssCache)
|
||||||
|
if editCSS:
|
||||||
|
if httpPrefix != 'https':
|
||||||
|
editCSS = \
|
||||||
|
editCSS.replace('https://', httpPrefix + '://')
|
||||||
|
|
||||||
|
# filename of the banner shown at the top
|
||||||
|
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain)
|
||||||
|
|
||||||
|
editNewswireForm = htmlHeader(cssFilename, editCSS)
|
||||||
|
|
||||||
|
# top banner
|
||||||
|
editNewswireForm += \
|
||||||
|
'<a href="/users/' + nickname + '/' + defaultTimeline + '" title="' + \
|
||||||
|
translate['Switch to timeline view'] + '" alt="' + \
|
||||||
|
translate['Switch to timeline view'] + '">\n'
|
||||||
|
editNewswireForm += '<img loading="lazy" class="timeline-banner" src="' + \
|
||||||
|
'/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
||||||
|
|
||||||
|
editNewswireForm += \
|
||||||
|
'<form enctype="multipart/form-data" method="POST" ' + \
|
||||||
|
'accept-charset="UTF-8" action="' + path + '/newswiredata">\n'
|
||||||
|
editNewswireForm += \
|
||||||
|
' <div class="vertical-center">\n'
|
||||||
|
editNewswireForm += \
|
||||||
|
' <p class="new-post-text">' + translate['Edit newswire'] + '</p>'
|
||||||
|
editNewswireForm += \
|
||||||
|
' <div class="container">\n'
|
||||||
|
# editNewswireForm += \
|
||||||
|
# ' <a href="' + pathOriginal + '"><button class="cancelbtn">' + \
|
||||||
|
# translate['Go Back'] + '</button></a>\n'
|
||||||
|
editNewswireForm += \
|
||||||
|
' <center>\n' + \
|
||||||
|
' <input type="submit" name="submitNewswire" value="' + \
|
||||||
|
translate['Submit'] + '">\n' + \
|
||||||
|
' </center>\n'
|
||||||
|
editNewswireForm += \
|
||||||
|
' </div>\n'
|
||||||
|
|
||||||
|
newswireFilename = baseDir + '/accounts/newswire.txt'
|
||||||
|
newswireStr = ''
|
||||||
|
if os.path.isfile(newswireFilename):
|
||||||
|
with open(newswireFilename, 'r') as fp:
|
||||||
|
newswireStr = fp.read()
|
||||||
|
|
||||||
|
editNewswireForm += \
|
||||||
|
'<div class="container">'
|
||||||
|
|
||||||
|
editNewswireForm += \
|
||||||
|
' ' + \
|
||||||
|
translate['Add RSS feed links below.'] + \
|
||||||
|
'<br>'
|
||||||
|
editNewswireForm += \
|
||||||
|
' <textarea id="message" name="editedNewswire" ' + \
|
||||||
|
'style="height:80vh">' + newswireStr + '</textarea>'
|
||||||
|
|
||||||
|
filterStr = ''
|
||||||
|
filterFilename = \
|
||||||
|
baseDir + '/accounts/news@' + domain + '/filters.txt'
|
||||||
|
if os.path.isfile(filterFilename):
|
||||||
|
with open(filterFilename, 'r') as filterfile:
|
||||||
|
filterStr = filterfile.read()
|
||||||
|
|
||||||
|
editNewswireForm += \
|
||||||
|
' <br><b><label class="labels">' + \
|
||||||
|
translate['Filtered words'] + '</label></b>\n'
|
||||||
|
editNewswireForm += ' <br><label class="labels">' + \
|
||||||
|
translate['One per line'] + '</label>'
|
||||||
|
editNewswireForm += ' <textarea id="message" ' + \
|
||||||
|
'name="filteredWordsNewswire" style="height:50vh">' + \
|
||||||
|
filterStr + '</textarea>\n'
|
||||||
|
|
||||||
|
hashtagRulesStr = ''
|
||||||
|
hashtagRulesFilename = \
|
||||||
|
baseDir + '/accounts/hashtagrules.txt'
|
||||||
|
if os.path.isfile(hashtagRulesFilename):
|
||||||
|
with open(hashtagRulesFilename, 'r') as rulesfile:
|
||||||
|
hashtagRulesStr = rulesfile.read()
|
||||||
|
|
||||||
|
editNewswireForm += \
|
||||||
|
' <br><b><label class="labels">' + \
|
||||||
|
translate['News tagging rules'] + '</label></b>\n'
|
||||||
|
editNewswireForm += ' <br><label class="labels">' + \
|
||||||
|
translate['One per line'] + '.</label>\n'
|
||||||
|
editNewswireForm += \
|
||||||
|
' <a href="' + \
|
||||||
|
'https://gitlab.com/bashrc2/epicyon/-/raw/main/hashtagrules.txt' + \
|
||||||
|
'">' + translate['See instructions'] + '</a>\n'
|
||||||
|
editNewswireForm += ' <textarea id="message" ' + \
|
||||||
|
'name="hashtagRulesList" style="height:80vh">' + \
|
||||||
|
hashtagRulesStr + '</textarea>\n'
|
||||||
|
|
||||||
|
editNewswireForm += \
|
||||||
|
'</div>'
|
||||||
|
|
||||||
|
editNewswireForm += htmlFooter()
|
||||||
|
return editNewswireForm
|
|
@ -0,0 +1,251 @@
|
||||||
|
__filename__ = "webapp_person_options.py"
|
||||||
|
__author__ = "Bob Mottram"
|
||||||
|
__license__ = "AGPL3+"
|
||||||
|
__version__ = "1.1.0"
|
||||||
|
__maintainer__ = "Bob Mottram"
|
||||||
|
__email__ = "bob@freedombone.net"
|
||||||
|
__status__ = "Production"
|
||||||
|
|
||||||
|
import os
|
||||||
|
from shutil import copyfile
|
||||||
|
from petnames import getPetName
|
||||||
|
from person import isPersonSnoozed
|
||||||
|
from posts import isModerator
|
||||||
|
from utils import getDomainFromActor
|
||||||
|
from utils import getNicknameFromActor
|
||||||
|
from utils import getCSS
|
||||||
|
from blocking import isBlocked
|
||||||
|
from follow import isFollowingActor
|
||||||
|
from followingCalendar import receivingCalendarEvents
|
||||||
|
from webapp_utils import htmlHeader
|
||||||
|
from webapp_utils import htmlFooter
|
||||||
|
|
||||||
|
|
||||||
|
def htmlPersonOptions(cssCache: {}, translate: {}, baseDir: str,
|
||||||
|
domain: str, domainFull: str,
|
||||||
|
originPathStr: str,
|
||||||
|
optionsActor: str,
|
||||||
|
optionsProfileUrl: str,
|
||||||
|
optionsLink: str,
|
||||||
|
pageNumber: int,
|
||||||
|
donateUrl: str,
|
||||||
|
xmppAddress: str,
|
||||||
|
matrixAddress: str,
|
||||||
|
ssbAddress: str,
|
||||||
|
blogAddress: str,
|
||||||
|
toxAddress: str,
|
||||||
|
PGPpubKey: str,
|
||||||
|
PGPfingerprint: str,
|
||||||
|
emailAddress) -> str:
|
||||||
|
"""Show options for a person: view/follow/block/report
|
||||||
|
"""
|
||||||
|
optionsDomain, optionsPort = getDomainFromActor(optionsActor)
|
||||||
|
optionsDomainFull = optionsDomain
|
||||||
|
if optionsPort:
|
||||||
|
if optionsPort != 80 and optionsPort != 443:
|
||||||
|
optionsDomainFull = optionsDomain + ':' + str(optionsPort)
|
||||||
|
|
||||||
|
if os.path.isfile(baseDir + '/accounts/options-background-custom.jpg'):
|
||||||
|
if not os.path.isfile(baseDir + '/accounts/options-background.jpg'):
|
||||||
|
copyfile(baseDir + '/accounts/options-background.jpg',
|
||||||
|
baseDir + '/accounts/options-background.jpg')
|
||||||
|
|
||||||
|
followStr = 'Follow'
|
||||||
|
blockStr = 'Block'
|
||||||
|
nickname = None
|
||||||
|
optionsNickname = None
|
||||||
|
if originPathStr.startswith('/users/'):
|
||||||
|
nickname = originPathStr.split('/users/')[1]
|
||||||
|
if '/' in nickname:
|
||||||
|
nickname = nickname.split('/')[0]
|
||||||
|
if '?' in nickname:
|
||||||
|
nickname = nickname.split('?')[0]
|
||||||
|
followerDomain, followerPort = getDomainFromActor(optionsActor)
|
||||||
|
if isFollowingActor(baseDir, nickname, domain, optionsActor):
|
||||||
|
followStr = 'Unfollow'
|
||||||
|
|
||||||
|
optionsNickname = getNicknameFromActor(optionsActor)
|
||||||
|
optionsDomainFull = optionsDomain
|
||||||
|
if optionsPort:
|
||||||
|
if optionsPort != 80 and optionsPort != 443:
|
||||||
|
optionsDomainFull = optionsDomain + ':' + str(optionsPort)
|
||||||
|
if isBlocked(baseDir, nickname, domain,
|
||||||
|
optionsNickname, optionsDomainFull):
|
||||||
|
blockStr = 'Block'
|
||||||
|
|
||||||
|
optionsLinkStr = ''
|
||||||
|
if optionsLink:
|
||||||
|
optionsLinkStr = \
|
||||||
|
' <input type="hidden" name="postUrl" value="' + \
|
||||||
|
optionsLink + '">\n'
|
||||||
|
cssFilename = baseDir + '/epicyon-options.css'
|
||||||
|
if os.path.isfile(baseDir + '/options.css'):
|
||||||
|
cssFilename = baseDir + '/options.css'
|
||||||
|
|
||||||
|
profileStyle = getCSS(baseDir, cssFilename, cssCache)
|
||||||
|
if profileStyle:
|
||||||
|
profileStyle = \
|
||||||
|
profileStyle.replace('--follow-text-entry-width: 90%;',
|
||||||
|
'--follow-text-entry-width: 20%;')
|
||||||
|
|
||||||
|
if not os.path.isfile(baseDir + '/accounts/' +
|
||||||
|
'options-background.jpg'):
|
||||||
|
profileStyle = \
|
||||||
|
profileStyle.replace('background-image: ' +
|
||||||
|
'url("options-background.jpg");',
|
||||||
|
'background-image: none;')
|
||||||
|
|
||||||
|
# To snooze, or not to snooze? That is the question
|
||||||
|
snoozeButtonStr = 'Snooze'
|
||||||
|
if nickname:
|
||||||
|
if isPersonSnoozed(baseDir, nickname, domain, optionsActor):
|
||||||
|
snoozeButtonStr = 'Unsnooze'
|
||||||
|
|
||||||
|
donateStr = ''
|
||||||
|
if donateUrl:
|
||||||
|
donateStr = \
|
||||||
|
' <a href="' + donateUrl + \
|
||||||
|
'"><button class="button" name="submitDonate">' + \
|
||||||
|
translate['Donate'] + '</button></a>\n'
|
||||||
|
|
||||||
|
optionsStr = htmlHeader(cssFilename, profileStyle)
|
||||||
|
optionsStr += '<br><br>\n'
|
||||||
|
optionsStr += '<div class="options">\n'
|
||||||
|
optionsStr += ' <div class="optionsAvatar">\n'
|
||||||
|
optionsStr += ' <center>\n'
|
||||||
|
optionsStr += ' <a href="' + optionsActor + '">\n'
|
||||||
|
optionsStr += ' <img loading="lazy" src="' + optionsProfileUrl + \
|
||||||
|
'"/></a>\n'
|
||||||
|
handle = getNicknameFromActor(optionsActor) + '@' + optionsDomain
|
||||||
|
optionsStr += \
|
||||||
|
' <p class="optionsText">' + translate['Options for'] + \
|
||||||
|
' @' + handle + '</p>\n'
|
||||||
|
if emailAddress:
|
||||||
|
optionsStr += \
|
||||||
|
'<p class="imText">' + translate['Email'] + \
|
||||||
|
': <a href="mailto:' + \
|
||||||
|
emailAddress + '">' + emailAddress + '</a></p>\n'
|
||||||
|
if xmppAddress:
|
||||||
|
optionsStr += \
|
||||||
|
'<p class="imText">' + translate['XMPP'] + \
|
||||||
|
': <a href="xmpp:' + xmppAddress + '">' + \
|
||||||
|
xmppAddress + '</a></p>\n'
|
||||||
|
if matrixAddress:
|
||||||
|
optionsStr += \
|
||||||
|
'<p class="imText">' + translate['Matrix'] + ': ' + \
|
||||||
|
matrixAddress + '</p>\n'
|
||||||
|
if ssbAddress:
|
||||||
|
optionsStr += \
|
||||||
|
'<p class="imText">SSB: ' + ssbAddress + '</p>\n'
|
||||||
|
if blogAddress:
|
||||||
|
optionsStr += \
|
||||||
|
'<p class="imText">Blog: <a href="' + blogAddress + '">' + \
|
||||||
|
blogAddress + '</a></p>\n'
|
||||||
|
if toxAddress:
|
||||||
|
optionsStr += \
|
||||||
|
'<p class="imText">Tox: ' + toxAddress + '</p>\n'
|
||||||
|
if PGPfingerprint:
|
||||||
|
optionsStr += '<p class="pgp">PGP: ' + \
|
||||||
|
PGPfingerprint.replace('\n', '<br>') + '</p>\n'
|
||||||
|
if PGPpubKey:
|
||||||
|
optionsStr += '<p class="pgp">' + \
|
||||||
|
PGPpubKey.replace('\n', '<br>') + '</p>\n'
|
||||||
|
optionsStr += ' <form method="POST" action="' + \
|
||||||
|
originPathStr + '/personoptions">\n'
|
||||||
|
optionsStr += ' <input type="hidden" name="pageNumber" value="' + \
|
||||||
|
str(pageNumber) + '">\n'
|
||||||
|
optionsStr += ' <input type="hidden" name="actor" value="' + \
|
||||||
|
optionsActor + '">\n'
|
||||||
|
optionsStr += ' <input type="hidden" name="avatarUrl" value="' + \
|
||||||
|
optionsProfileUrl + '">\n'
|
||||||
|
if optionsNickname:
|
||||||
|
handle = optionsNickname + '@' + optionsDomainFull
|
||||||
|
petname = getPetName(baseDir, nickname, domain, handle)
|
||||||
|
optionsStr += \
|
||||||
|
' ' + translate['Petname'] + ': \n' + \
|
||||||
|
' <input type="text" name="optionpetname" value="' + \
|
||||||
|
petname + '">\n' \
|
||||||
|
' <button type="submit" class="buttonsmall" ' + \
|
||||||
|
'name="submitPetname">' + \
|
||||||
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
|
||||||
|
# checkbox for receiving calendar events
|
||||||
|
if isFollowingActor(baseDir, nickname, domain, optionsActor):
|
||||||
|
checkboxStr = \
|
||||||
|
' <input type="checkbox" ' + \
|
||||||
|
'class="profilecheckbox" name="onCalendar" checked> ' + \
|
||||||
|
translate['Receive calendar events from this account'] + \
|
||||||
|
'\n <button type="submit" class="buttonsmall" ' + \
|
||||||
|
'name="submitOnCalendar">' + \
|
||||||
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
if not receivingCalendarEvents(baseDir, nickname, domain,
|
||||||
|
optionsNickname, optionsDomainFull):
|
||||||
|
checkboxStr = checkboxStr.replace(' checked>', '>')
|
||||||
|
optionsStr += checkboxStr
|
||||||
|
|
||||||
|
# checkbox for permission to post to newswire
|
||||||
|
if optionsDomainFull == domainFull:
|
||||||
|
if isModerator(baseDir, nickname) and \
|
||||||
|
not isModerator(baseDir, optionsNickname):
|
||||||
|
newswireBlockedFilename = \
|
||||||
|
baseDir + '/accounts/' + \
|
||||||
|
optionsNickname + '@' + optionsDomain + '/.nonewswire'
|
||||||
|
checkboxStr = \
|
||||||
|
' <input type="checkbox" ' + \
|
||||||
|
'class="profilecheckbox" name="postsToNews" checked> ' + \
|
||||||
|
translate['Allow news posts'] + \
|
||||||
|
'\n <button type="submit" class="buttonsmall" ' + \
|
||||||
|
'name="submitPostToNews">' + \
|
||||||
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
if os.path.isfile(newswireBlockedFilename):
|
||||||
|
checkboxStr = checkboxStr.replace(' checked>', '>')
|
||||||
|
optionsStr += checkboxStr
|
||||||
|
|
||||||
|
optionsStr += optionsLinkStr
|
||||||
|
optionsStr += \
|
||||||
|
' <a href="/"><button type="button" class="buttonIcon" ' + \
|
||||||
|
'name="submitBack">' + translate['Go Back'] + '</button></a>'
|
||||||
|
optionsStr += \
|
||||||
|
' <button type="submit" class="button" name="submitView">' + \
|
||||||
|
translate['View'] + '</button>'
|
||||||
|
optionsStr += donateStr
|
||||||
|
optionsStr += \
|
||||||
|
' <button type="submit" class="button" name="submit' + \
|
||||||
|
followStr + '">' + translate[followStr] + '</button>'
|
||||||
|
optionsStr += \
|
||||||
|
' <button type="submit" class="button" name="submit' + \
|
||||||
|
blockStr + '">' + translate[blockStr] + '</button>'
|
||||||
|
optionsStr += \
|
||||||
|
' <button type="submit" class="button" name="submitDM">' + \
|
||||||
|
translate['DM'] + '</button>'
|
||||||
|
optionsStr += \
|
||||||
|
' <button type="submit" class="button" name="submit' + \
|
||||||
|
snoozeButtonStr + '">' + translate[snoozeButtonStr] + '</button>'
|
||||||
|
optionsStr += \
|
||||||
|
' <button type="submit" class="button" name="submitReport">' + \
|
||||||
|
translate['Report'] + '</button>'
|
||||||
|
|
||||||
|
personNotes = ''
|
||||||
|
personNotesFilename = \
|
||||||
|
baseDir + '/accounts/' + nickname + '@' + domain + \
|
||||||
|
'/notes/' + handle + '.txt'
|
||||||
|
if os.path.isfile(personNotesFilename):
|
||||||
|
with open(personNotesFilename, 'r') as fp:
|
||||||
|
personNotes = fp.read()
|
||||||
|
|
||||||
|
optionsStr += \
|
||||||
|
' <br><br>' + translate['Notes'] + ': \n'
|
||||||
|
optionsStr += ' <button type="submit" class="buttonsmall" ' + \
|
||||||
|
'name="submitPersonNotes">' + \
|
||||||
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
optionsStr += \
|
||||||
|
' <textarea id="message" ' + \
|
||||||
|
'name="optionnotes" style="height:400px">' + \
|
||||||
|
personNotes + '</textarea>\n'
|
||||||
|
|
||||||
|
optionsStr += ' </form>\n'
|
||||||
|
optionsStr += '</center>\n'
|
||||||
|
optionsStr += '</div>\n'
|
||||||
|
optionsStr += '</div>\n'
|
||||||
|
optionsStr += htmlFooter()
|
||||||
|
return optionsStr
|
167
webapp_post.py
167
webapp_post.py
|
@ -16,11 +16,16 @@ from cache import getPersonFromCache
|
||||||
from bookmarks import bookmarkedByPerson
|
from bookmarks import bookmarkedByPerson
|
||||||
from like import likedByPerson
|
from like import likedByPerson
|
||||||
from like import noOfLikes
|
from like import noOfLikes
|
||||||
|
from follow import isFollowingActor
|
||||||
from posts import isEditor
|
from posts import isEditor
|
||||||
from posts import postIsMuted
|
from posts import postIsMuted
|
||||||
from posts import getPersonBox
|
from posts import getPersonBox
|
||||||
from posts import isDM
|
from posts import isDM
|
||||||
from posts import downloadAnnounce
|
from posts import downloadAnnounce
|
||||||
|
from posts import populateRepliesJson
|
||||||
|
from utils import locatePost
|
||||||
|
from utils import loadJson
|
||||||
|
from utils import getCSS
|
||||||
from utils import getCachedPostDirectory
|
from utils import getCachedPostDirectory
|
||||||
from utils import getCachedPostFilename
|
from utils import getCachedPostFilename
|
||||||
from utils import getProtocolPrefixes
|
from utils import getProtocolPrefixes
|
||||||
|
@ -49,6 +54,9 @@ from webapp_utils import addEmojiToDisplayName
|
||||||
from webapp_utils import postContainsPublic
|
from webapp_utils import postContainsPublic
|
||||||
from webapp_utils import getContentWarningButton
|
from webapp_utils import getContentWarningButton
|
||||||
from webapp_utils import getPostAttachmentsAsHtml
|
from webapp_utils import getPostAttachmentsAsHtml
|
||||||
|
from webapp_utils import getIconsDir
|
||||||
|
from webapp_utils import htmlHeader
|
||||||
|
from webapp_utils import htmlFooter
|
||||||
from webapp_media import addEmbeddedElements
|
from webapp_media import addEmbeddedElements
|
||||||
from webapp_question import insertQuestion
|
from webapp_question import insertQuestion
|
||||||
from devices import E2EEdecryptMessageFromDevice
|
from devices import E2EEdecryptMessageFromDevice
|
||||||
|
@ -1215,3 +1223,162 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
print('TIMING INDIV ' + boxName + ' 19 = ' + str(timeDiff))
|
print('TIMING INDIV ' + boxName + ' 19 = ' + str(timeDiff))
|
||||||
|
|
||||||
return postHtml
|
return postHtml
|
||||||
|
|
||||||
|
|
||||||
|
def htmlIndividualPost(cssCache: {},
|
||||||
|
recentPostsCache: {}, maxRecentPosts: int,
|
||||||
|
translate: {},
|
||||||
|
baseDir: str, session, wfRequest: {}, personCache: {},
|
||||||
|
nickname: str, domain: str, port: int, authorized: bool,
|
||||||
|
postJsonObject: {}, httpPrefix: str,
|
||||||
|
projectVersion: str, likedBy: str,
|
||||||
|
YTReplacementDomain: str,
|
||||||
|
showPublishedDateOnly: bool) -> str:
|
||||||
|
"""Show an individual post as html
|
||||||
|
"""
|
||||||
|
iconsDir = getIconsDir(baseDir)
|
||||||
|
postStr = ''
|
||||||
|
if likedBy:
|
||||||
|
likedByNickname = getNicknameFromActor(likedBy)
|
||||||
|
likedByDomain, likedByPort = getDomainFromActor(likedBy)
|
||||||
|
if likedByPort:
|
||||||
|
if likedByPort != 80 and likedByPort != 443:
|
||||||
|
likedByDomain += ':' + str(likedByPort)
|
||||||
|
likedByHandle = likedByNickname + '@' + likedByDomain
|
||||||
|
postStr += \
|
||||||
|
'<p>' + translate['Liked by'] + \
|
||||||
|
' <a href="' + likedBy + '">@' + \
|
||||||
|
likedByHandle + '</a>\n'
|
||||||
|
|
||||||
|
domainFull = domain
|
||||||
|
if port:
|
||||||
|
if port != 80 and port != 443:
|
||||||
|
domainFull = domain + ':' + str(port)
|
||||||
|
actor = '/users/' + nickname
|
||||||
|
followStr = ' <form method="POST" ' + \
|
||||||
|
'accept-charset="UTF-8" action="' + actor + '/searchhandle">\n'
|
||||||
|
followStr += \
|
||||||
|
' <input type="hidden" name="actor" value="' + actor + '">\n'
|
||||||
|
followStr += \
|
||||||
|
' <input type="hidden" name="searchtext" value="' + \
|
||||||
|
likedByHandle + '">\n'
|
||||||
|
if not isFollowingActor(baseDir, nickname, domainFull, likedBy):
|
||||||
|
followStr += ' <button type="submit" class="button" ' + \
|
||||||
|
'name="submitSearch">' + translate['Follow'] + '</button>\n'
|
||||||
|
followStr += ' <button type="submit" class="button" ' + \
|
||||||
|
'name="submitBack">' + translate['Go Back'] + '</button>\n'
|
||||||
|
followStr += ' </form>\n'
|
||||||
|
postStr += followStr + '</p>\n'
|
||||||
|
|
||||||
|
postStr += \
|
||||||
|
individualPostAsHtml(True, recentPostsCache, maxRecentPosts,
|
||||||
|
iconsDir, translate, None,
|
||||||
|
baseDir, session, wfRequest, personCache,
|
||||||
|
nickname, domain, port, postJsonObject,
|
||||||
|
None, True, False,
|
||||||
|
httpPrefix, projectVersion, 'inbox',
|
||||||
|
YTReplacementDomain,
|
||||||
|
showPublishedDateOnly,
|
||||||
|
False, authorized, False, False, False)
|
||||||
|
messageId = removeIdEnding(postJsonObject['id'])
|
||||||
|
|
||||||
|
# show the previous posts
|
||||||
|
if isinstance(postJsonObject['object'], dict):
|
||||||
|
while postJsonObject['object'].get('inReplyTo'):
|
||||||
|
postFilename = \
|
||||||
|
locatePost(baseDir, nickname, domain,
|
||||||
|
postJsonObject['object']['inReplyTo'])
|
||||||
|
if not postFilename:
|
||||||
|
break
|
||||||
|
postJsonObject = loadJson(postFilename)
|
||||||
|
if postJsonObject:
|
||||||
|
postStr = \
|
||||||
|
individualPostAsHtml(True, recentPostsCache,
|
||||||
|
maxRecentPosts,
|
||||||
|
iconsDir, translate, None,
|
||||||
|
baseDir, session, wfRequest,
|
||||||
|
personCache,
|
||||||
|
nickname, domain, port,
|
||||||
|
postJsonObject,
|
||||||
|
None, True, False,
|
||||||
|
httpPrefix, projectVersion, 'inbox',
|
||||||
|
YTReplacementDomain,
|
||||||
|
showPublishedDateOnly,
|
||||||
|
False, authorized,
|
||||||
|
False, False, False) + postStr
|
||||||
|
|
||||||
|
# show the following posts
|
||||||
|
postFilename = locatePost(baseDir, nickname, domain, messageId)
|
||||||
|
if postFilename:
|
||||||
|
# is there a replies file for this post?
|
||||||
|
repliesFilename = postFilename.replace('.json', '.replies')
|
||||||
|
if os.path.isfile(repliesFilename):
|
||||||
|
# get items from the replies file
|
||||||
|
repliesJson = {
|
||||||
|
'orderedItems': []
|
||||||
|
}
|
||||||
|
populateRepliesJson(baseDir, nickname, domain,
|
||||||
|
repliesFilename, authorized, repliesJson)
|
||||||
|
# add items to the html output
|
||||||
|
for item in repliesJson['orderedItems']:
|
||||||
|
postStr += \
|
||||||
|
individualPostAsHtml(True, recentPostsCache,
|
||||||
|
maxRecentPosts,
|
||||||
|
iconsDir, translate, None,
|
||||||
|
baseDir, session, wfRequest,
|
||||||
|
personCache,
|
||||||
|
nickname, domain, port, item,
|
||||||
|
None, True, False,
|
||||||
|
httpPrefix, projectVersion, 'inbox',
|
||||||
|
YTReplacementDomain,
|
||||||
|
showPublishedDateOnly,
|
||||||
|
False, authorized,
|
||||||
|
False, False, False)
|
||||||
|
cssFilename = baseDir + '/epicyon-profile.css'
|
||||||
|
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||||
|
cssFilename = baseDir + '/epicyon.css'
|
||||||
|
|
||||||
|
postsCSS = getCSS(baseDir, cssFilename, cssCache)
|
||||||
|
if postsCSS:
|
||||||
|
if httpPrefix != 'https':
|
||||||
|
postsCSS = postsCSS.replace('https://',
|
||||||
|
httpPrefix + '://')
|
||||||
|
return htmlHeader(cssFilename, postsCSS) + postStr + htmlFooter()
|
||||||
|
|
||||||
|
|
||||||
|
def htmlPostReplies(cssCache: {},
|
||||||
|
recentPostsCache: {}, maxRecentPosts: int,
|
||||||
|
translate: {}, baseDir: str,
|
||||||
|
session, wfRequest: {}, personCache: {},
|
||||||
|
nickname: str, domain: str, port: int, repliesJson: {},
|
||||||
|
httpPrefix: str, projectVersion: str,
|
||||||
|
YTReplacementDomain: str,
|
||||||
|
showPublishedDateOnly: bool) -> str:
|
||||||
|
"""Show the replies to an individual post as html
|
||||||
|
"""
|
||||||
|
iconsDir = getIconsDir(baseDir)
|
||||||
|
repliesStr = ''
|
||||||
|
if repliesJson.get('orderedItems'):
|
||||||
|
for item in repliesJson['orderedItems']:
|
||||||
|
repliesStr += \
|
||||||
|
individualPostAsHtml(True, recentPostsCache,
|
||||||
|
maxRecentPosts,
|
||||||
|
iconsDir, translate, None,
|
||||||
|
baseDir, session, wfRequest, personCache,
|
||||||
|
nickname, domain, port, item,
|
||||||
|
None, True, False,
|
||||||
|
httpPrefix, projectVersion, 'inbox',
|
||||||
|
YTReplacementDomain,
|
||||||
|
showPublishedDateOnly,
|
||||||
|
False, False, False, False, False)
|
||||||
|
|
||||||
|
cssFilename = baseDir + '/epicyon-profile.css'
|
||||||
|
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||||
|
cssFilename = baseDir + '/epicyon.css'
|
||||||
|
|
||||||
|
postsCSS = getCSS(baseDir, cssFilename, cssCache)
|
||||||
|
if postsCSS:
|
||||||
|
if httpPrefix != 'https':
|
||||||
|
postsCSS = postsCSS.replace('https://',
|
||||||
|
httpPrefix + '://')
|
||||||
|
return htmlHeader(cssFilename, postsCSS) + repliesStr + htmlFooter()
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -740,3 +740,73 @@ def htmlPostSeparator(baseDir: str, column: str) -> str:
|
||||||
'<img src="/' + iconsDir + '/' + filename + '"/>' + \
|
'<img src="/' + iconsDir + '/' + filename + '"/>' + \
|
||||||
'</center></div>\n'
|
'</center></div>\n'
|
||||||
return separatorStr
|
return separatorStr
|
||||||
|
|
||||||
|
|
||||||
|
def headerButtonsFrontScreen(translate: {},
|
||||||
|
nickname: str, boxName: str,
|
||||||
|
authorized: bool,
|
||||||
|
iconsAsButtons: bool,
|
||||||
|
iconsDir: bool) -> str:
|
||||||
|
"""Returns the header buttons for the front page of a news instance
|
||||||
|
"""
|
||||||
|
headerStr = ''
|
||||||
|
if nickname == 'news':
|
||||||
|
buttonFeatures = 'buttonMobile'
|
||||||
|
buttonNewswire = 'buttonMobile'
|
||||||
|
buttonLinks = 'buttonMobile'
|
||||||
|
if boxName == 'features':
|
||||||
|
buttonFeatures = 'buttonselected'
|
||||||
|
elif boxName == 'newswire':
|
||||||
|
buttonNewswire = 'buttonselected'
|
||||||
|
elif boxName == 'links':
|
||||||
|
buttonLinks = 'buttonselected'
|
||||||
|
|
||||||
|
headerStr += \
|
||||||
|
' <a href="/">' + \
|
||||||
|
'<button class="' + buttonFeatures + '">' + \
|
||||||
|
'<span>' + translate['Features'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
if not authorized:
|
||||||
|
headerStr += \
|
||||||
|
' <a href="/login">' + \
|
||||||
|
'<button class="buttonMobile">' + \
|
||||||
|
'<span>' + translate['Login'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
if iconsAsButtons:
|
||||||
|
headerStr += \
|
||||||
|
' <a href="/users/news/newswiremobile">' + \
|
||||||
|
'<button class="' + buttonNewswire + '">' + \
|
||||||
|
'<span>' + translate['Newswire'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
headerStr += \
|
||||||
|
' <a href="/users/news/linksmobile">' + \
|
||||||
|
'<button class="' + buttonLinks + '">' + \
|
||||||
|
'<span>' + translate['Links'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
else:
|
||||||
|
headerStr += \
|
||||||
|
' <a href="' + \
|
||||||
|
'/users/news/newswiremobile">' + \
|
||||||
|
'<img loading="lazy" src="/' + iconsDir + \
|
||||||
|
'/newswire.png" title="' + translate['Newswire'] + \
|
||||||
|
'" alt="| ' + translate['Newswire'] + '"/></a>\n'
|
||||||
|
headerStr += \
|
||||||
|
' <a href="' + \
|
||||||
|
'/users/news/linksmobile">' + \
|
||||||
|
'<img loading="lazy" src="/' + iconsDir + \
|
||||||
|
'/links.png" title="' + translate['Links'] + \
|
||||||
|
'" alt="| ' + translate['Links'] + '"/></a>\n'
|
||||||
|
else:
|
||||||
|
if not authorized:
|
||||||
|
headerStr += \
|
||||||
|
' <a href="/login">' + \
|
||||||
|
'<button class="buttonMobile">' + \
|
||||||
|
'<span>' + translate['Login'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
if headerStr:
|
||||||
|
headerStr = \
|
||||||
|
'\n <div class="frontPageMobileButtons">\n' + \
|
||||||
|
headerStr + \
|
||||||
|
' </div>\n'
|
||||||
|
return headerStr
|
||||||
|
|
Loading…
Reference in New Issue