__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 content import removeLongWords
from utils import removeHtml
from utils import locatePost
from utils import loadJson
from utils import votesOnNewswireItem
from utils import getNicknameFromActor
from utils import isEditor
from posts import isModerator
from webapp_utils import getRightImageFile
from webapp_utils import getImageFile
from webapp_utils import htmlHeaderWithExternalStyle, htmlHeaderWithExternalStyles
from webapp_utils import htmlFooter
from webapp_utils import getBannerFile
from webapp_utils import htmlPostSeparator
from webapp_utils import headerButtonsFrontScreen
from webapp_utils import getIconsWebPath
from webapp_headerbuttons import headerNewsTabs
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: {},
moderator: bool, editor: bool,
newswire: {}, positiveVoting: bool,
showBackButton: bool, timelinePath: str,
showPublishButton: bool,
showPublishAsIcon: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool,
showHeaderImage: bool,
theme: str) -> 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 = \
' ' + \
'\n'
else:
# if not logged in then replace the publish button with
# a login button
publishButtonStr = \
' \n'
# show publish button at the top if needed
if publishButtonAtTop:
htmlStr += '
' + publishButtonStr + '
'
editImageClass = ''
# show a column header, eg. title of the theme or newswire banner
htmlStr += '
Newswire
\n'
if (showPublishButton or editor or rssIconAtTop):
htmlStr += '
'
if editImageClass == 'rightColEdit':
htmlStr += '\n
\n'
# whether to show a back icon
# This is probably going to be osolete soon
if showBackButton:
htmlStr += \
' ' + \
'\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 += \
' ' + \
'\n'
else:
# show the edit icon
htmlStr += \
' ' + \
'\n'
# show the RSS icons
rssIconStr = \
' ' + \
'\n'
rssIconStr += \
' ' + \
'\n'
if rssIconAtTop:
htmlStr += rssIconStr
# show publish icon at top
if showPublishButton:
if showPublishAsIcon:
htmlStr += \
' ' + \
'\n'
if editImageClass == 'rightColEdit':
htmlStr += '
\n'
if (showPublishButton or editor or rssIconAtTop):
htmlStr += '
'
# show the newswire lines
newswireContentStr = \
_htmlNewswire(baseDir, newswire, nickname, moderator, translate,
positiveVoting)
htmlStr += newswireContentStr
# show the rss icon at the bottom, typically on the right hand side
if newswireContentStr and not rssIconAtTop:
htmlStr += '
' + rssIconStr + '
'
return htmlStr
def _getBrokenFavSubstitute() -> str:
"""Substitute link used if a favicon is not available
"""
return " onerror=\"this.onerror=null; this.style='width:0%'; this.src=''\""
def _getNewswireFavicon(url: str) -> str:
"""Returns a favicon url from the given article link
"""
if '://' not in url:
return None
domain = url.split('://')[1]
if '/' not in domain:
return url + '/favicon.ico'
else:
domain = domain.split('/')[0]
return url.split('://')[0] + '://' + domain + '/favicon.ico'
def _htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
translate: {}, positiveVoting: bool) -> str:
"""Converts a newswire dict into html
"""
separatorStr = htmlPostSeparator(baseDir, 'right')
htmlStr = ''
for dateStr, item in newswire.items():
item[0] = removeHtml(item[0]).strip()
if not item[0]:
continue
# remove any CDATA
if 'CDATA[' in item[0]:
item[0] = item[0].split('CDATA[')[1]
if ']' in item[0]:
item[0] = item[0].split(']')[0]
try:
publishedDate = \
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
except BaseException:
print('WARN: bad date format ' + dateStr)
continue
dateShown = publishedDate.strftime("%Y-%m-%d %H:%M")
dateStrLink = dateStr.replace('T', ' ')
dateStrLink = dateStrLink.replace('Z', '')
url = item[1]
faviconUrl = _getNewswireFavicon(url)
faviconLink = ''
if faviconUrl:
faviconLink = \
''
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', ' ')
htmlStr += '
\n'
else:
htmlStr += ' '
htmlStr += dateShown + '\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', ' ')
if moderator and moderatedItem:
htmlStr += '
\n'
if htmlStr:
htmlStr = '\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,
theme: 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'
htmlStr = htmlHeaderWithExternalStyle(cssFilename)
# top banner
bannerFile, bannerFilename = \
getBannerFile(baseDir, nickname, domain, theme)
htmlStr += \
'\n'
htmlStr += '\n'
htmlStr += \
'\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,
theme: str) -> str:
"""Shows the mobile version of the newswire right column
"""
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 '.css'
themeName = getConfigParam(baseDir, 'theme')
themePath = f'{baseDir}/theme/{themeName}.css'
if os.path.isfile(themePath):
cssFiles.append('theme/' + themeName + '.css')
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 = htmlHeaderWithExternalStyles(cssFiles)
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain)
usersPath = '/users/' + nickname
htmlStr += '
\n'
if not defaultTimeline == 'tlfeatures':
htmlStr += (f"\t"
f"{translate['Switch to profile view']}\n")
else:
# TODO: News instances should ignore personalised banners
# Currently uses the 'news' actor banner - as it remains unchanged
htmlStr += (f"\t"
f"{translate['Features']}\n"
f"\t
\n"
f"\t\t#IndymediaBack\n"
f"\t
\n")
# 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']
# Full row "navbar"
if defaultTimeline == 'tlfeatures':
# Show "tab" links instead of standard "buttons"
htmlStr += headerNewsTabs('newswiremobile', translate, usersPath, baseDir, userPages)
# Close banner div
htmlStr += '