2020-11-09 22:44:03 +00:00
|
|
|
__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
|
2020-11-18 22:31:38 +00:00
|
|
|
from utils import removeHtml
|
2020-11-10 09:55:06 +00:00
|
|
|
from utils import locatePost
|
|
|
|
from utils import loadJson
|
2020-11-09 22:44:03 +00:00
|
|
|
from utils import votesOnNewswireItem
|
|
|
|
from utils import getNicknameFromActor
|
2020-12-01 21:44:27 +00:00
|
|
|
from utils import isEditor
|
2020-11-09 22:44:03 +00:00
|
|
|
from posts import isModerator
|
|
|
|
from webapp_utils import getRightImageFile
|
2020-11-10 16:54:35 +00:00
|
|
|
from webapp_utils import htmlHeaderWithExternalStyle
|
2020-11-09 22:44:03 +00:00
|
|
|
from webapp_utils import htmlFooter
|
|
|
|
from webapp_utils import getBannerFile
|
|
|
|
from webapp_utils import htmlPostSeparator
|
|
|
|
from webapp_utils import headerButtonsFrontScreen
|
|
|
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
def _votesIndicator(totalVotes: int, positiveVoting: bool) -> str:
|
2020-11-09 22:44:03 +00:00
|
|
|
"""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: {},
|
2020-12-09 13:31:54 +00:00
|
|
|
moderator: bool, editor: bool,
|
2020-11-09 22:44:03 +00:00
|
|
|
newswire: {}, positiveVoting: bool,
|
|
|
|
showBackButton: bool, timelinePath: str,
|
|
|
|
showPublishButton: bool,
|
|
|
|
showPublishAsIcon: bool,
|
|
|
|
rssIconAtTop: bool,
|
|
|
|
publishButtonAtTop: bool,
|
|
|
|
authorized: bool,
|
2020-12-20 17:26:38 +00:00
|
|
|
showHeaderImage: bool,
|
|
|
|
theme: str) -> str:
|
2020-11-09 22:44:03 +00:00
|
|
|
"""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 = \
|
2020-12-20 17:26:38 +00:00
|
|
|
getRightImageFile(baseDir, nickname, domain, theme)
|
2020-11-09 22:44:03 +00:00
|
|
|
|
|
|
|
# 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="/' + \
|
2020-12-09 13:08:26 +00:00
|
|
|
'icons/edit_notify.png" /></a>\n'
|
2020-11-09 22:44:03 +00:00
|
|
|
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="/' + \
|
2020-12-09 13:08:26 +00:00
|
|
|
'icons/edit.png" /></a>\n'
|
2020-11-09 22:44:03 +00:00
|
|
|
|
2020-12-02 18:40:51 +00:00
|
|
|
# show the RSS icons
|
2020-11-09 22:44:03 +00:00
|
|
|
rssIconStr = \
|
2020-12-02 18:40:51 +00:00
|
|
|
' <a href="/categories.xml">' + \
|
|
|
|
'<img class="' + editImageClass + \
|
|
|
|
'" loading="lazy" alt="' + \
|
|
|
|
translate['Hashtag Categories RSS Feed'] + '" title="' + \
|
|
|
|
translate['Hashtag Categories RSS Feed'] + '" src="/' + \
|
2020-12-09 13:08:26 +00:00
|
|
|
'icons/categoriesrss.png" /></a>\n'
|
2020-12-02 18:40:51 +00:00
|
|
|
rssIconStr += \
|
2020-11-09 22:44:03 +00:00
|
|
|
' <a href="/newswire.xml">' + \
|
|
|
|
'<img class="' + editImageClass + \
|
|
|
|
'" loading="lazy" alt="' + \
|
|
|
|
translate['Newswire RSS Feed'] + '" title="' + \
|
|
|
|
translate['Newswire RSS Feed'] + '" src="/' + \
|
2020-12-09 13:08:26 +00:00
|
|
|
'icons/logorss.png" /></a>\n'
|
2020-11-09 22:44:03 +00:00
|
|
|
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="/' + \
|
2020-12-09 13:08:26 +00:00
|
|
|
'icons/publish.png" /></a>\n'
|
2020-11-09 22:44:03 +00:00
|
|
|
|
|
|
|
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 = \
|
2020-12-22 18:06:23 +00:00
|
|
|
_htmlNewswire(baseDir, newswire, nickname, moderator, translate,
|
|
|
|
positiveVoting)
|
2020-11-09 22:44:03 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2020-12-27 20:39:32 +00:00
|
|
|
def _getBrokenFavSubstitute() -> str:
|
2020-12-26 11:06:57 +00:00
|
|
|
"""Substitute link used if a favicon is not available
|
|
|
|
"""
|
2021-01-07 12:12:09 +00:00
|
|
|
return " onerror=\"this.onerror=null; this.src='/newswire_favicon.ico'\""
|
2020-12-26 11:06:57 +00:00
|
|
|
|
|
|
|
|
2020-12-27 20:41:29 +00:00
|
|
|
def _getNewswireFavicon(url: str) -> str:
|
2020-12-26 11:06:57 +00:00
|
|
|
"""Returns a favicon url from the given article link
|
|
|
|
"""
|
|
|
|
if '://' not in url:
|
2021-01-07 10:38:33 +00:00
|
|
|
return '/newswire_favicon.ico'
|
2021-01-05 21:30:56 +00:00
|
|
|
if url.startswith('http://'):
|
|
|
|
if not (url.endswith('.onion') or url.endswith('.i2p')):
|
2021-01-07 10:38:33 +00:00
|
|
|
return '/newswire_favicon.ico'
|
2020-12-26 11:06:57 +00:00
|
|
|
domain = url.split('://')[1]
|
|
|
|
if '/' not in domain:
|
|
|
|
return url + '/favicon.ico'
|
|
|
|
else:
|
|
|
|
domain = domain.split('/')[0]
|
2020-12-26 11:14:26 +00:00
|
|
|
return url.split('://')[0] + '://' + domain + '/favicon.ico'
|
2020-12-26 11:06:57 +00:00
|
|
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
def _htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
|
|
|
|
translate: {}, positiveVoting: bool) -> str:
|
2020-11-09 22:44:03 +00:00
|
|
|
"""Converts a newswire dict into html
|
|
|
|
"""
|
|
|
|
separatorStr = htmlPostSeparator(baseDir, 'right')
|
|
|
|
htmlStr = ''
|
|
|
|
for dateStr, item in newswire.items():
|
2020-11-18 22:31:38 +00:00
|
|
|
item[0] = removeHtml(item[0]).strip()
|
|
|
|
if not item[0]:
|
2020-11-18 22:15:32 +00:00
|
|
|
continue
|
2020-11-18 22:31:38 +00:00
|
|
|
# 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]
|
2020-11-22 15:41:42 +00:00
|
|
|
try:
|
|
|
|
publishedDate = \
|
|
|
|
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
|
|
|
|
except BaseException:
|
|
|
|
print('WARN: bad date format ' + dateStr)
|
|
|
|
continue
|
2020-11-09 22:44:03 +00:00
|
|
|
dateShown = publishedDate.strftime("%Y-%m-%d %H:%M")
|
|
|
|
|
|
|
|
dateStrLink = dateStr.replace('T', ' ')
|
|
|
|
dateStrLink = dateStrLink.replace('Z', '')
|
2020-12-26 11:06:57 +00:00
|
|
|
url = item[1]
|
2020-12-27 20:41:29 +00:00
|
|
|
faviconUrl = _getNewswireFavicon(url)
|
2020-12-26 11:06:57 +00:00
|
|
|
faviconLink = ''
|
|
|
|
if faviconUrl:
|
|
|
|
faviconLink = \
|
|
|
|
'<img loading="lazy" src="' + faviconUrl + '" ' + \
|
2020-12-27 20:39:32 +00:00
|
|
|
_getBrokenFavSubstitute() + '/>'
|
2020-11-09 22:44:03 +00:00
|
|
|
moderatedItem = item[5]
|
|
|
|
htmlStr += separatorStr
|
|
|
|
if moderatedItem and 'vote:' + nickname in item[2]:
|
|
|
|
totalVotesStr = ''
|
|
|
|
totalVotes = 0
|
|
|
|
if moderator:
|
|
|
|
totalVotes = votesOnNewswireItem(item[2])
|
|
|
|
totalVotesStr = \
|
2020-12-22 18:06:23 +00:00
|
|
|
_votesIndicator(totalVotes, positiveVoting)
|
2020-11-09 22:44:03 +00:00
|
|
|
|
2020-11-18 22:15:32 +00:00
|
|
|
title = removeLongWords(item[0], 16, []).replace('\n', '<br>')
|
2020-11-09 22:44:03 +00:00
|
|
|
htmlStr += '<p class="newswireItemVotedOn">' + \
|
2020-12-26 11:06:57 +00:00
|
|
|
'<a href="' + url + '" target="_blank" ' + \
|
2020-12-11 10:14:58 +00:00
|
|
|
'rel="nofollow noopener noreferrer">' + \
|
2020-12-26 11:06:57 +00:00
|
|
|
'<span class="newswireItemVotedOn">' + \
|
|
|
|
faviconLink + title + \
|
2020-11-09 22:44:03 +00:00
|
|
|
'</span></a>' + totalVotesStr
|
|
|
|
if moderator:
|
|
|
|
htmlStr += \
|
|
|
|
' ' + dateShown + '<a href="/users/' + nickname + \
|
|
|
|
'/newswireunvote=' + dateStrLink + '" ' + \
|
|
|
|
'title="' + translate['Remove Vote'] + '">'
|
|
|
|
htmlStr += '<img loading="lazy" class="voteicon" src="/' + \
|
2020-12-09 13:08:26 +00:00
|
|
|
'icons/vote.png" /></a></p>\n'
|
2020-11-09 22:44:03 +00:00
|
|
|
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 = \
|
2020-12-22 18:06:23 +00:00
|
|
|
_votesIndicator(totalVotes, positiveVoting)
|
2020-11-09 22:44:03 +00:00
|
|
|
|
2020-11-18 22:15:32 +00:00
|
|
|
title = removeLongWords(item[0], 16, []).replace('\n', '<br>')
|
2020-11-09 22:44:03 +00:00
|
|
|
if moderator and moderatedItem:
|
|
|
|
htmlStr += '<p class="newswireItemModerated">' + \
|
2020-12-26 11:06:57 +00:00
|
|
|
'<a href="' + url + '" target="_blank" ' + \
|
2020-12-11 10:14:58 +00:00
|
|
|
'rel="nofollow noopener noreferrer">' + \
|
2020-12-26 11:06:57 +00:00
|
|
|
faviconLink + title + '</a>' + totalVotesStr
|
2020-11-09 22:44:03 +00:00
|
|
|
htmlStr += ' ' + dateShown
|
|
|
|
htmlStr += '<a href="/users/' + nickname + \
|
|
|
|
'/newswirevote=' + dateStrLink + '" ' + \
|
|
|
|
'title="' + translate['Vote'] + '">'
|
|
|
|
htmlStr += '<img class="voteicon" src="/' + \
|
2020-12-09 13:08:26 +00:00
|
|
|
'icons/vote.png" /></a>'
|
2020-11-09 22:44:03 +00:00
|
|
|
htmlStr += '</p>\n'
|
|
|
|
else:
|
|
|
|
htmlStr += '<p class="newswireItem">' + \
|
2020-12-26 11:06:57 +00:00
|
|
|
'<a href="' + url + '" target="_blank" ' + \
|
2020-12-11 10:14:58 +00:00
|
|
|
'rel="nofollow noopener noreferrer">' + \
|
2020-12-26 11:06:57 +00:00
|
|
|
faviconLink + title + '</a>' + \
|
2020-11-09 22:44:03 +00:00
|
|
|
totalVotesStr
|
|
|
|
htmlStr += ' <span class="newswireDate">'
|
|
|
|
htmlStr += dateShown + '</span></p>\n'
|
2020-12-27 15:22:14 +00:00
|
|
|
|
|
|
|
if htmlStr:
|
|
|
|
htmlStr = '<nav>\n' + htmlStr + '</nav>\n'
|
2020-11-09 22:44:03 +00:00
|
|
|
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,
|
2020-12-20 18:16:53 +00:00
|
|
|
blogImageDescription: str,
|
|
|
|
theme: str) -> str:
|
2020-11-09 22:44:03 +00:00
|
|
|
"""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'
|
|
|
|
|
2020-11-12 15:55:08 +00:00
|
|
|
htmlStr = htmlHeaderWithExternalStyle(cssFilename)
|
2020-11-09 22:44:03 +00:00
|
|
|
|
|
|
|
# top banner
|
2020-12-20 18:16:53 +00:00
|
|
|
bannerFile, bannerFilename = \
|
|
|
|
getBannerFile(baseDir, nickname, domain, theme)
|
2020-11-09 22:44:03 +00:00
|
|
|
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():
|
2020-11-18 22:31:38 +00:00
|
|
|
item[0] = removeHtml(item[0]).strip()
|
|
|
|
if not item[0]:
|
2020-11-18 22:15:32 +00:00
|
|
|
continue
|
2020-11-18 22:31:38 +00:00
|
|
|
# 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]
|
2020-11-09 22:44:03 +00:00
|
|
|
# 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,
|
2020-12-20 17:26:38 +00:00
|
|
|
defaultTimeline: str,
|
|
|
|
theme: str) -> str:
|
2020-11-09 22:44:03 +00:00
|
|
|
"""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'
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2020-11-12 15:55:08 +00:00
|
|
|
htmlStr = htmlHeaderWithExternalStyle(cssFilename)
|
2020-11-09 22:44:03 +00:00
|
|
|
|
2020-12-20 18:16:53 +00:00
|
|
|
bannerFile, bannerFilename = \
|
|
|
|
getBannerFile(baseDir, nickname, domain, theme)
|
2020-11-09 22:44:03 +00:00
|
|
|
htmlStr += \
|
|
|
|
'<a href="/users/' + nickname + '/' + defaultTimeline + '">' + \
|
|
|
|
'<img loading="lazy" class="timeline-banner" ' + \
|
|
|
|
'src="/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
|
|
|
|
2020-11-19 21:57:26 +00:00
|
|
|
htmlStr += '<div class="col-right-mobile">\n'
|
|
|
|
|
2020-11-09 22:44:03 +00:00
|
|
|
htmlStr += '<center>' + \
|
|
|
|
headerButtonsFrontScreen(translate, nickname,
|
|
|
|
'newswire', authorized,
|
2020-12-09 13:31:54 +00:00
|
|
|
iconsAsButtons) + '</center>'
|
2020-11-29 11:19:09 +00:00
|
|
|
if newswire:
|
|
|
|
htmlStr += \
|
|
|
|
getRightColumnContent(baseDir, nickname, domainFull,
|
|
|
|
httpPrefix, translate,
|
2020-12-09 13:31:54 +00:00
|
|
|
moderator, editor,
|
2020-11-29 11:19:09 +00:00
|
|
|
newswire, positiveVoting,
|
|
|
|
False, timelinePath, showPublishButton,
|
|
|
|
showPublishAsIcon, rssIconAtTop, False,
|
2020-12-20 17:26:38 +00:00
|
|
|
authorized, False, theme)
|
2020-11-29 11:19:09 +00:00
|
|
|
else:
|
|
|
|
if editor:
|
|
|
|
htmlStr += '<br><br><br>\n'
|
|
|
|
htmlStr += '<center>\n '
|
|
|
|
htmlStr += translate['Select the edit icon to add RSS feeds']
|
|
|
|
htmlStr += '\n</center>\n'
|
2020-11-19 21:57:26 +00:00
|
|
|
# end of col-right-mobile
|
|
|
|
htmlStr += '</div\n>'
|
|
|
|
|
2020-11-09 22:44:03 +00:00
|
|
|
htmlStr += htmlFooter()
|
|
|
|
return htmlStr
|
|
|
|
|
|
|
|
|
|
|
|
def htmlEditNewswire(cssCache: {}, translate: {}, baseDir: str, path: str,
|
|
|
|
domain: str, port: int, httpPrefix: str,
|
2020-12-20 18:16:53 +00:00
|
|
|
defaultTimeline: str, theme: str) -> str:
|
2020-11-09 22:44:03 +00:00
|
|
|
"""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'
|
|
|
|
|
|
|
|
# filename of the banner shown at the top
|
2020-12-20 18:16:53 +00:00
|
|
|
bannerFile, bannerFilename = \
|
|
|
|
getBannerFile(baseDir, nickname, domain, theme)
|
2020-11-09 22:44:03 +00:00
|
|
|
|
2020-11-12 15:55:08 +00:00
|
|
|
editNewswireForm = htmlHeaderWithExternalStyle(cssFilename)
|
2020-11-09 22:44:03 +00:00
|
|
|
|
|
|
|
# top banner
|
|
|
|
editNewswireForm += \
|
2020-12-27 16:57:15 +00:00
|
|
|
'<header>' + \
|
2020-11-09 22:44:03 +00:00
|
|
|
'<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="' + \
|
2020-12-27 16:57:15 +00:00
|
|
|
'/users/' + nickname + '/' + bannerFile + '" /></a>\n' + \
|
|
|
|
'</header>'
|
2020-11-09 22:44:03 +00:00
|
|
|
|
|
|
|
editNewswireForm += \
|
|
|
|
'<form enctype="multipart/form-data" method="POST" ' + \
|
|
|
|
'accept-charset="UTF-8" action="' + path + '/newswiredata">\n'
|
|
|
|
editNewswireForm += \
|
|
|
|
' <div class="vertical-center">\n'
|
|
|
|
editNewswireForm += \
|
2020-11-21 09:44:33 +00:00
|
|
|
' <h1>' + translate['Edit newswire'] + '</h1>'
|
2020-11-09 22:44:03 +00:00
|
|
|
editNewswireForm += \
|
2020-12-21 16:20:17 +00:00
|
|
|
' <div class="containerSubmitNewPost">\n'
|
2020-11-09 22:44:03 +00:00
|
|
|
editNewswireForm += \
|
|
|
|
' <input type="submit" name="submitNewswire" value="' + \
|
2020-12-21 15:09:29 +00:00
|
|
|
translate['Submit'] + '">\n'
|
2020-11-09 22:44:03 +00:00
|
|
|
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
|
2020-11-10 09:55:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
def htmlEditNewsPost(cssCache: {}, translate: {}, baseDir: str, path: str,
|
|
|
|
domain: str, port: int,
|
|
|
|
httpPrefix: str, postUrl: str) -> str:
|
|
|
|
"""Edits a news post on the news/features timeline
|
|
|
|
"""
|
|
|
|
if '/users/' not in path:
|
|
|
|
return ''
|
|
|
|
pathOriginal = path
|
|
|
|
|
|
|
|
nickname = getNicknameFromActor(path)
|
|
|
|
if not nickname:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
# is the user an editor?
|
|
|
|
if not isEditor(baseDir, nickname):
|
|
|
|
return ''
|
|
|
|
|
|
|
|
postUrl = postUrl.replace('/', '#')
|
|
|
|
postFilename = locatePost(baseDir, nickname, domain, postUrl)
|
|
|
|
if not postFilename:
|
|
|
|
return ''
|
|
|
|
postJsonObject = loadJson(postFilename)
|
|
|
|
if not postJsonObject:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
cssFilename = baseDir + '/epicyon-links.css'
|
|
|
|
if os.path.isfile(baseDir + '/links.css'):
|
|
|
|
cssFilename = baseDir + '/links.css'
|
|
|
|
|
2020-11-12 15:55:08 +00:00
|
|
|
editNewsPostForm = htmlHeaderWithExternalStyle(cssFilename)
|
2020-11-10 09:55:06 +00:00
|
|
|
editNewsPostForm += \
|
|
|
|
'<form enctype="multipart/form-data" method="POST" ' + \
|
|
|
|
'accept-charset="UTF-8" action="' + path + '/newseditdata">\n'
|
|
|
|
editNewsPostForm += \
|
|
|
|
' <div class="vertical-center">\n'
|
|
|
|
editNewsPostForm += \
|
2020-11-21 09:56:45 +00:00
|
|
|
' <h1>' + translate['Edit News Post'] + '</h1>'
|
2020-11-10 09:55:06 +00:00
|
|
|
editNewsPostForm += \
|
|
|
|
' <div class="container">\n'
|
|
|
|
editNewsPostForm += \
|
|
|
|
' <a href="' + pathOriginal + '/tlnews">' + \
|
|
|
|
'<button class="cancelbtn">' + translate['Go Back'] + '</button></a>\n'
|
|
|
|
editNewsPostForm += \
|
|
|
|
' <input type="submit" name="submitEditedNewsPost" value="' + \
|
|
|
|
translate['Submit'] + '">\n'
|
|
|
|
editNewsPostForm += \
|
|
|
|
' </div>\n'
|
|
|
|
|
|
|
|
editNewsPostForm += \
|
|
|
|
'<div class="container">'
|
|
|
|
|
|
|
|
editNewsPostForm += \
|
|
|
|
' <input type="hidden" name="newsPostUrl" value="' + \
|
|
|
|
postUrl + '">\n'
|
|
|
|
|
|
|
|
newsPostTitle = postJsonObject['object']['summary']
|
|
|
|
editNewsPostForm += \
|
|
|
|
' <input type="text" name="newsPostTitle" value="' + \
|
|
|
|
newsPostTitle + '"><br>\n'
|
|
|
|
|
|
|
|
newsPostContent = postJsonObject['object']['content']
|
|
|
|
editNewsPostForm += \
|
|
|
|
' <textarea id="message" name="editedNewsPost" ' + \
|
|
|
|
'style="height:600px">' + newsPostContent + '</textarea>'
|
|
|
|
|
|
|
|
editNewsPostForm += \
|
|
|
|
'</div>'
|
|
|
|
|
|
|
|
editNewsPostForm += htmlFooter()
|
|
|
|
return editNewsPostForm
|