Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon into main
21
blog.py
|
|
@ -365,7 +365,7 @@ def htmlBlogPost(authorized: bool,
|
||||||
blogStr += '<img style="width:3%;min-width:50px" ' + \
|
blogStr += '<img style="width:3%;min-width:50px" ' + \
|
||||||
'loading="lazy" alt="RSS 2.0" ' + \
|
'loading="lazy" alt="RSS 2.0" ' + \
|
||||||
'title="RSS 2.0" src="/' + \
|
'title="RSS 2.0" src="/' + \
|
||||||
iconsDir + '/rss.png" /></a>'
|
iconsDir + '/logorss.png" /></a>'
|
||||||
|
|
||||||
# blogStr += '<a href="' + httpPrefix + '://' + \
|
# blogStr += '<a href="' + httpPrefix + '://' + \
|
||||||
# domainFull + '/blog/' + nickname + '/rss.txt">'
|
# domainFull + '/blog/' + nickname + '/rss.txt">'
|
||||||
|
|
@ -461,7 +461,7 @@ def htmlBlogPage(authorized: bool, session,
|
||||||
domainFull + '/blog/' + nickname + '/rss.xml">'
|
domainFull + '/blog/' + nickname + '/rss.xml">'
|
||||||
blogStr += '<img loading="lazy" alt="RSS 2.0" ' + \
|
blogStr += '<img loading="lazy" alt="RSS 2.0" ' + \
|
||||||
'title="RSS 2.0" src="/' + \
|
'title="RSS 2.0" src="/' + \
|
||||||
iconsDir + '/rss.png" /></a>'
|
iconsDir + '/logorss.png" /></a>'
|
||||||
|
|
||||||
# blogStr += '<a href="' + httpPrefix + '://' + \
|
# blogStr += '<a href="' + httpPrefix + '://' + \
|
||||||
# domainFull + '/blog/' + nickname + '/rss.txt">'
|
# domainFull + '/blog/' + nickname + '/rss.txt">'
|
||||||
|
|
@ -478,7 +478,8 @@ def htmlBlogPage(authorized: bool, session,
|
||||||
def htmlBlogPageRSS2(authorized: bool, session,
|
def htmlBlogPageRSS2(authorized: bool, session,
|
||||||
baseDir: str, httpPrefix: str, translate: {},
|
baseDir: str, httpPrefix: str, translate: {},
|
||||||
nickname: str, domain: str, port: int,
|
nickname: str, domain: str, port: int,
|
||||||
noOfItems: int, pageNumber: int) -> str:
|
noOfItems: int, pageNumber: int,
|
||||||
|
includeHeader: bool) -> str:
|
||||||
"""Returns an RSS version 2 feed containing posts
|
"""Returns an RSS version 2 feed containing posts
|
||||||
"""
|
"""
|
||||||
if ' ' in nickname or '@' in nickname or \
|
if ' ' in nickname or '@' in nickname or \
|
||||||
|
|
@ -490,12 +491,18 @@ def htmlBlogPageRSS2(authorized: bool, session,
|
||||||
if port != 80 and port != 443:
|
if port != 80 and port != 443:
|
||||||
domainFull = domain + ':' + str(port)
|
domainFull = domain + ':' + str(port)
|
||||||
|
|
||||||
blogRSS2 = rss2Header(httpPrefix, nickname, domainFull, 'Blog', translate)
|
blogRSS2 = ''
|
||||||
|
if includeHeader:
|
||||||
|
blogRSS2 = rss2Header(httpPrefix, nickname, domainFull,
|
||||||
|
'Blog', translate)
|
||||||
|
|
||||||
blogsIndex = baseDir + '/accounts/' + \
|
blogsIndex = baseDir + '/accounts/' + \
|
||||||
nickname + '@' + domain + '/tlblogs.index'
|
nickname + '@' + domain + '/tlblogs.index'
|
||||||
if not os.path.isfile(blogsIndex):
|
if not os.path.isfile(blogsIndex):
|
||||||
|
if includeHeader:
|
||||||
return blogRSS2 + rss2Footer()
|
return blogRSS2 + rss2Footer()
|
||||||
|
else:
|
||||||
|
return blogRSS2
|
||||||
|
|
||||||
timelineJson = createBlogsTimeline(session, baseDir,
|
timelineJson = createBlogsTimeline(session, baseDir,
|
||||||
nickname, domain, port,
|
nickname, domain, port,
|
||||||
|
|
@ -504,7 +511,10 @@ def htmlBlogPageRSS2(authorized: bool, session,
|
||||||
pageNumber)
|
pageNumber)
|
||||||
|
|
||||||
if not timelineJson:
|
if not timelineJson:
|
||||||
|
if includeHeader:
|
||||||
return blogRSS2 + rss2Footer()
|
return blogRSS2 + rss2Footer()
|
||||||
|
else:
|
||||||
|
return blogRSS2
|
||||||
|
|
||||||
if pageNumber is not None:
|
if pageNumber is not None:
|
||||||
for item in timelineJson['orderedItems']:
|
for item in timelineJson['orderedItems']:
|
||||||
|
|
@ -518,7 +528,10 @@ def htmlBlogPageRSS2(authorized: bool, session,
|
||||||
domainFull, item,
|
domainFull, item,
|
||||||
None, True)
|
None, True)
|
||||||
|
|
||||||
|
if includeHeader:
|
||||||
return blogRSS2 + rss2Footer()
|
return blogRSS2 + rss2Footer()
|
||||||
|
else:
|
||||||
|
return blogRSS2
|
||||||
|
|
||||||
|
|
||||||
def htmlBlogPageRSS3(authorized: bool, session,
|
def htmlBlogPageRSS3(authorized: bool, session,
|
||||||
|
|
|
||||||
440
daemon.py
|
|
@ -164,6 +164,8 @@ from shares import getSharesFeedForPerson
|
||||||
from shares import addShare
|
from shares import addShare
|
||||||
from shares import removeShare
|
from shares import removeShare
|
||||||
from shares import expireShares
|
from shares import expireShares
|
||||||
|
from utils import containsInvalidChars
|
||||||
|
from utils import isSystemAccount
|
||||||
from utils import setConfigParam
|
from utils import setConfigParam
|
||||||
from utils import getConfigParam
|
from utils import getConfigParam
|
||||||
from utils import removeIdEnding
|
from utils import removeIdEnding
|
||||||
|
|
@ -194,6 +196,7 @@ from media import removeMetaData
|
||||||
from cache import storePersonInCache
|
from cache import storePersonInCache
|
||||||
from cache import getPersonFromCache
|
from cache import getPersonFromCache
|
||||||
from httpsig import verifyPostHeaders
|
from httpsig import verifyPostHeaders
|
||||||
|
from theme import setNewsAvatar
|
||||||
from theme import setTheme
|
from theme import setTheme
|
||||||
from theme import getTheme
|
from theme import getTheme
|
||||||
from theme import enableGrayscale
|
from theme import enableGrayscale
|
||||||
|
|
@ -212,6 +215,8 @@ from devices import E2EEdevicesCollection
|
||||||
from devices import E2EEvalidDevice
|
from devices import E2EEvalidDevice
|
||||||
from devices import E2EEaddDevice
|
from devices import E2EEaddDevice
|
||||||
from newswire import getRSSfromDict
|
from newswire import getRSSfromDict
|
||||||
|
from newswire import rss2Header
|
||||||
|
from newswire import rss2Footer
|
||||||
from newsdaemon import runNewswireWatchdog
|
from newsdaemon import runNewswireWatchdog
|
||||||
from newsdaemon import runNewswireDaemon
|
from newsdaemon import runNewswireDaemon
|
||||||
import os
|
import os
|
||||||
|
|
@ -300,11 +305,11 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
accountDir = self.server.baseDir + '/accounts/' + \
|
accountDir = self.server.baseDir + '/accounts/' + \
|
||||||
nickname + '@' + self.server.domain
|
nickname + '@' + self.server.domain
|
||||||
if not os.path.isdir(accountDir):
|
if not os.path.isdir(accountDir):
|
||||||
return False
|
|
||||||
minimalFilename = accountDir + '/minimal'
|
|
||||||
if os.path.isfile(minimalFilename):
|
|
||||||
return True
|
return True
|
||||||
|
minimalFilename = accountDir + '/.notminimal'
|
||||||
|
if os.path.isfile(minimalFilename):
|
||||||
return False
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def _setMinimal(self, nickname: str, minimal: bool) -> None:
|
def _setMinimal(self, nickname: str, minimal: bool) -> None:
|
||||||
"""Sets whether an account should display minimal buttons
|
"""Sets whether an account should display minimal buttons
|
||||||
|
|
@ -313,11 +318,11 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
nickname + '@' + self.server.domain
|
nickname + '@' + self.server.domain
|
||||||
if not os.path.isdir(accountDir):
|
if not os.path.isdir(accountDir):
|
||||||
return
|
return
|
||||||
minimalFilename = accountDir + '/minimal'
|
minimalFilename = accountDir + '/.notminimal'
|
||||||
minimalFileExists = os.path.isfile(minimalFilename)
|
minimalFileExists = os.path.isfile(minimalFilename)
|
||||||
if not minimal and minimalFileExists:
|
if minimal and minimalFileExists:
|
||||||
os.remove(minimalFilename)
|
os.remove(minimalFilename)
|
||||||
elif minimal and not minimalFileExists:
|
elif not minimal and not minimalFileExists:
|
||||||
with open(minimalFilename, 'w+') as fp:
|
with open(minimalFilename, 'w+') as fp:
|
||||||
fp.write('\n')
|
fp.write('\n')
|
||||||
|
|
||||||
|
|
@ -521,6 +526,21 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.send_header('X-Robots-Tag', 'noindex')
|
self.send_header('X-Robots-Tag', 'noindex')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
|
def _logout_redirect(self, redirect: str, cookie: str,
|
||||||
|
callingDomain: str) -> None:
|
||||||
|
if '://' not in redirect:
|
||||||
|
print('REDIRECT ERROR: redirect is not an absolute url ' +
|
||||||
|
redirect)
|
||||||
|
|
||||||
|
self.send_response(303)
|
||||||
|
self.send_header('Set-Cookie', 'epicyon=; SameSite=Strict')
|
||||||
|
self.send_header('Location', redirect)
|
||||||
|
self.send_header('Host', callingDomain)
|
||||||
|
self.send_header('InstanceID', self.server.instanceId)
|
||||||
|
self.send_header('Content-Length', '0')
|
||||||
|
self.send_header('X-Robots-Tag', 'noindex')
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
def _set_headers_base(self, fileFormat: str, length: int, cookie: str,
|
def _set_headers_base(self, fileFormat: str, length: int, cookie: str,
|
||||||
callingDomain: str) -> None:
|
callingDomain: str) -> None:
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
|
|
@ -1240,8 +1260,9 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
loginNickname, loginPassword, register = \
|
loginNickname, loginPassword, register = \
|
||||||
htmlGetLoginCredentials(loginParams, self.server.lastLoginTime)
|
htmlGetLoginCredentials(loginParams, self.server.lastLoginTime)
|
||||||
if loginNickname:
|
if loginNickname:
|
||||||
if loginNickname == 'news' or loginNickname == 'inbox':
|
if isSystemAccount(loginNickname):
|
||||||
print('Invalid username login: ' + loginNickname)
|
print('Invalid username login: ' + loginNickname +
|
||||||
|
' (system account)')
|
||||||
self._clearLoginDetails(loginNickname, callingDomain)
|
self._clearLoginDetails(loginNickname, callingDomain)
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
@ -1625,7 +1646,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if debug:
|
if debug:
|
||||||
print('You cannot perform an option action on yourself')
|
print('You cannot perform an option action on yourself')
|
||||||
|
|
||||||
# view button on person option screen
|
# person options screen, view button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitView=' in optionsConfirmParams:
|
if '&submitView=' in optionsConfirmParams:
|
||||||
if debug:
|
if debug:
|
||||||
print('Viewing ' + optionsActor)
|
print('Viewing ' + optionsActor)
|
||||||
|
|
@ -1634,7 +1656,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# petname submit button on person option screen
|
# person options screen, petname submit button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitPetname=' in optionsConfirmParams and petname:
|
if '&submitPetname=' in optionsConfirmParams and petname:
|
||||||
if debug:
|
if debug:
|
||||||
print('Change petname to ' + petname)
|
print('Change petname to ' + petname)
|
||||||
|
|
@ -1650,7 +1673,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# person notes submit button on person option screen
|
# person options screen, person notes submit button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitPersonNotes=' in optionsConfirmParams:
|
if '&submitPersonNotes=' in optionsConfirmParams:
|
||||||
if debug:
|
if debug:
|
||||||
print('Change person notes')
|
print('Change person notes')
|
||||||
|
|
@ -1668,7 +1692,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# person on calendar checkbox on person option screen
|
# person options screen, on calendar checkbox
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitOnCalendar=' in optionsConfirmParams:
|
if '&submitOnCalendar=' in optionsConfirmParams:
|
||||||
onCalendar = None
|
onCalendar = None
|
||||||
if 'onCalendar=' in optionsConfirmParams:
|
if 'onCalendar=' in optionsConfirmParams:
|
||||||
|
|
@ -1694,7 +1719,35 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# block person button on person option screen
|
# person options screen, permission to post to newswire
|
||||||
|
# See htmlPersonOptions
|
||||||
|
if '&submitPostToNews=' in optionsConfirmParams:
|
||||||
|
if isModerator(self.server.baseDir, chooserNickname):
|
||||||
|
postsToNews = None
|
||||||
|
if 'postsToNews=' in optionsConfirmParams:
|
||||||
|
postsToNews = optionsConfirmParams.split('postsToNews=')[1]
|
||||||
|
if '&' in postsToNews:
|
||||||
|
postsToNews = postsToNews.split('&')[0]
|
||||||
|
newswireBlockedFilename = \
|
||||||
|
self.server.baseDir + '/accounts/' + \
|
||||||
|
optionsNickname + '@' + optionsDomain + '/.nonewswire'
|
||||||
|
if postsToNews == 'on':
|
||||||
|
if os.path.isfile(newswireBlockedFilename):
|
||||||
|
os.remove(newswireBlockedFilename)
|
||||||
|
else:
|
||||||
|
noNewswireFile = open(newswireBlockedFilename, "w+")
|
||||||
|
if noNewswireFile:
|
||||||
|
noNewswireFile.write('\n')
|
||||||
|
noNewswireFile.close()
|
||||||
|
self._redirect_headers(usersPath + '/' +
|
||||||
|
self.server.defaultTimeline +
|
||||||
|
'?page='+str(pageNumber), cookie,
|
||||||
|
callingDomain)
|
||||||
|
self.server.POSTbusy = False
|
||||||
|
return
|
||||||
|
|
||||||
|
# person options screen, block button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitBlock=' in optionsConfirmParams:
|
if '&submitBlock=' in optionsConfirmParams:
|
||||||
if debug:
|
if debug:
|
||||||
print('Adding block by ' + chooserNickname +
|
print('Adding block by ' + chooserNickname +
|
||||||
|
|
@ -1703,7 +1756,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
domain,
|
domain,
|
||||||
optionsNickname, optionsDomainFull)
|
optionsNickname, optionsDomainFull)
|
||||||
|
|
||||||
# unblock button on person option screen
|
# person options screen, unblock button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitUnblock=' in optionsConfirmParams:
|
if '&submitUnblock=' in optionsConfirmParams:
|
||||||
if debug:
|
if debug:
|
||||||
print('Unblocking ' + optionsActor)
|
print('Unblocking ' + optionsActor)
|
||||||
|
|
@ -1719,7 +1773,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# follow button on person option screen
|
# person options screen, follow button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitFollow=' in optionsConfirmParams:
|
if '&submitFollow=' in optionsConfirmParams:
|
||||||
if debug:
|
if debug:
|
||||||
print('Following ' + optionsActor)
|
print('Following ' + optionsActor)
|
||||||
|
|
@ -1735,7 +1790,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# unfollow button on person option screen
|
# person options screen, unfollow button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitUnfollow=' in optionsConfirmParams:
|
if '&submitUnfollow=' in optionsConfirmParams:
|
||||||
if debug:
|
if debug:
|
||||||
print('Unfollowing ' + optionsActor)
|
print('Unfollowing ' + optionsActor)
|
||||||
|
|
@ -1751,7 +1807,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# DM button on person option screen
|
# person options screen, DM button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitDM=' in optionsConfirmParams:
|
if '&submitDM=' in optionsConfirmParams:
|
||||||
if debug:
|
if debug:
|
||||||
print('Sending DM to ' + optionsActor)
|
print('Sending DM to ' + optionsActor)
|
||||||
|
|
@ -1771,7 +1828,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# snooze button on person option screen
|
# person options screen, snooze button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitSnooze=' in optionsConfirmParams:
|
if '&submitSnooze=' in optionsConfirmParams:
|
||||||
usersPath = path.split('/personoptions')[0]
|
usersPath = path.split('/personoptions')[0]
|
||||||
thisActor = httpPrefix + '://' + domainFull + usersPath
|
thisActor = httpPrefix + '://' + domainFull + usersPath
|
||||||
|
|
@ -1792,7 +1850,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# unsnooze button on person option screen
|
# person options screen, unsnooze button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitUnSnooze=' in optionsConfirmParams:
|
if '&submitUnSnooze=' in optionsConfirmParams:
|
||||||
usersPath = path.split('/personoptions')[0]
|
usersPath = path.split('/personoptions')[0]
|
||||||
thisActor = httpPrefix + '://' + domainFull + usersPath
|
thisActor = httpPrefix + '://' + domainFull + usersPath
|
||||||
|
|
@ -1813,7 +1872,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# report button on person option screen
|
# person options screen, report button
|
||||||
|
# See htmlPersonOptions
|
||||||
if '&submitReport=' in optionsConfirmParams:
|
if '&submitReport=' in optionsConfirmParams:
|
||||||
if debug:
|
if debug:
|
||||||
print('Reporting ' + optionsActor)
|
print('Reporting ' + optionsActor)
|
||||||
|
|
@ -3309,10 +3369,95 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if fields['displayNickname'] != actorJson['name']:
|
if fields['displayNickname'] != actorJson['name']:
|
||||||
actorJson['name'] = fields['displayNickname']
|
actorJson['name'] = fields['displayNickname']
|
||||||
actorChanged = True
|
actorChanged = True
|
||||||
|
|
||||||
|
# change media instance status
|
||||||
|
if fields.get('mediaInstance'):
|
||||||
|
self.server.mediaInstance = False
|
||||||
|
self.server.defaultTimeline = 'inbox'
|
||||||
|
if fields['mediaInstance'] == 'on':
|
||||||
|
self.server.mediaInstance = True
|
||||||
|
self.server.blogsInstance = False
|
||||||
|
self.server.newsInstance = False
|
||||||
|
self.server.defaultTimeline = 'tlmedia'
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"mediaInstance",
|
||||||
|
self.server.mediaInstance)
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"blogsInstance",
|
||||||
|
self.server.blogsInstance)
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"newsInstance",
|
||||||
|
self.server.newsInstance)
|
||||||
|
else:
|
||||||
|
if self.server.mediaInstance:
|
||||||
|
self.server.mediaInstance = False
|
||||||
|
self.server.defaultTimeline = 'inbox'
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"mediaInstance",
|
||||||
|
self.server.mediaInstance)
|
||||||
|
|
||||||
|
# change news instance status
|
||||||
|
if fields.get('newsInstance'):
|
||||||
|
self.server.newsInstance = False
|
||||||
|
self.server.defaultTimeline = 'inbox'
|
||||||
|
if fields['newsInstance'] == 'on':
|
||||||
|
self.server.newsInstance = True
|
||||||
|
self.server.blogsInstance = False
|
||||||
|
self.server.mediaInstance = False
|
||||||
|
self.server.defaultTimeline = 'tlnews'
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"mediaInstance",
|
||||||
|
self.server.mediaInstance)
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"blogsInstance",
|
||||||
|
self.server.blogsInstance)
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"newsInstance",
|
||||||
|
self.server.newsInstance)
|
||||||
|
else:
|
||||||
|
if self.server.newsInstance:
|
||||||
|
self.server.newsInstance = False
|
||||||
|
self.server.defaultTimeline = 'inbox'
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"newsInstance",
|
||||||
|
self.server.mediaInstance)
|
||||||
|
|
||||||
|
# change blog instance status
|
||||||
|
if fields.get('blogsInstance'):
|
||||||
|
self.server.blogsInstance = False
|
||||||
|
self.server.defaultTimeline = 'inbox'
|
||||||
|
if fields['blogsInstance'] == 'on':
|
||||||
|
self.server.blogsInstance = True
|
||||||
|
self.server.mediaInstance = False
|
||||||
|
self.server.newsInstance = False
|
||||||
|
self.server.defaultTimeline = 'tlblogs'
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"blogsInstance",
|
||||||
|
self.server.blogsInstance)
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"mediaInstance",
|
||||||
|
self.server.mediaInstance)
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"newsInstance",
|
||||||
|
self.server.newsInstance)
|
||||||
|
else:
|
||||||
|
if self.server.blogsInstance:
|
||||||
|
self.server.blogsInstance = False
|
||||||
|
self.server.defaultTimeline = 'inbox'
|
||||||
|
setConfigParam(baseDir,
|
||||||
|
"blogsInstance",
|
||||||
|
self.server.blogsInstance)
|
||||||
|
|
||||||
|
# change theme
|
||||||
if fields.get('themeDropdown'):
|
if fields.get('themeDropdown'):
|
||||||
setTheme(baseDir,
|
setTheme(baseDir,
|
||||||
fields['themeDropdown'],
|
fields['themeDropdown'],
|
||||||
domain)
|
domain)
|
||||||
|
setNewsAvatar(baseDir,
|
||||||
|
fields['themeDropdown'],
|
||||||
|
httpPrefix,
|
||||||
|
domain,
|
||||||
|
domainFull)
|
||||||
|
|
||||||
# change email address
|
# change email address
|
||||||
currentEmailAddress = getEmailAddress(actorJson)
|
currentEmailAddress = getEmailAddress(actorJson)
|
||||||
|
|
@ -3653,84 +3798,6 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if currTheme:
|
if currTheme:
|
||||||
setTheme(baseDir, currTheme, domain)
|
setTheme(baseDir, currTheme, domain)
|
||||||
|
|
||||||
# change media instance status
|
|
||||||
if fields.get('mediaInstance'):
|
|
||||||
self.server.mediaInstance = False
|
|
||||||
self.server.defaultTimeline = 'inbox'
|
|
||||||
if fields['mediaInstance'] == 'on':
|
|
||||||
self.server.mediaInstance = True
|
|
||||||
self.server.blogsInstance = False
|
|
||||||
self.server.newsInstance = False
|
|
||||||
self.server.defaultTimeline = 'tlmedia'
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"mediaInstance",
|
|
||||||
self.server.mediaInstance)
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"blogsInstance",
|
|
||||||
self.server.blogsInstance)
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"newsInstance",
|
|
||||||
self.server.newsInstance)
|
|
||||||
else:
|
|
||||||
if self.server.mediaInstance:
|
|
||||||
self.server.mediaInstance = False
|
|
||||||
self.server.defaultTimeline = 'inbox'
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"mediaInstance",
|
|
||||||
self.server.mediaInstance)
|
|
||||||
|
|
||||||
# change news instance status
|
|
||||||
if fields.get('newsInstance'):
|
|
||||||
self.server.newsInstance = False
|
|
||||||
self.server.defaultTimeline = 'inbox'
|
|
||||||
if fields['newsInstance'] == 'on':
|
|
||||||
self.server.newsInstance = True
|
|
||||||
self.server.blogsInstance = False
|
|
||||||
self.server.mediaInstance = False
|
|
||||||
self.server.defaultTimeline = 'tlnews'
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"mediaInstance",
|
|
||||||
self.server.mediaInstance)
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"blogsInstance",
|
|
||||||
self.server.blogsInstance)
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"newsInstance",
|
|
||||||
self.server.newsInstance)
|
|
||||||
else:
|
|
||||||
if self.server.newsInstance:
|
|
||||||
self.server.newsInstance = False
|
|
||||||
self.server.defaultTimeline = 'inbox'
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"newsInstance",
|
|
||||||
self.server.mediaInstance)
|
|
||||||
|
|
||||||
# change blog instance status
|
|
||||||
if fields.get('blogsInstance'):
|
|
||||||
self.server.blogsInstance = False
|
|
||||||
self.server.defaultTimeline = 'inbox'
|
|
||||||
if fields['blogsInstance'] == 'on':
|
|
||||||
self.server.blogsInstance = True
|
|
||||||
self.server.mediaInstance = False
|
|
||||||
self.server.newsInstance = False
|
|
||||||
self.server.defaultTimeline = 'tlblogs'
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"blogsInstance",
|
|
||||||
self.server.blogsInstance)
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"mediaInstance",
|
|
||||||
self.server.mediaInstance)
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"newsInstance",
|
|
||||||
self.server.newsInstance)
|
|
||||||
else:
|
|
||||||
if self.server.blogsInstance:
|
|
||||||
self.server.blogsInstance = False
|
|
||||||
self.server.defaultTimeline = 'inbox'
|
|
||||||
setConfigParam(baseDir,
|
|
||||||
"blogsInstance",
|
|
||||||
self.server.blogsInstance)
|
|
||||||
|
|
||||||
# only receive DMs from accounts you follow
|
# only receive DMs from accounts you follow
|
||||||
followDMsFilename = \
|
followDMsFilename = \
|
||||||
baseDir + '/accounts/' + \
|
baseDir + '/accounts/' + \
|
||||||
|
|
@ -4211,7 +4278,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
nickname,
|
nickname,
|
||||||
domain,
|
domain,
|
||||||
port,
|
port,
|
||||||
maxPostsInRSSFeed, 1)
|
maxPostsInRSSFeed, 1,
|
||||||
|
True)
|
||||||
if msg is not None:
|
if msg is not None:
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
self._set_headers('text/xml', len(msg),
|
self._set_headers('text/xml', len(msg),
|
||||||
|
|
@ -4229,6 +4297,66 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
path + ' ' + callingDomain)
|
path + ' ' + callingDomain)
|
||||||
self._404()
|
self._404()
|
||||||
|
|
||||||
|
def _getRSS2site(self, authorized: bool,
|
||||||
|
callingDomain: str, path: str,
|
||||||
|
baseDir: str, httpPrefix: str,
|
||||||
|
domainFull: str, port: int, proxyType: str,
|
||||||
|
translate: {},
|
||||||
|
GETstartTime, GETtimings: {},
|
||||||
|
debug: bool):
|
||||||
|
"""Returns an RSS2 feed for all blogs on this instance
|
||||||
|
"""
|
||||||
|
if not self.server.session:
|
||||||
|
print('Starting new session during RSS request')
|
||||||
|
self.server.session = \
|
||||||
|
createSession(proxyType)
|
||||||
|
if not self.server.session:
|
||||||
|
print('ERROR: GET failed to create session ' +
|
||||||
|
'during RSS request')
|
||||||
|
self._404()
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = ''
|
||||||
|
for subdir, dirs, files in os.walk(baseDir + '/accounts'):
|
||||||
|
for acct in dirs:
|
||||||
|
if '@' not in acct:
|
||||||
|
continue
|
||||||
|
if 'inbox@' in acct or 'news@' in acct:
|
||||||
|
continue
|
||||||
|
nickname = acct.split('@')[0]
|
||||||
|
domain = acct.split('@')[1]
|
||||||
|
msg += \
|
||||||
|
htmlBlogPageRSS2(authorized,
|
||||||
|
self.server.session,
|
||||||
|
baseDir,
|
||||||
|
httpPrefix,
|
||||||
|
self.server.translate,
|
||||||
|
nickname,
|
||||||
|
domain,
|
||||||
|
port,
|
||||||
|
maxPostsInRSSFeed, 1,
|
||||||
|
False)
|
||||||
|
if msg:
|
||||||
|
msg = rss2Header(httpPrefix,
|
||||||
|
'news', domainFull,
|
||||||
|
'Site', translate) + msg + rss2Footer()
|
||||||
|
|
||||||
|
msg = msg.encode('utf-8')
|
||||||
|
self._set_headers('text/xml', len(msg),
|
||||||
|
None, callingDomain)
|
||||||
|
self._write(msg)
|
||||||
|
if debug:
|
||||||
|
print('Sent rss2 feed: ' +
|
||||||
|
path + ' ' + callingDomain)
|
||||||
|
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
||||||
|
'sharedInbox enabled',
|
||||||
|
'blog rss2')
|
||||||
|
return
|
||||||
|
if debug:
|
||||||
|
print('Failed to get rss2 feed: ' +
|
||||||
|
path + ' ' + callingDomain)
|
||||||
|
self._404()
|
||||||
|
|
||||||
def _getNewswireFeed(self, authorized: bool,
|
def _getNewswireFeed(self, authorized: bool,
|
||||||
callingDomain: str, path: str,
|
callingDomain: str, path: str,
|
||||||
baseDir: str, httpPrefix: str,
|
baseDir: str, httpPrefix: str,
|
||||||
|
|
@ -4358,6 +4486,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
PGPfingerprint = getPGPfingerprint(actorJson)
|
PGPfingerprint = getPGPfingerprint(actorJson)
|
||||||
msg = htmlPersonOptions(self.server.translate,
|
msg = htmlPersonOptions(self.server.translate,
|
||||||
baseDir, domain,
|
baseDir, domain,
|
||||||
|
domainFull,
|
||||||
originPathStr,
|
originPathStr,
|
||||||
optionsActor,
|
optionsActor,
|
||||||
optionsProfileUrl,
|
optionsProfileUrl,
|
||||||
|
|
@ -5870,6 +5999,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.personCache,
|
self.server.personCache,
|
||||||
YTReplacementDomain,
|
YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
|
self.server.newswire,
|
||||||
actorJson['roles'],
|
actorJson['roles'],
|
||||||
None, None)
|
None, None)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
|
|
@ -5943,6 +6073,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.personCache,
|
self.server.personCache,
|
||||||
YTReplacementDomain,
|
YTReplacementDomain,
|
||||||
showPublishedDateOnly,
|
showPublishedDateOnly,
|
||||||
|
self.server.newswire,
|
||||||
actorJson['skills'],
|
actorJson['skills'],
|
||||||
None, None)
|
None, None)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
|
|
@ -7405,6 +7536,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.personCache,
|
self.server.personCache,
|
||||||
self.server.YTReplacementDomain,
|
self.server.YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
|
self.server.newswire,
|
||||||
shares,
|
shares,
|
||||||
pageNumber, sharesPerPage)
|
pageNumber, sharesPerPage)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
|
|
@ -7492,6 +7624,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.personCache,
|
self.server.personCache,
|
||||||
self.server.YTReplacementDomain,
|
self.server.YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
|
self.server.newswire,
|
||||||
following,
|
following,
|
||||||
pageNumber,
|
pageNumber,
|
||||||
followsPerPage).encode('utf-8')
|
followsPerPage).encode('utf-8')
|
||||||
|
|
@ -7579,6 +7712,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.personCache,
|
self.server.personCache,
|
||||||
self.server.YTReplacementDomain,
|
self.server.YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
|
self.server.newswire,
|
||||||
followers,
|
followers,
|
||||||
pageNumber,
|
pageNumber,
|
||||||
followsPerPage).encode('utf-8')
|
followsPerPage).encode('utf-8')
|
||||||
|
|
@ -7641,6 +7775,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.personCache,
|
self.server.personCache,
|
||||||
self.server.YTReplacementDomain,
|
self.server.YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
|
self.server.newswire,
|
||||||
None, None).encode('utf-8')
|
None, None).encode('utf-8')
|
||||||
self._set_headers('text/html', len(msg),
|
self._set_headers('text/html', len(msg),
|
||||||
cookie, callingDomain)
|
cookie, callingDomain)
|
||||||
|
|
@ -7748,23 +7883,28 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
divertToLoginScreen = False
|
divertToLoginScreen = False
|
||||||
|
|
||||||
if divertToLoginScreen and not authorized:
|
if divertToLoginScreen and not authorized:
|
||||||
if debug:
|
divertPath = '/login'
|
||||||
|
if self.server.newsInstance:
|
||||||
|
# for news instances if not logged in then show the
|
||||||
|
# front page
|
||||||
|
divertPath = '/users/news'
|
||||||
|
# if debug:
|
||||||
print('DEBUG: divertToLoginScreen=' +
|
print('DEBUG: divertToLoginScreen=' +
|
||||||
str(divertToLoginScreen))
|
str(divertToLoginScreen))
|
||||||
print('DEBUG: authorized=' + str(authorized))
|
print('DEBUG: authorized=' + str(authorized))
|
||||||
print('DEBUG: path=' + path)
|
print('DEBUG: path=' + path)
|
||||||
if callingDomain.endswith('.onion') and onionDomain:
|
if callingDomain.endswith('.onion') and onionDomain:
|
||||||
self._redirect_headers('http://' +
|
self._redirect_headers('http://' +
|
||||||
onionDomain + '/login',
|
onionDomain + divertPath,
|
||||||
None, callingDomain)
|
None, callingDomain)
|
||||||
elif callingDomain.endswith('.i2p') and i2pDomain:
|
elif callingDomain.endswith('.i2p') and i2pDomain:
|
||||||
self._redirect_headers('http://' +
|
self._redirect_headers('http://' +
|
||||||
i2pDomain + '/login',
|
i2pDomain + divertPath,
|
||||||
None, callingDomain)
|
None, callingDomain)
|
||||||
else:
|
else:
|
||||||
self._redirect_headers(httpPrefix + '://' +
|
self._redirect_headers(httpPrefix + '://' +
|
||||||
domainFull +
|
domainFull +
|
||||||
'/login', None, callingDomain)
|
divertPath, None, callingDomain)
|
||||||
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
||||||
'robots txt',
|
'robots txt',
|
||||||
'show login screen')
|
'show login screen')
|
||||||
|
|
@ -8328,11 +8468,31 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
'_mastoApi(callingDomain)')
|
'_mastoApi(callingDomain)')
|
||||||
|
|
||||||
if self.path == '/logout':
|
if self.path == '/logout':
|
||||||
|
if not self.server.newsInstance:
|
||||||
msg = \
|
msg = \
|
||||||
htmlLogin(self.server.translate,
|
htmlLogin(self.server.translate,
|
||||||
self.server.baseDir, False).encode('utf-8')
|
self.server.baseDir, False).encode('utf-8')
|
||||||
self._logout_headers('text/html', len(msg), callingDomain)
|
self._logout_headers('text/html', len(msg), callingDomain)
|
||||||
self._write(msg)
|
self._write(msg)
|
||||||
|
else:
|
||||||
|
if callingDomain.endswith('.onion') and \
|
||||||
|
self.server.onionDomain:
|
||||||
|
self._logout_redirect('http://' +
|
||||||
|
self.server.onionDomain +
|
||||||
|
'/users/news', None,
|
||||||
|
callingDomain)
|
||||||
|
elif (callingDomain.endswith('.i2p') and
|
||||||
|
self.server.i2pDomain):
|
||||||
|
self._logout_redirect('http://' +
|
||||||
|
self.server.i2pDomain +
|
||||||
|
'/users/news', None,
|
||||||
|
callingDomain)
|
||||||
|
else:
|
||||||
|
self._logout_redirect(self.server.httpPrefix +
|
||||||
|
'://' +
|
||||||
|
self.server.domainFull +
|
||||||
|
'/users/news',
|
||||||
|
None, callingDomain)
|
||||||
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
||||||
'_nodeinfo(callingDomain)',
|
'_nodeinfo(callingDomain)',
|
||||||
'logout')
|
'logout')
|
||||||
|
|
@ -8465,6 +8625,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
# RSS 2.0
|
# RSS 2.0
|
||||||
if self.path.startswith('/blog/') and \
|
if self.path.startswith('/blog/') and \
|
||||||
self.path.endswith('/rss.xml'):
|
self.path.endswith('/rss.xml'):
|
||||||
|
if not self.path == '/blog/rss.xml':
|
||||||
self._getRSS2feed(authorized,
|
self._getRSS2feed(authorized,
|
||||||
callingDomain, self.path,
|
callingDomain, self.path,
|
||||||
self.server.baseDir,
|
self.server.baseDir,
|
||||||
|
|
@ -8474,6 +8635,17 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.proxyType,
|
self.server.proxyType,
|
||||||
GETstartTime, GETtimings,
|
GETstartTime, GETtimings,
|
||||||
self.server.debug)
|
self.server.debug)
|
||||||
|
else:
|
||||||
|
self._getRSS2site(authorized,
|
||||||
|
callingDomain, self.path,
|
||||||
|
self.server.baseDir,
|
||||||
|
self.server.httpPrefix,
|
||||||
|
self.server.domainFull,
|
||||||
|
self.server.port,
|
||||||
|
self.server.proxyType,
|
||||||
|
self.server.translate,
|
||||||
|
GETstartTime, GETtimings,
|
||||||
|
self.server.debug)
|
||||||
return
|
return
|
||||||
|
|
||||||
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
||||||
|
|
@ -9067,8 +9239,11 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
'GET busy time',
|
'GET busy time',
|
||||||
'permitted directory')
|
'permitted directory')
|
||||||
|
|
||||||
if self.path.startswith('/login') or \
|
# show the login screen
|
||||||
(self.path == '/' and not authorized):
|
if (self.path.startswith('/login') or
|
||||||
|
(self.path == '/' and
|
||||||
|
not authorized and
|
||||||
|
not self.server.newsInstance)):
|
||||||
# request basic auth
|
# request basic auth
|
||||||
msg = htmlLogin(self.server.translate,
|
msg = htmlLogin(self.server.translate,
|
||||||
self.server.baseDir).encode('utf-8')
|
self.server.baseDir).encode('utf-8')
|
||||||
|
|
@ -9080,6 +9255,33 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
'login shown')
|
'login shown')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# show the news front page
|
||||||
|
if self.path == '/' and \
|
||||||
|
not authorized and \
|
||||||
|
self.server.newsInstance:
|
||||||
|
if callingDomain.endswith('.onion') and \
|
||||||
|
self.server.onionDomain:
|
||||||
|
self._logout_redirect('http://' +
|
||||||
|
self.server.onionDomain +
|
||||||
|
'/users/news', None,
|
||||||
|
callingDomain)
|
||||||
|
elif (callingDomain.endswith('.i2p') and
|
||||||
|
self.server.i2pDomain):
|
||||||
|
self._logout_redirect('http://' +
|
||||||
|
self.server.i2pDomain +
|
||||||
|
'/users/news', None,
|
||||||
|
callingDomain)
|
||||||
|
else:
|
||||||
|
self._logout_redirect(self.server.httpPrefix +
|
||||||
|
'://' +
|
||||||
|
self.server.domainFull +
|
||||||
|
'/users/news',
|
||||||
|
None, callingDomain)
|
||||||
|
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
||||||
|
'permitted directory',
|
||||||
|
'news front page shown')
|
||||||
|
return
|
||||||
|
|
||||||
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
||||||
'permitted directory',
|
'permitted directory',
|
||||||
'login shown done')
|
'login shown done')
|
||||||
|
|
@ -11558,6 +11760,11 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if containsInvalidChars(messageBytes.decode("utf-8")):
|
||||||
|
self._400()
|
||||||
|
self.server.POSTbusy = False
|
||||||
|
return
|
||||||
|
|
||||||
# convert the raw bytes to json
|
# convert the raw bytes to json
|
||||||
messageJson = json.loads(messageBytes)
|
messageJson = json.loads(messageBytes)
|
||||||
|
|
||||||
|
|
@ -11744,7 +11951,9 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
|
||||||
tokensLookup[token] = nickname
|
tokensLookup[token] = nickname
|
||||||
|
|
||||||
|
|
||||||
def runDaemon(showPublishedDateOnly: bool,
|
def runDaemon(maxNewswireFeedSizeKb: int,
|
||||||
|
maxNewswirePostsPerSource: int,
|
||||||
|
showPublishedDateOnly: bool,
|
||||||
votingTimeMins: int,
|
votingTimeMins: int,
|
||||||
positiveVoting: bool,
|
positiveVoting: bool,
|
||||||
newswireVotesThreshold: int,
|
newswireVotesThreshold: int,
|
||||||
|
|
@ -11864,6 +12073,15 @@ def runDaemon(showPublishedDateOnly: bool,
|
||||||
# number of votes needed to remove a newswire item from the news timeline
|
# number of votes needed to remove a newswire item from the news timeline
|
||||||
# or if positive voting is anabled to add the item to the news timeline
|
# or if positive voting is anabled to add the item to the news timeline
|
||||||
httpd.newswireVotesThreshold = newswireVotesThreshold
|
httpd.newswireVotesThreshold = newswireVotesThreshold
|
||||||
|
# maximum overall size of an rss/atom feed read by the newswire daemon
|
||||||
|
# If the feed is too large then this is probably a DoS attempt
|
||||||
|
httpd.maxNewswireFeedSizeKb = maxNewswireFeedSizeKb
|
||||||
|
|
||||||
|
# For each newswire source (account or rss feed)
|
||||||
|
# this is the maximum number of posts to show for each.
|
||||||
|
# This avoids one or two sources from dominating the news,
|
||||||
|
# and also prevents big feeds from slowing down page load times
|
||||||
|
httpd.maxNewswirePostsPerSource = maxNewswirePostsPerSource
|
||||||
|
|
||||||
# Show only the date at the bottom of posts, and not the time
|
# Show only the date at the bottom of posts, and not the time
|
||||||
httpd.showPublishedDateOnly = showPublishedDateOnly
|
httpd.showPublishedDateOnly = showPublishedDateOnly
|
||||||
|
|
@ -11929,6 +12147,16 @@ def runDaemon(showPublishedDateOnly: bool,
|
||||||
print('Creating news inbox: news@' + domain)
|
print('Creating news inbox: news@' + domain)
|
||||||
createNewsInbox(baseDir, domain, port, httpPrefix)
|
createNewsInbox(baseDir, domain, port, httpPrefix)
|
||||||
|
|
||||||
|
# set the avatar for the news account
|
||||||
|
themeName = getConfigParam(baseDir, 'theme')
|
||||||
|
if not themeName:
|
||||||
|
themeName = 'default'
|
||||||
|
setNewsAvatar(baseDir,
|
||||||
|
themeName,
|
||||||
|
httpPrefix,
|
||||||
|
domain,
|
||||||
|
httpd.domainFull)
|
||||||
|
|
||||||
if not os.path.isdir(baseDir + '/cache'):
|
if not os.path.isdir(baseDir + '/cache'):
|
||||||
os.mkdir(baseDir + '/cache')
|
os.mkdir(baseDir + '/cache')
|
||||||
if not os.path.isdir(baseDir + '/cache/actors'):
|
if not os.path.isdir(baseDir + '/cache/actors'):
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,9 @@
|
||||||
--font-size-tox2: 8px;
|
--font-size-tox2: 8px;
|
||||||
--time-color: #aaa;
|
--time-color: #aaa;
|
||||||
--time-vertical-align: 4px;
|
--time-vertical-align: 4px;
|
||||||
|
--publish-button-text: #FFFFFF;
|
||||||
--button-text: #FFFFFF;
|
--button-text: #FFFFFF;
|
||||||
|
--publish-button-background: #999;
|
||||||
--button-background: #999;
|
--button-background: #999;
|
||||||
--button-background-hover: #777;
|
--button-background-hover: #777;
|
||||||
--button-selected: #666;
|
--button-selected: #666;
|
||||||
|
|
@ -50,6 +52,7 @@
|
||||||
--button-selected-highlighted: darkgreen;
|
--button-selected-highlighted: darkgreen;
|
||||||
--button-approve: darkgreen;
|
--button-approve: darkgreen;
|
||||||
--button-deny: darkred;
|
--button-deny: darkred;
|
||||||
|
--button-width-chars: 10ch;
|
||||||
--button-height: 10px;
|
--button-height: 10px;
|
||||||
--button-height-padding-mobile: 20px;
|
--button-height-padding-mobile: 20px;
|
||||||
--button-height-padding: 10px;
|
--button-height-padding: 10px;
|
||||||
|
|
@ -68,7 +71,7 @@
|
||||||
--quote-font-weight: normal;
|
--quote-font-weight: normal;
|
||||||
--quote-font-size: 120%;
|
--quote-font-size: 120%;
|
||||||
--line-spacing: 130%;
|
--line-spacing: 130%;
|
||||||
--line-spacing-newswire: 100%;
|
--line-spacing-newswire: 120%;
|
||||||
--newswire-item-moderated-color: white;
|
--newswire-item-moderated-color: white;
|
||||||
--newswire-date-moderated-color: white;
|
--newswire-date-moderated-color: white;
|
||||||
--column-left-width: 10vw;
|
--column-left-width: 10vw;
|
||||||
|
|
@ -81,9 +84,13 @@
|
||||||
--column-left-icon-size: 20%;
|
--column-left-icon-size: 20%;
|
||||||
--column-left-icon-size-mobile: 10%;
|
--column-left-icon-size-mobile: 10%;
|
||||||
--column-left-image-width-mobile: 40vw;
|
--column-left-image-width-mobile: 40vw;
|
||||||
|
--column-right-image-width-mobile: 100vw;
|
||||||
--column-right-icon-size: 20%;
|
--column-right-icon-size: 20%;
|
||||||
|
--column-right-icon-size-mobile: 10%;
|
||||||
--newswire-date-color: white;
|
--newswire-date-color: white;
|
||||||
--newswire-voted-background-color: black;
|
--newswire-voted-background-color: black;
|
||||||
|
--login-button-color: #2965;
|
||||||
|
--login-button-fg-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
|
@ -577,8 +584,8 @@ input[type=submit] {
|
||||||
}
|
}
|
||||||
|
|
||||||
.loginButton {
|
.loginButton {
|
||||||
background-color: #2965;
|
background-color: var(--login-button-color);
|
||||||
color: #000;
|
color: var(--login-button-fg-color);
|
||||||
float: none;
|
float: none;
|
||||||
margin: 0px 10px;
|
margin: 0px 10px;
|
||||||
padding: 12px 40px;
|
padding: 12px 40px;
|
||||||
|
|
@ -1224,11 +1231,27 @@ aside .toggle-inside li {
|
||||||
padding: var(--button-height-padding);
|
padding: var(--button-height-padding);
|
||||||
width: 10%;
|
width: 10%;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
min-width: 10ch;
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
.publishbtn {
|
||||||
|
border-radius: var(--button-corner-radius);
|
||||||
|
background-color: var(--publish-button-background);
|
||||||
|
border: none;
|
||||||
|
color: var(--publish-button-text);
|
||||||
|
text-align: center;
|
||||||
|
font-size: var(--font-size-header);
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
padding: var(--button-height-padding);
|
||||||
|
width: 10%;
|
||||||
|
max-width: 200px;
|
||||||
|
min-width: var(--button-width-chars);
|
||||||
|
transition: all 0.5s;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: -20px 0px;
|
||||||
|
}
|
||||||
.buttonhighlighted {
|
.buttonhighlighted {
|
||||||
border-radius: var(--button-corner-radius);
|
border-radius: var(--button-corner-radius);
|
||||||
background-color: var(--button-highlighted);
|
background-color: var(--button-highlighted);
|
||||||
|
|
@ -1240,7 +1263,7 @@ aside .toggle-inside li {
|
||||||
padding: var(--button-height-padding);
|
padding: var(--button-height-padding);
|
||||||
width: 10%;
|
width: 10%;
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
min-width: 10ch;
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
|
|
@ -1256,7 +1279,7 @@ aside .toggle-inside li {
|
||||||
padding: var(--button-height-padding);
|
padding: var(--button-height-padding);
|
||||||
width: 10%;
|
width: 10%;
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
min-width: 10ch;
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
|
|
@ -1272,7 +1295,7 @@ aside .toggle-inside li {
|
||||||
padding: var(--button-height-padding);
|
padding: var(--button-height-padding);
|
||||||
width: 10%;
|
width: 10%;
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
min-width: 10ch;
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
|
|
@ -1566,13 +1589,14 @@ aside .toggle-inside li {
|
||||||
}
|
}
|
||||||
.rightColEditImage {
|
.rightColEditImage {
|
||||||
background: var(--main-bg-color);
|
background: var(--main-bg-color);
|
||||||
width: var(--column-right-icon-size);
|
width: var(--column-right-icon-size-mobile);
|
||||||
float: right;
|
float: right;
|
||||||
margin: 20px 0px;
|
margin: 20px 0px;
|
||||||
}
|
}
|
||||||
.rightColImg {
|
.rightColImg {
|
||||||
background: var(--main-bg-color);
|
background: var(--main-bg-color);
|
||||||
width: 100vw;
|
width: var(--column-right-image-width-mobile);
|
||||||
|
float: right;
|
||||||
margin: 0 0;
|
margin: 0 0;
|
||||||
padding: 0 0;
|
padding: 0 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1790,7 +1814,23 @@ aside .toggle-inside li {
|
||||||
padding: var(--button-height-padding-mobile);
|
padding: var(--button-height-padding-mobile);
|
||||||
width: 20%;
|
width: 20%;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
min-width: 10ch;
|
min-width: var(--button-width-chars);
|
||||||
|
transition: all 0.5s;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
.publishbtn {
|
||||||
|
border-radius: var(--button-corner-radius);
|
||||||
|
background-color: var(--publish-button-background);
|
||||||
|
border: none;
|
||||||
|
color: var(--publish-button-text);
|
||||||
|
text-align: center;
|
||||||
|
font-size: var(--font-size-newswire-mobile);
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
padding: var(--button-height-padding-mobile);
|
||||||
|
width: 20%;
|
||||||
|
max-width: 400px;
|
||||||
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
|
|
@ -1806,7 +1846,7 @@ aside .toggle-inside li {
|
||||||
padding: var(--button-height-padding-mobile);
|
padding: var(--button-height-padding-mobile);
|
||||||
width: 20%;
|
width: 20%;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
min-width: 10ch;
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
|
|
@ -1822,7 +1862,7 @@ aside .toggle-inside li {
|
||||||
padding: var(--button-height-padding-mobile);
|
padding: var(--button-height-padding-mobile);
|
||||||
width: 20%;
|
width: 20%;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
min-width: 10ch;
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
|
|
@ -1838,7 +1878,7 @@ aside .toggle-inside li {
|
||||||
padding: var(--button-height-padding-mobile);
|
padding: var(--button-height-padding-mobile);
|
||||||
width: 20%;
|
width: 20%;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
min-width: 10ch;
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
|
|
|
||||||
26
epicyon.py
|
|
@ -112,6 +112,14 @@ parser.add_argument('--i2pDomain', dest='i2pDomain', type=str,
|
||||||
parser.add_argument('-p', '--port', dest='port', type=int,
|
parser.add_argument('-p', '--port', dest='port', type=int,
|
||||||
default=None,
|
default=None,
|
||||||
help='Port number to run on')
|
help='Port number to run on')
|
||||||
|
parser.add_argument('--postsPerSource',
|
||||||
|
dest='maxNewswirePostsPerSource', type=int,
|
||||||
|
default=5,
|
||||||
|
help='Maximum newswire posts per feed or account')
|
||||||
|
parser.add_argument('--maxFeedSize',
|
||||||
|
dest='maxNewswireFeedSizeKb', type=int,
|
||||||
|
default=2048,
|
||||||
|
help='Maximum newswire rss/atom feed size in K')
|
||||||
parser.add_argument('--postcache', dest='maxRecentPosts', type=int,
|
parser.add_argument('--postcache', dest='maxRecentPosts', type=int,
|
||||||
default=512,
|
default=512,
|
||||||
help='The maximum number of recent posts to store in RAM')
|
help='The maximum number of recent posts to store in RAM')
|
||||||
|
|
@ -1925,6 +1933,20 @@ dateonly = getConfigParam(baseDir, 'dateonly')
|
||||||
if dateonly:
|
if dateonly:
|
||||||
args.dateonly = dateonly
|
args.dateonly = dateonly
|
||||||
|
|
||||||
|
# set the maximum number of newswire posts per account or rss feed
|
||||||
|
maxNewswirePostsPerSource = \
|
||||||
|
getConfigParam(baseDir, 'maxNewswirePostsPerSource')
|
||||||
|
if maxNewswirePostsPerSource:
|
||||||
|
if maxNewswirePostsPerSource.isdigit():
|
||||||
|
args.maxNewswirePostsPerSource = maxNewswirePostsPerSource
|
||||||
|
|
||||||
|
# set the maximum size of a newswire rss/atom feed in Kilobytes
|
||||||
|
maxNewswireFeedSizeKb = \
|
||||||
|
getConfigParam(baseDir, 'maxNewswireFeedSizeKb')
|
||||||
|
if maxNewswireFeedSizeKb:
|
||||||
|
if maxNewswireFeedSizeKb.isdigit():
|
||||||
|
args.maxNewswireFeedSizeKb = maxNewswireFeedSizeKb
|
||||||
|
|
||||||
YTDomain = getConfigParam(baseDir, 'youtubedomain')
|
YTDomain = getConfigParam(baseDir, 'youtubedomain')
|
||||||
if YTDomain:
|
if YTDomain:
|
||||||
if '://' in YTDomain:
|
if '://' in YTDomain:
|
||||||
|
|
@ -1938,7 +1960,9 @@ if setTheme(baseDir, themeName, domain):
|
||||||
print('Theme set to ' + themeName)
|
print('Theme set to ' + themeName)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
runDaemon(args.dateonly,
|
runDaemon(args.maxNewswireFeedSizeKb,
|
||||||
|
args.maxNewswirePostsPerSource,
|
||||||
|
args.dateonly,
|
||||||
args.votingtime,
|
args.votingtime,
|
||||||
args.positivevoting,
|
args.positivevoting,
|
||||||
args.minimumvotes,
|
args.minimumvotes,
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 978 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 219 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 95 KiB |
|
|
@ -202,9 +202,8 @@ def mergeWithPreviousNewswire(oldNewswire: {}, newNewswire: {}) -> None:
|
||||||
for published, fields in oldNewswire.items():
|
for published, fields in oldNewswire.items():
|
||||||
if not newNewswire.get(published):
|
if not newNewswire.get(published):
|
||||||
continue
|
continue
|
||||||
newNewswire[published][1] = fields[1]
|
for i in range(1, 5):
|
||||||
newNewswire[published][2] = fields[2]
|
newNewswire[published][i] = fields[i]
|
||||||
newNewswire[published][3] = fields[3]
|
|
||||||
|
|
||||||
|
|
||||||
def runNewswireDaemon(baseDir: str, httpd,
|
def runNewswireDaemon(baseDir: str, httpd,
|
||||||
|
|
@ -226,7 +225,10 @@ def runNewswireDaemon(baseDir: str, httpd,
|
||||||
# try to update the feeds
|
# try to update the feeds
|
||||||
newNewswire = None
|
newNewswire = None
|
||||||
try:
|
try:
|
||||||
newNewswire = getDictFromNewswire(httpd.session, baseDir)
|
newNewswire = \
|
||||||
|
getDictFromNewswire(httpd.session, baseDir,
|
||||||
|
httpd.maxNewswirePostsPerSource,
|
||||||
|
httpd.maxNewswireFeedSizeKb)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('WARN: unable to update newswire ' + str(e))
|
print('WARN: unable to update newswire ' + str(e))
|
||||||
time.sleep(120)
|
time.sleep(120)
|
||||||
|
|
|
||||||
88
newswire.py
|
|
@ -16,6 +16,8 @@ from utils import locatePost
|
||||||
from utils import loadJson
|
from utils import loadJson
|
||||||
from utils import saveJson
|
from utils import saveJson
|
||||||
from utils import isSuspended
|
from utils import isSuspended
|
||||||
|
from utils import containsInvalidChars
|
||||||
|
from blocking import isBlockedDomain
|
||||||
|
|
||||||
|
|
||||||
def rss2Header(httpPrefix: str,
|
def rss2Header(httpPrefix: str,
|
||||||
|
|
@ -26,14 +28,17 @@ def rss2Header(httpPrefix: str,
|
||||||
rssStr = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
|
rssStr = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
|
||||||
rssStr += "<rss version=\"2.0\">"
|
rssStr += "<rss version=\"2.0\">"
|
||||||
rssStr += '<channel>'
|
rssStr += '<channel>'
|
||||||
|
|
||||||
if title.startswith('News'):
|
if title.startswith('News'):
|
||||||
rssStr += ' <title>Newswire</title>'
|
rssStr += ' <title>Newswire</title>'
|
||||||
else:
|
|
||||||
rssStr += ' <title>' + translate[title] + '</title>'
|
|
||||||
if title.startswith('News'):
|
|
||||||
rssStr += ' <link>' + httpPrefix + '://' + domainFull + \
|
rssStr += ' <link>' + httpPrefix + '://' + domainFull + \
|
||||||
'/newswire.xml' + '</link>'
|
'/newswire.xml' + '</link>'
|
||||||
|
elif title.startswith('Site'):
|
||||||
|
rssStr += ' <title>' + domainFull + '</title>'
|
||||||
|
rssStr += ' <link>' + httpPrefix + '://' + domainFull + \
|
||||||
|
'/blog/rss.xml' + '</link>'
|
||||||
else:
|
else:
|
||||||
|
rssStr += ' <title>' + translate[title] + '</title>'
|
||||||
rssStr += ' <link>' + httpPrefix + '://' + domainFull + \
|
rssStr += ' <link>' + httpPrefix + '://' + domainFull + \
|
||||||
'/users/' + nickname + '/rss.xml' + '</link>'
|
'/users/' + nickname + '/rss.xml' + '</link>'
|
||||||
return rssStr
|
return rssStr
|
||||||
|
|
@ -47,13 +52,15 @@ def rss2Footer() -> str:
|
||||||
return rssStr
|
return rssStr
|
||||||
|
|
||||||
|
|
||||||
def xml2StrToDict(xmlStr: str, moderated: bool) -> {}:
|
def xml2StrToDict(baseDir: str, xmlStr: str, moderated: bool,
|
||||||
|
maxPostsPerSource: int) -> {}:
|
||||||
"""Converts an xml 2.0 string to a dictionary
|
"""Converts an xml 2.0 string to a dictionary
|
||||||
"""
|
"""
|
||||||
if '<item>' not in xmlStr:
|
if '<item>' not in xmlStr:
|
||||||
return {}
|
return {}
|
||||||
result = {}
|
result = {}
|
||||||
rssItems = xmlStr.split('<item>')
|
rssItems = xmlStr.split('<item>')
|
||||||
|
postCtr = 0
|
||||||
for rssItem in rssItems:
|
for rssItem in rssItems:
|
||||||
if '<title>' not in rssItem:
|
if '<title>' not in rssItem:
|
||||||
continue
|
continue
|
||||||
|
|
@ -75,6 +82,13 @@ def xml2StrToDict(xmlStr: str, moderated: bool) -> {}:
|
||||||
description = description.split('</description>')[0]
|
description = description.split('</description>')[0]
|
||||||
link = rssItem.split('<link>')[1]
|
link = rssItem.split('<link>')[1]
|
||||||
link = link.split('</link>')[0]
|
link = link.split('</link>')[0]
|
||||||
|
if '://' not in link:
|
||||||
|
continue
|
||||||
|
domain = link.split('://')[1]
|
||||||
|
if '/' in domain:
|
||||||
|
domain = domain.split('/')[0]
|
||||||
|
if isBlockedDomain(baseDir, domain):
|
||||||
|
continue
|
||||||
pubDate = rssItem.split('<pubDate>')[1]
|
pubDate = rssItem.split('<pubDate>')[1]
|
||||||
pubDate = pubDate.split('</pubDate>')[0]
|
pubDate = pubDate.split('</pubDate>')[0]
|
||||||
parsed = False
|
parsed = False
|
||||||
|
|
@ -86,6 +100,9 @@ def xml2StrToDict(xmlStr: str, moderated: bool) -> {}:
|
||||||
result[str(publishedDate)] = [title, link,
|
result[str(publishedDate)] = [title, link,
|
||||||
votesStatus, postFilename,
|
votesStatus, postFilename,
|
||||||
description, moderated]
|
description, moderated]
|
||||||
|
postCtr += 1
|
||||||
|
if postCtr >= maxPostsPerSource:
|
||||||
|
break
|
||||||
parsed = True
|
parsed = True
|
||||||
except BaseException:
|
except BaseException:
|
||||||
pass
|
pass
|
||||||
|
|
@ -93,7 +110,15 @@ def xml2StrToDict(xmlStr: str, moderated: bool) -> {}:
|
||||||
try:
|
try:
|
||||||
publishedDate = \
|
publishedDate = \
|
||||||
datetime.strptime(pubDate, "%a, %d %b %Y %H:%M:%S UT")
|
datetime.strptime(pubDate, "%a, %d %b %Y %H:%M:%S UT")
|
||||||
result[str(publishedDate) + '+00:00'] = [title, link]
|
postFilename = ''
|
||||||
|
votesStatus = []
|
||||||
|
result[str(publishedDate) + '+00:00'] = \
|
||||||
|
[title, link,
|
||||||
|
votesStatus, postFilename,
|
||||||
|
description, moderated]
|
||||||
|
postCtr += 1
|
||||||
|
if postCtr >= maxPostsPerSource:
|
||||||
|
break
|
||||||
parsed = True
|
parsed = True
|
||||||
except BaseException:
|
except BaseException:
|
||||||
print('WARN: unrecognized RSS date format: ' + pubDate)
|
print('WARN: unrecognized RSS date format: ' + pubDate)
|
||||||
|
|
@ -101,13 +126,15 @@ def xml2StrToDict(xmlStr: str, moderated: bool) -> {}:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def atomFeedToDict(xmlStr: str, moderated: bool) -> {}:
|
def atomFeedToDict(baseDir: str, xmlStr: str, moderated: bool,
|
||||||
|
maxPostsPerSource: int) -> {}:
|
||||||
"""Converts an atom feed string to a dictionary
|
"""Converts an atom feed string to a dictionary
|
||||||
"""
|
"""
|
||||||
if '<entry>' not in xmlStr:
|
if '<entry>' not in xmlStr:
|
||||||
return {}
|
return {}
|
||||||
result = {}
|
result = {}
|
||||||
rssItems = xmlStr.split('<entry>')
|
rssItems = xmlStr.split('<entry>')
|
||||||
|
postCtr = 0
|
||||||
for rssItem in rssItems:
|
for rssItem in rssItems:
|
||||||
if '<title>' not in rssItem:
|
if '<title>' not in rssItem:
|
||||||
continue
|
continue
|
||||||
|
|
@ -129,6 +156,13 @@ def atomFeedToDict(xmlStr: str, moderated: bool) -> {}:
|
||||||
description = description.split('</summary>')[0]
|
description = description.split('</summary>')[0]
|
||||||
link = rssItem.split('<link>')[1]
|
link = rssItem.split('<link>')[1]
|
||||||
link = link.split('</link>')[0]
|
link = link.split('</link>')[0]
|
||||||
|
if '://' not in link:
|
||||||
|
continue
|
||||||
|
domain = link.split('://')[1]
|
||||||
|
if '/' in domain:
|
||||||
|
domain = domain.split('/')[0]
|
||||||
|
if isBlockedDomain(baseDir, domain):
|
||||||
|
continue
|
||||||
pubDate = rssItem.split('<updated>')[1]
|
pubDate = rssItem.split('<updated>')[1]
|
||||||
pubDate = pubDate.split('</updated>')[0]
|
pubDate = pubDate.split('</updated>')[0]
|
||||||
parsed = False
|
parsed = False
|
||||||
|
|
@ -140,6 +174,9 @@ def atomFeedToDict(xmlStr: str, moderated: bool) -> {}:
|
||||||
result[str(publishedDate)] = [title, link,
|
result[str(publishedDate)] = [title, link,
|
||||||
votesStatus, postFilename,
|
votesStatus, postFilename,
|
||||||
description, moderated]
|
description, moderated]
|
||||||
|
postCtr += 1
|
||||||
|
if postCtr >= maxPostsPerSource:
|
||||||
|
break
|
||||||
parsed = True
|
parsed = True
|
||||||
except BaseException:
|
except BaseException:
|
||||||
pass
|
pass
|
||||||
|
|
@ -147,7 +184,15 @@ def atomFeedToDict(xmlStr: str, moderated: bool) -> {}:
|
||||||
try:
|
try:
|
||||||
publishedDate = \
|
publishedDate = \
|
||||||
datetime.strptime(pubDate, "%a, %d %b %Y %H:%M:%S UT")
|
datetime.strptime(pubDate, "%a, %d %b %Y %H:%M:%S UT")
|
||||||
result[str(publishedDate) + '+00:00'] = [title, link]
|
postFilename = ''
|
||||||
|
votesStatus = []
|
||||||
|
result[str(publishedDate) + '+00:00'] = \
|
||||||
|
[title, link,
|
||||||
|
votesStatus, postFilename,
|
||||||
|
description, moderated]
|
||||||
|
postCtr += 1
|
||||||
|
if postCtr >= maxPostsPerSource:
|
||||||
|
break
|
||||||
parsed = True
|
parsed = True
|
||||||
except BaseException:
|
except BaseException:
|
||||||
print('WARN: unrecognized atom feed date format: ' + pubDate)
|
print('WARN: unrecognized atom feed date format: ' + pubDate)
|
||||||
|
|
@ -155,17 +200,20 @@ def atomFeedToDict(xmlStr: str, moderated: bool) -> {}:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def xmlStrToDict(xmlStr: str, moderated: bool) -> {}:
|
def xmlStrToDict(baseDir: str, xmlStr: str, moderated: bool,
|
||||||
|
maxPostsPerSource: int) -> {}:
|
||||||
"""Converts an xml string to a dictionary
|
"""Converts an xml string to a dictionary
|
||||||
"""
|
"""
|
||||||
if 'rss version="2.0"' in xmlStr:
|
if 'rss version="2.0"' in xmlStr:
|
||||||
return xml2StrToDict(xmlStr, moderated)
|
return xml2StrToDict(baseDir, xmlStr, moderated, maxPostsPerSource)
|
||||||
elif 'xmlns="http://www.w3.org/2005/Atom"' in xmlStr:
|
elif 'xmlns="http://www.w3.org/2005/Atom"' in xmlStr:
|
||||||
return atomFeedToDict(xmlStr, moderated)
|
return atomFeedToDict(baseDir, xmlStr, moderated, maxPostsPerSource)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def getRSS(session, url: str, moderated: bool) -> {}:
|
def getRSS(baseDir: str, session, url: str, moderated: bool,
|
||||||
|
maxPostsPerSource: int,
|
||||||
|
maxFeedSizeKb: int) -> {}:
|
||||||
"""Returns an RSS url as a dict
|
"""Returns an RSS url as a dict
|
||||||
"""
|
"""
|
||||||
if not isinstance(url, str):
|
if not isinstance(url, str):
|
||||||
|
|
@ -188,7 +236,13 @@ def getRSS(session, url: str, moderated: bool) -> {}:
|
||||||
print('WARN: no session specified for getRSS')
|
print('WARN: no session specified for getRSS')
|
||||||
try:
|
try:
|
||||||
result = session.get(url, headers=sessionHeaders, params=sessionParams)
|
result = session.get(url, headers=sessionHeaders, params=sessionParams)
|
||||||
return xmlStrToDict(result.text, moderated)
|
if result:
|
||||||
|
if int(len(result.text) / 1024) < maxFeedSizeKb and \
|
||||||
|
not containsInvalidChars(result.text):
|
||||||
|
return xmlStrToDict(baseDir, result.text, moderated,
|
||||||
|
maxPostsPerSource)
|
||||||
|
else:
|
||||||
|
print('WARN: feed is too large: ' + url)
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
print('ERROR: getRSS failed\nurl: ' + str(url) + '\n' +
|
print('ERROR: getRSS failed\nurl: ' + str(url) + '\n' +
|
||||||
'headers: ' + str(sessionHeaders) + '\n' +
|
'headers: ' + str(sessionHeaders) + '\n' +
|
||||||
|
|
@ -365,13 +419,16 @@ def addBlogsToNewswire(baseDir: str, newswire: {},
|
||||||
os.remove(newswireModerationFilename)
|
os.remove(newswireModerationFilename)
|
||||||
|
|
||||||
|
|
||||||
def getDictFromNewswire(session, baseDir: str) -> {}:
|
def getDictFromNewswire(session, baseDir: str,
|
||||||
|
maxPostsPerSource: int, maxFeedSizeKb: int) -> {}:
|
||||||
"""Gets rss feeds as a dictionary from newswire file
|
"""Gets rss feeds as a dictionary from newswire file
|
||||||
"""
|
"""
|
||||||
subscriptionsFilename = baseDir + '/accounts/newswire.txt'
|
subscriptionsFilename = baseDir + '/accounts/newswire.txt'
|
||||||
if not os.path.isfile(subscriptionsFilename):
|
if not os.path.isfile(subscriptionsFilename):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
maxPostsPerSource = 5
|
||||||
|
|
||||||
# add rss feeds
|
# add rss feeds
|
||||||
rssFeed = []
|
rssFeed = []
|
||||||
with open(subscriptionsFilename, 'r') as fp:
|
with open(subscriptionsFilename, 'r') as fp:
|
||||||
|
|
@ -394,12 +451,13 @@ def getDictFromNewswire(session, baseDir: str) -> {}:
|
||||||
moderated = True
|
moderated = True
|
||||||
url = url.replace('*', '').strip()
|
url = url.replace('*', '').strip()
|
||||||
|
|
||||||
itemsList = getRSS(session, url, moderated)
|
itemsList = getRSS(baseDir, session, url, moderated,
|
||||||
|
maxPostsPerSource, maxFeedSizeKb)
|
||||||
for dateStr, item in itemsList.items():
|
for dateStr, item in itemsList.items():
|
||||||
result[dateStr] = item
|
result[dateStr] = item
|
||||||
|
|
||||||
# add blogs from each user account
|
# add blogs from each user account
|
||||||
addBlogsToNewswire(baseDir, result, 5)
|
addBlogsToNewswire(baseDir, result, maxPostsPerSource)
|
||||||
|
|
||||||
# sort into chronological order, latest first
|
# sort into chronological order, latest first
|
||||||
sortedResult = OrderedDict(sorted(result.items(), reverse=True))
|
sortedResult = OrderedDict(sorted(result.items(), reverse=True))
|
||||||
|
|
|
||||||
4
posts.py
|
|
@ -1211,13 +1211,12 @@ def createPublicPost(baseDir: str,
|
||||||
def createBlogPost(baseDir: str,
|
def createBlogPost(baseDir: str,
|
||||||
nickname: str, domain: str, port: int, httpPrefix: str,
|
nickname: str, domain: str, port: int, httpPrefix: str,
|
||||||
content: str, followersOnly: bool, saveToFile: bool,
|
content: str, followersOnly: bool, saveToFile: bool,
|
||||||
clientToServer: bool,
|
clientToServer: bool, commentsEnabled: bool,
|
||||||
attachImageFilename: str, mediaType: str,
|
attachImageFilename: str, mediaType: str,
|
||||||
imageDescription: str, useBlurhash: bool,
|
imageDescription: str, useBlurhash: bool,
|
||||||
inReplyTo=None, inReplyToAtomUri=None, subject=None,
|
inReplyTo=None, inReplyToAtomUri=None, subject=None,
|
||||||
schedulePost=False,
|
schedulePost=False,
|
||||||
eventDate=None, eventTime=None, location=None) -> {}:
|
eventDate=None, eventTime=None, location=None) -> {}:
|
||||||
commentsEnabled = True
|
|
||||||
blog = \
|
blog = \
|
||||||
createPublicPost(baseDir,
|
createPublicPost(baseDir,
|
||||||
nickname, domain, port, httpPrefix,
|
nickname, domain, port, httpPrefix,
|
||||||
|
|
@ -3532,6 +3531,7 @@ def rejectAnnounce(announceFilename: str):
|
||||||
"""
|
"""
|
||||||
if not os.path.isfile(announceFilename + '.reject'):
|
if not os.path.isfile(announceFilename + '.reject'):
|
||||||
rejectAnnounceFile = open(announceFilename + '.reject', "w+")
|
rejectAnnounceFile = open(announceFilename + '.reject', "w+")
|
||||||
|
if rejectAnnounceFile:
|
||||||
rejectAnnounceFile.write('\n')
|
rejectAnnounceFile.write('\n')
|
||||||
rejectAnnounceFile.close()
|
rejectAnnounceFile.close()
|
||||||
|
|
||||||
|
|
|
||||||
6
tests.py
|
|
@ -288,7 +288,7 @@ def createServerAlice(path: str, domain: str, port: int,
|
||||||
onionDomain = None
|
onionDomain = None
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
print('Server running: Alice')
|
print('Server running: Alice')
|
||||||
runDaemon(False, 0, False, 1, False, False, False,
|
runDaemon(1024, 5, False, 0, False, 1, False, False, False,
|
||||||
5, True, True, 'en', __version__,
|
5, True, True, 'en', __version__,
|
||||||
"instanceId", False, path, domain,
|
"instanceId", False, path, domain,
|
||||||
onionDomain, i2pDomain, None, port, port,
|
onionDomain, i2pDomain, None, port, port,
|
||||||
|
|
@ -351,7 +351,7 @@ def createServerBob(path: str, domain: str, port: int,
|
||||||
onionDomain = None
|
onionDomain = None
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
print('Server running: Bob')
|
print('Server running: Bob')
|
||||||
runDaemon(False, 0, False, 1, False, False, False,
|
runDaemon(1024, 5, False, 0, False, 1, False, False, False,
|
||||||
5, True, True, 'en', __version__,
|
5, True, True, 'en', __version__,
|
||||||
"instanceId", False, path, domain,
|
"instanceId", False, path, domain,
|
||||||
onionDomain, i2pDomain, None, port, port,
|
onionDomain, i2pDomain, None, port, port,
|
||||||
|
|
@ -388,7 +388,7 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
|
||||||
onionDomain = None
|
onionDomain = None
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
print('Server running: Eve')
|
print('Server running: Eve')
|
||||||
runDaemon(False, 0, False, 1, False, False, False,
|
runDaemon(1024, 5, False, 0, False, 1, False, False, False,
|
||||||
5, True, True, 'en', __version__,
|
5, True, True, 'en', __version__,
|
||||||
"instanceId", False, path, domain,
|
"instanceId", False, path, domain,
|
||||||
onionDomain, i2pDomain, None, port, port,
|
onionDomain, i2pDomain, None, port, port,
|
||||||
|
|
|
||||||
66
theme.py
|
|
@ -293,6 +293,8 @@ def setThemeIndymedia(baseDir: str):
|
||||||
"hashtag-vertical-spacing3": "100px",
|
"hashtag-vertical-spacing3": "100px",
|
||||||
"hashtag-vertical-spacing4": "150px",
|
"hashtag-vertical-spacing4": "150px",
|
||||||
"button-background-hover": "darkblue",
|
"button-background-hover": "darkblue",
|
||||||
|
"publish-button-background": "#ff9900",
|
||||||
|
"publish-button-text": "#003366",
|
||||||
"button-background": "#003366",
|
"button-background": "#003366",
|
||||||
"button-selected": "blue",
|
"button-selected": "blue",
|
||||||
"calendar-bg-color": "#0f0d10",
|
"calendar-bg-color": "#0f0d10",
|
||||||
|
|
@ -310,7 +312,9 @@ def setThemeIndymedia(baseDir: str):
|
||||||
"column-left-width": "10vw",
|
"column-left-width": "10vw",
|
||||||
"column-center-width": "70vw",
|
"column-center-width": "70vw",
|
||||||
"column-right-width": "20vw",
|
"column-right-width": "20vw",
|
||||||
"column-right-icon-size": "11%"
|
"column-right-icon-size": "11%",
|
||||||
|
"login-button-color": "red",
|
||||||
|
"login-button-fg-color": "white"
|
||||||
}
|
}
|
||||||
setThemeFromDict(baseDir, name, themeParams, bgParams)
|
setThemeFromDict(baseDir, name, themeParams, bgParams)
|
||||||
|
|
||||||
|
|
@ -320,6 +324,7 @@ def setThemeBlue(baseDir: str):
|
||||||
removeTheme(baseDir)
|
removeTheme(baseDir)
|
||||||
setThemeInConfig(baseDir, name)
|
setThemeInConfig(baseDir, name)
|
||||||
themeParams = {
|
themeParams = {
|
||||||
|
"newswire-date-color": "blue",
|
||||||
"font-size-header": "22px",
|
"font-size-header": "22px",
|
||||||
"font-size-header-mobile": "32px",
|
"font-size-header-mobile": "32px",
|
||||||
"font-size": "45px",
|
"font-size": "45px",
|
||||||
|
|
@ -373,17 +378,18 @@ def setThemeNight(baseDir: str):
|
||||||
"link-bg-color": "#0f0d10",
|
"link-bg-color": "#0f0d10",
|
||||||
"main-link-color": "ff9900",
|
"main-link-color": "ff9900",
|
||||||
"main-link-color-hover": "#d09338",
|
"main-link-color-hover": "#d09338",
|
||||||
"main-fg-color": "#a961ab",
|
"main-fg-color": "#0481f5",
|
||||||
"column-left-fg-color": "#a961ab",
|
"column-left-fg-color": "#0481f5",
|
||||||
"main-bg-color-dm": "#0b0a0a",
|
"main-bg-color-dm": "#0b0a0a",
|
||||||
"border-color": "#606984",
|
"border-color": "#606984",
|
||||||
"main-bg-color-reply": "#0f0d10",
|
"main-bg-color-reply": "#0f0d10",
|
||||||
"main-bg-color-report": "#0f0d10",
|
"main-bg-color-report": "#0f0d10",
|
||||||
"hashtag-vertical-spacing3": "100px",
|
"hashtag-vertical-spacing3": "100px",
|
||||||
"hashtag-vertical-spacing4": "150px",
|
"hashtag-vertical-spacing4": "150px",
|
||||||
"button-background-hover": "#6961ab",
|
"button-background-hover": "#0481f5",
|
||||||
"button-background": "#a961ab",
|
"publish-button-background": "#07447c",
|
||||||
"button-selected": "#86579d",
|
"button-background": "#07447c",
|
||||||
|
"button-selected": "#0481f5",
|
||||||
"calendar-bg-color": "#0f0d10",
|
"calendar-bg-color": "#0f0d10",
|
||||||
"lines-color": "#a961ab",
|
"lines-color": "#a961ab",
|
||||||
"day-number": "#a961ab",
|
"day-number": "#a961ab",
|
||||||
|
|
@ -412,6 +418,8 @@ def setThemeStarlight(baseDir: str):
|
||||||
removeTheme(baseDir)
|
removeTheme(baseDir)
|
||||||
setThemeInConfig(baseDir, name)
|
setThemeInConfig(baseDir, name)
|
||||||
themeParams = {
|
themeParams = {
|
||||||
|
"column-left-image-width-mobile": "40vw",
|
||||||
|
"line-spacing-newswire": "120%",
|
||||||
"focus-color": "darkred",
|
"focus-color": "darkred",
|
||||||
"font-size-button-mobile": "36px",
|
"font-size-button-mobile": "36px",
|
||||||
"font-size": "32px",
|
"font-size": "32px",
|
||||||
|
|
@ -437,6 +445,7 @@ def setThemeStarlight(baseDir: str):
|
||||||
"hashtag-vertical-spacing3": "100px",
|
"hashtag-vertical-spacing3": "100px",
|
||||||
"hashtag-vertical-spacing4": "150px",
|
"hashtag-vertical-spacing4": "150px",
|
||||||
"button-background-hover": "#a9282c",
|
"button-background-hover": "#a9282c",
|
||||||
|
"publish-button-background": "#69282c",
|
||||||
"button-background": "#69282c",
|
"button-background": "#69282c",
|
||||||
"button-small-background": "darkblue",
|
"button-small-background": "darkblue",
|
||||||
"button-selected": "#a34046",
|
"button-selected": "#a34046",
|
||||||
|
|
@ -474,6 +483,8 @@ def setThemeHenge(baseDir: str):
|
||||||
removeTheme(baseDir)
|
removeTheme(baseDir)
|
||||||
setThemeInConfig(baseDir, name)
|
setThemeInConfig(baseDir, name)
|
||||||
themeParams = {
|
themeParams = {
|
||||||
|
"column-left-image-width-mobile": "40vw",
|
||||||
|
"column-right-image-width-mobile": "40vw",
|
||||||
"font-size-button-mobile": "36px",
|
"font-size-button-mobile": "36px",
|
||||||
"font-size": "32px",
|
"font-size": "32px",
|
||||||
"font-size2": "26px",
|
"font-size2": "26px",
|
||||||
|
|
@ -498,6 +509,7 @@ def setThemeHenge(baseDir: str):
|
||||||
"hashtag-vertical-spacing3": "100px",
|
"hashtag-vertical-spacing3": "100px",
|
||||||
"hashtag-vertical-spacing4": "150px",
|
"hashtag-vertical-spacing4": "150px",
|
||||||
"button-background-hover": "#444",
|
"button-background-hover": "#444",
|
||||||
|
"publish-button-background": "#222",
|
||||||
"button-background": "#222",
|
"button-background": "#222",
|
||||||
"button-selected": "black",
|
"button-selected": "black",
|
||||||
"dropdown-fg-color": "#dddddd",
|
"dropdown-fg-color": "#dddddd",
|
||||||
|
|
@ -545,6 +557,7 @@ def setThemeZen(baseDir: str):
|
||||||
"title-color": "#dddddd",
|
"title-color": "#dddddd",
|
||||||
"main-visited-color": "#dddddd",
|
"main-visited-color": "#dddddd",
|
||||||
"button-background-hover": "#a63b35",
|
"button-background-hover": "#a63b35",
|
||||||
|
"publish-button-background": "#463b35",
|
||||||
"button-background": "#463b35",
|
"button-background": "#463b35",
|
||||||
"button-selected": "#26201d",
|
"button-selected": "#26201d",
|
||||||
"main-bg-color-dm": "#5c4a40",
|
"main-bg-color-dm": "#5c4a40",
|
||||||
|
|
@ -591,8 +604,12 @@ def setThemeHighVis(baseDir: str):
|
||||||
def setThemeLCD(baseDir: str):
|
def setThemeLCD(baseDir: str):
|
||||||
name = 'lcd'
|
name = 'lcd'
|
||||||
themeParams = {
|
themeParams = {
|
||||||
|
"newswire-date-color": "#cfb42b",
|
||||||
|
"column-left-header-background": "#9fb42b",
|
||||||
|
"column-left-header-color": "#33390d",
|
||||||
"main-bg-color": "#9fb42b",
|
"main-bg-color": "#9fb42b",
|
||||||
"column-left-color": "#9fb42b",
|
"column-left-color": "#33390d",
|
||||||
|
"column-left-fg-color": "#9fb42b",
|
||||||
"link-bg-color": "#33390d",
|
"link-bg-color": "#33390d",
|
||||||
"text-entry-foreground": "#33390d",
|
"text-entry-foreground": "#33390d",
|
||||||
"text-entry-background": "#9fb42b",
|
"text-entry-background": "#9fb42b",
|
||||||
|
|
@ -601,7 +618,6 @@ def setThemeLCD(baseDir: str):
|
||||||
"main-bg-color-dm": "#5fb42b",
|
"main-bg-color-dm": "#5fb42b",
|
||||||
"main-header-color-roles": "#9fb42b",
|
"main-header-color-roles": "#9fb42b",
|
||||||
"main-fg-color": "#33390d",
|
"main-fg-color": "#33390d",
|
||||||
"column-left-fg-color": "#33390d",
|
|
||||||
"border-color": "#33390d",
|
"border-color": "#33390d",
|
||||||
"border-width": "5px",
|
"border-width": "5px",
|
||||||
"main-link-color": "#9fb42b",
|
"main-link-color": "#9fb42b",
|
||||||
|
|
@ -611,9 +627,11 @@ def setThemeLCD(baseDir: str):
|
||||||
"button-selected": "black",
|
"button-selected": "black",
|
||||||
"button-highlighted": "green",
|
"button-highlighted": "green",
|
||||||
"button-background-hover": "#a3390d",
|
"button-background-hover": "#a3390d",
|
||||||
|
"publish-button-background": "#33390d",
|
||||||
"button-background": "#33390d",
|
"button-background": "#33390d",
|
||||||
"button-small-background": "#33390d",
|
"button-small-background": "#33390d",
|
||||||
"button-text": "#9fb42b",
|
"button-text": "#9fb42b",
|
||||||
|
"publish-button-text": "#9fb42b",
|
||||||
"button-small-text": "#9fb42b",
|
"button-small-text": "#9fb42b",
|
||||||
"color: #FFFFFE;": "color: #9fb42b;",
|
"color: #FFFFFE;": "color: #9fb42b;",
|
||||||
"calendar-bg-color": "#eee",
|
"calendar-bg-color": "#eee",
|
||||||
|
|
@ -684,9 +702,11 @@ def setThemePurple(baseDir: str):
|
||||||
"main-visited-color": "#f93bb0",
|
"main-visited-color": "#f93bb0",
|
||||||
"button-selected": "#c042a0",
|
"button-selected": "#c042a0",
|
||||||
"button-background-hover": "#af42a0",
|
"button-background-hover": "#af42a0",
|
||||||
|
"publish-button-background": "#ff42a0",
|
||||||
"button-background": "#ff42a0",
|
"button-background": "#ff42a0",
|
||||||
"button-small-background": "#ff42a0",
|
"button-small-background": "#ff42a0",
|
||||||
"button-text": "white",
|
"button-text": "white",
|
||||||
|
"publish-button-text": "white",
|
||||||
"button-small-text": "white",
|
"button-small-text": "white",
|
||||||
"color: #FFFFFE;": "color: #1f152d;",
|
"color: #FFFFFE;": "color: #1f152d;",
|
||||||
"calendar-bg-color": "#eee",
|
"calendar-bg-color": "#eee",
|
||||||
|
|
@ -735,9 +755,11 @@ def setThemeHacker(baseDir: str):
|
||||||
"main-visited-color": "#3c8234",
|
"main-visited-color": "#3c8234",
|
||||||
"button-selected": "#063200",
|
"button-selected": "#063200",
|
||||||
"button-background-hover": "#a62200",
|
"button-background-hover": "#a62200",
|
||||||
|
"publish-button-background": "#062200",
|
||||||
"button-background": "#062200",
|
"button-background": "#062200",
|
||||||
"button-small-background": "#062200",
|
"button-small-background": "#062200",
|
||||||
"button-text": "#00ff00",
|
"button-text": "#00ff00",
|
||||||
|
"publish-button-text": "#00ff00",
|
||||||
"button-small-text": "#00ff00",
|
"button-small-text": "#00ff00",
|
||||||
"button-corner-radius": "4px",
|
"button-corner-radius": "4px",
|
||||||
"timeline-border-radius": "4px",
|
"timeline-border-radius": "4px",
|
||||||
|
|
@ -830,6 +852,9 @@ def setThemeLight(baseDir: str):
|
||||||
def setThemeSolidaric(baseDir: str):
|
def setThemeSolidaric(baseDir: str):
|
||||||
name = 'solidaric'
|
name = 'solidaric'
|
||||||
themeParams = {
|
themeParams = {
|
||||||
|
"button-highlighted": "darkred",
|
||||||
|
"button-selected-highlighted": "darkred",
|
||||||
|
"newswire-date-color": "grey",
|
||||||
"focus-color": "grey",
|
"focus-color": "grey",
|
||||||
"font-size-button-mobile": "36px",
|
"font-size-button-mobile": "36px",
|
||||||
"font-size": "32px",
|
"font-size": "32px",
|
||||||
|
|
@ -1006,6 +1031,31 @@ def setThemeImages(baseDir: str, name: str) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def setNewsAvatar(baseDir: str, name: str,
|
||||||
|
httpPrefix: str,
|
||||||
|
domain: str, domainFull: str) -> None:
|
||||||
|
"""Sets the avatar for the news account
|
||||||
|
"""
|
||||||
|
nickname = 'news'
|
||||||
|
newFilename = baseDir + '/img/icons/' + name + '/avatar_news.png'
|
||||||
|
if not os.path.isfile(newFilename):
|
||||||
|
newFilename = baseDir + '/img/icons/avatar_news.png'
|
||||||
|
if not os.path.isfile(newFilename):
|
||||||
|
return
|
||||||
|
avatarFilename = \
|
||||||
|
httpPrefix + '://' + domainFull + '/users/' + nickname + '.png'
|
||||||
|
avatarFilename = avatarFilename.replace('/', '-')
|
||||||
|
filename = baseDir + '/cache/avatars/' + avatarFilename
|
||||||
|
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
os.remove(filename)
|
||||||
|
if os.path.isdir(baseDir + '/cache/avatars'):
|
||||||
|
copyfile(newFilename, filename)
|
||||||
|
copyfile(newFilename,
|
||||||
|
baseDir + '/accounts/' +
|
||||||
|
nickname + '@' + domain + '/avatar.png')
|
||||||
|
|
||||||
|
|
||||||
def setTheme(baseDir: str, name: str, domain: str) -> bool:
|
def setTheme(baseDir: str, name: str, domain: str) -> bool:
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "اقرأ أكثر...",
|
"Read more...": "اقرأ أكثر...",
|
||||||
"Edit News Post": "تحرير منشور الأخبار",
|
"Edit News Post": "تحرير منشور الأخبار",
|
||||||
"A list of editor nicknames. One per line.": "قائمة بأسماء المحرر. واحد في كل سطر.",
|
"A list of editor nicknames. One per line.": "قائمة بأسماء المحرر. واحد في كل سطر.",
|
||||||
"Site Editors": "محررو الموقع"
|
"Site Editors": "محررو الموقع",
|
||||||
|
"Allow news posts": "السماح بنشر الأخبار",
|
||||||
|
"Publish": "ينشر",
|
||||||
|
"Publish a news article": "انشر مقالة إخبارية"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Llegeix més...",
|
"Read more...": "Llegeix més...",
|
||||||
"Edit News Post": "Edita la publicació de notícies",
|
"Edit News Post": "Edita la publicació de notícies",
|
||||||
"A list of editor nicknames. One per line.": "Una llista de sobrenoms de l'editor. Un per línia.",
|
"A list of editor nicknames. One per line.": "Una llista de sobrenoms de l'editor. Un per línia.",
|
||||||
"Site Editors": "Editors de llocs"
|
"Site Editors": "Editors de llocs",
|
||||||
|
"Allow news posts": "Permet publicacions de notícies",
|
||||||
|
"Publish": "Publica",
|
||||||
|
"Publish a news article": "Publicar un article de notícies"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Darllen mwy...",
|
"Read more...": "Darllen mwy...",
|
||||||
"Edit News Post": "Golygu News News",
|
"Edit News Post": "Golygu News News",
|
||||||
"A list of editor nicknames. One per line.": "Rhestr o lysenwau golygydd. Un i bob llinell.",
|
"A list of editor nicknames. One per line.": "Rhestr o lysenwau golygydd. Un i bob llinell.",
|
||||||
"Site Editors": "Golygyddion Safle"
|
"Site Editors": "Golygyddion Safle",
|
||||||
|
"Allow news posts": "Caniatáu swyddi newyddion",
|
||||||
|
"Publish": "Cyhoeddi",
|
||||||
|
"Publish a news article": "Cyhoeddi erthygl newyddion"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Weiterlesen...",
|
"Read more...": "Weiterlesen...",
|
||||||
"Edit News Post": "Nachrichtenbeitrag bearbeiten",
|
"Edit News Post": "Nachrichtenbeitrag bearbeiten",
|
||||||
"A list of editor nicknames. One per line.": "Eine Liste der Editor-Spitznamen. Eine pro Zeile.",
|
"A list of editor nicknames. One per line.": "Eine Liste der Editor-Spitznamen. Eine pro Zeile.",
|
||||||
"Site Editors": "Site-Editoren"
|
"Site Editors": "Site-Editoren",
|
||||||
|
"Allow news posts": "Nachrichtenbeiträge zulassen",
|
||||||
|
"Publish": "Veröffentlichen",
|
||||||
|
"Publish a news article": "Veröffentlichen Sie einen Nachrichtenartikel"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Read more...",
|
"Read more...": "Read more...",
|
||||||
"Edit News Post": "Edit News Post",
|
"Edit News Post": "Edit News Post",
|
||||||
"A list of editor nicknames. One per line.": "A list of editor nicknames. One per line.",
|
"A list of editor nicknames. One per line.": "A list of editor nicknames. One per line.",
|
||||||
"Site Editors": "Site Editors"
|
"Site Editors": "Site Editors",
|
||||||
|
"Allow news posts": "Allow news posts",
|
||||||
|
"Publish": "Publish",
|
||||||
|
"Publish a news article": "Publish a news article"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Lee mas...",
|
"Read more...": "Lee mas...",
|
||||||
"Edit News Post": "Editar publicación de noticias",
|
"Edit News Post": "Editar publicación de noticias",
|
||||||
"A list of editor nicknames. One per line.": "Una lista de apodos de los editores. Uno por línea.",
|
"A list of editor nicknames. One per line.": "Una lista de apodos de los editores. Uno por línea.",
|
||||||
"Site Editors": "Editores del sitio"
|
"Site Editors": "Editores del sitio",
|
||||||
|
"Allow news posts": "Permitir publicaciones de noticias",
|
||||||
|
"Publish": "Publicar",
|
||||||
|
"Publish a news article": "Publica un artículo de noticias"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Lire la suite...",
|
"Read more...": "Lire la suite...",
|
||||||
"Edit News Post": "Modifier l'article d'actualité",
|
"Edit News Post": "Modifier l'article d'actualité",
|
||||||
"A list of editor nicknames. One per line.": "Une liste de surnoms d'éditeur. Un par ligne.",
|
"A list of editor nicknames. One per line.": "Une liste de surnoms d'éditeur. Un par ligne.",
|
||||||
"Site Editors": "Éditeurs du site"
|
"Site Editors": "Éditeurs du site",
|
||||||
|
"Allow news posts": "Autoriser les articles d'actualité",
|
||||||
|
"Publish": "Publier",
|
||||||
|
"Publish a news article": "Publier un article de presse"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Leigh Nios mo...",
|
"Read more...": "Leigh Nios mo...",
|
||||||
"Edit News Post": "Cuir News Post in eagar",
|
"Edit News Post": "Cuir News Post in eagar",
|
||||||
"A list of editor nicknames. One per line.": "Liosta leasainmneacha eagarthóra. Ceann in aghaidh na líne.",
|
"A list of editor nicknames. One per line.": "Liosta leasainmneacha eagarthóra. Ceann in aghaidh na líne.",
|
||||||
"Site Editors": "Eagarthóirí Suímh"
|
"Site Editors": "Eagarthóirí Suímh",
|
||||||
|
"Allow news posts": "Ceadaigh poist nuachta",
|
||||||
|
"Publish": "Fhoilsiú",
|
||||||
|
"Publish a news article": "Foilsigh alt nuachta"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "अधिक पढ़ें...",
|
"Read more...": "अधिक पढ़ें...",
|
||||||
"Edit News Post": "समाचार पोस्ट संपादित करें",
|
"Edit News Post": "समाचार पोस्ट संपादित करें",
|
||||||
"A list of editor nicknames. One per line.": "संपादक उपनामों की एक सूची। प्रति पंक्ति एक।",
|
"A list of editor nicknames. One per line.": "संपादक उपनामों की एक सूची। प्रति पंक्ति एक।",
|
||||||
"Site Editors": "साइट संपादकों"
|
"Site Editors": "साइट संपादकों",
|
||||||
|
"Allow news posts": "समाचार पोस्ट की अनुमति दें",
|
||||||
|
"Publish": "प्रकाशित करना",
|
||||||
|
"Publish a news article": "एक समाचार लेख प्रकाशित करें"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Leggi di più...",
|
"Read more...": "Leggi di più...",
|
||||||
"Edit News Post": "Modifica post di notizie",
|
"Edit News Post": "Modifica post di notizie",
|
||||||
"A list of editor nicknames. One per line.": "Un elenco di soprannomi dell'editor. Uno per riga.",
|
"A list of editor nicknames. One per line.": "Un elenco di soprannomi dell'editor. Uno per riga.",
|
||||||
"Site Editors": "Editori del sito"
|
"Site Editors": "Editori del sito",
|
||||||
|
"Allow news posts": "Consenti post di notizie",
|
||||||
|
"Publish": "Pubblicare",
|
||||||
|
"Publish a news article": "Pubblica un articolo di notizie"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "続きを読む...",
|
"Read more...": "続きを読む...",
|
||||||
"Edit News Post": "ニュース投稿を編集する",
|
"Edit News Post": "ニュース投稿を編集する",
|
||||||
"A list of editor nicknames. One per line.": "編集者のニックネームのリスト。 1行に1つ。",
|
"A list of editor nicknames. One per line.": "編集者のニックネームのリスト。 1行に1つ。",
|
||||||
"Site Editors": "サイト編集者"
|
"Site Editors": "サイト編集者",
|
||||||
|
"Allow news posts": "ニュース投稿を許可する",
|
||||||
|
"Publish": "公開する",
|
||||||
|
"Publish a news article": "ニュース記事を公開する"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -304,5 +304,8 @@
|
||||||
"Read more...": "Read more...",
|
"Read more...": "Read more...",
|
||||||
"Edit News Post": "Edit News Post",
|
"Edit News Post": "Edit News Post",
|
||||||
"A list of editor nicknames. One per line.": "A list of editor nicknames. One per line.",
|
"A list of editor nicknames. One per line.": "A list of editor nicknames. One per line.",
|
||||||
"Site Editors": "Site Editors"
|
"Site Editors": "Site Editors",
|
||||||
|
"Allow news posts": "Allow news posts",
|
||||||
|
"Publish": "Publish",
|
||||||
|
"Publish a news article": "Publish a news article"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Consulte Mais informação...",
|
"Read more...": "Consulte Mais informação...",
|
||||||
"Edit News Post": "Editar Postagem de Notícias",
|
"Edit News Post": "Editar Postagem de Notícias",
|
||||||
"A list of editor nicknames. One per line.": "Uma lista de apelidos de editores. Um por linha.",
|
"A list of editor nicknames. One per line.": "Uma lista de apelidos de editores. Um por linha.",
|
||||||
"Site Editors": "Editores do site"
|
"Site Editors": "Editores do site",
|
||||||
|
"Allow news posts": "Permitir postagens de notícias",
|
||||||
|
"Publish": "Publicar",
|
||||||
|
"Publish a news article": "Publique um artigo de notícias"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "Подробнее...",
|
"Read more...": "Подробнее...",
|
||||||
"Edit News Post": "Редактировать новость",
|
"Edit News Post": "Редактировать новость",
|
||||||
"A list of editor nicknames. One per line.": "Список ников редакторов. По одному на строку.",
|
"A list of editor nicknames. One per line.": "Список ников редакторов. По одному на строку.",
|
||||||
"Site Editors": "Редакторы сайта"
|
"Site Editors": "Редакторы сайта",
|
||||||
|
"Allow news posts": "Разрешить публикации новостей",
|
||||||
|
"Publish": "Публиковать",
|
||||||
|
"Publish a news article": "Опубликовать новостную статью"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,5 +308,8 @@
|
||||||
"Read more...": "阅读更多...",
|
"Read more...": "阅读更多...",
|
||||||
"Edit News Post": "编辑新闻帖子",
|
"Edit News Post": "编辑新闻帖子",
|
||||||
"A list of editor nicknames. One per line.": "编辑者昵称列表。 每行一个。",
|
"A list of editor nicknames. One per line.": "编辑者昵称列表。 每行一个。",
|
||||||
"Site Editors": "网站编辑"
|
"Site Editors": "网站编辑",
|
||||||
|
"Allow news posts": "允许新闻发布",
|
||||||
|
"Publish": "发布",
|
||||||
|
"Publish a news article": "发布新闻文章"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
utils.py
|
|
@ -19,6 +19,14 @@ from calendar import monthrange
|
||||||
from followingCalendar import addPersonToCalendar
|
from followingCalendar import addPersonToCalendar
|
||||||
|
|
||||||
|
|
||||||
|
def isSystemAccount(nickname: str) -> bool:
|
||||||
|
"""Returns true if the given nickname is a system account
|
||||||
|
"""
|
||||||
|
if nickname == 'news' or nickname == 'inbox':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def createConfig(baseDir: str) -> None:
|
def createConfig(baseDir: str) -> None:
|
||||||
"""Creates a configuration file
|
"""Creates a configuration file
|
||||||
"""
|
"""
|
||||||
|
|
@ -49,7 +57,7 @@ def getConfigParam(baseDir: str, variableName: str):
|
||||||
configFilename = baseDir + '/config.json'
|
configFilename = baseDir + '/config.json'
|
||||||
configJson = loadJson(configFilename)
|
configJson = loadJson(configFilename)
|
||||||
if configJson:
|
if configJson:
|
||||||
if configJson.get(variableName):
|
if variableName in configJson:
|
||||||
return configJson[variableName]
|
return configJson[variableName]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -265,6 +273,19 @@ def isEvil(domain: str) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def containsInvalidChars(jsonStr: str) -> bool:
|
||||||
|
"""Does the given json string contain invalid characters?
|
||||||
|
e.g. dubious clacks/admin dogwhistles
|
||||||
|
"""
|
||||||
|
invalidStrings = {
|
||||||
|
'卐', '卍', '࿕', '࿖', '࿗', '࿘'
|
||||||
|
}
|
||||||
|
for isInvalid in invalidStrings:
|
||||||
|
if isInvalid in jsonStr:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def createPersonDir(nickname: str, domain: str, baseDir: str,
|
def createPersonDir(nickname: str, domain: str, baseDir: str,
|
||||||
dirname: str) -> str:
|
dirname: str) -> str:
|
||||||
"""Create a directory for a person
|
"""Create a directory for a person
|
||||||
|
|
|
||||||
210
webinterface.py
|
|
@ -25,6 +25,7 @@ from ssb import getSSBAddress
|
||||||
from tox import getToxAddress
|
from tox import getToxAddress
|
||||||
from matrix import getMatrixAddress
|
from matrix import getMatrixAddress
|
||||||
from donate import getDonationUrl
|
from donate import getDonationUrl
|
||||||
|
from utils import isSystemAccount
|
||||||
from utils import removeIdEnding
|
from utils import removeIdEnding
|
||||||
from utils import getProtocolPrefixes
|
from utils import getProtocolPrefixes
|
||||||
from utils import searchBoxPosts
|
from utils import searchBoxPosts
|
||||||
|
|
@ -3232,7 +3233,7 @@ def htmlProfile(defaultTimeline: str,
|
||||||
session, wfRequest: {}, personCache: {},
|
session, wfRequest: {}, personCache: {},
|
||||||
YTReplacementDomain: str,
|
YTReplacementDomain: str,
|
||||||
showPublishedDateOnly: bool,
|
showPublishedDateOnly: bool,
|
||||||
extraJson=None,
|
newswire: {}, extraJson=None,
|
||||||
pageNumber=None, maxItemsPerPage=None) -> str:
|
pageNumber=None, maxItemsPerPage=None) -> str:
|
||||||
"""Show the profile page as html
|
"""Show the profile page as html
|
||||||
"""
|
"""
|
||||||
|
|
@ -3296,7 +3297,7 @@ def htmlProfile(defaultTimeline: str,
|
||||||
PGPfingerprint or emailAddress:
|
PGPfingerprint or emailAddress:
|
||||||
donateSection = '<div class="container">\n'
|
donateSection = '<div class="container">\n'
|
||||||
donateSection += ' <center>\n'
|
donateSection += ' <center>\n'
|
||||||
if donateUrl:
|
if donateUrl and not isSystemAccount(nickname):
|
||||||
donateSection += \
|
donateSection += \
|
||||||
' <p><a href="' + donateUrl + \
|
' <p><a href="' + donateUrl + \
|
||||||
'"><button class="donateButton">' + translate['Donate'] + \
|
'"><button class="donateButton">' + translate['Donate'] + \
|
||||||
|
|
@ -3415,13 +3416,37 @@ def htmlProfile(defaultTimeline: str,
|
||||||
avatarDescription = profileJson['summary'].replace('<br>', '\n')
|
avatarDescription = profileJson['summary'].replace('<br>', '\n')
|
||||||
avatarDescription = avatarDescription.replace('<p>', '')
|
avatarDescription = avatarDescription.replace('<p>', '')
|
||||||
avatarDescription = avatarDescription.replace('</p>', '')
|
avatarDescription = avatarDescription.replace('</p>', '')
|
||||||
profileHeaderStr = '<div class="hero-image">'
|
|
||||||
profileHeaderStr += ' <div class="hero-text">'
|
# If this is the news account then show a different banner
|
||||||
|
if isSystemAccount(nickname):
|
||||||
|
profileHeaderStr = '<div class="timeline-banner"></div>\n'
|
||||||
|
profileHeaderStr += '<center>' + loginButton + '</center>\n'
|
||||||
|
|
||||||
|
profileHeaderStr += '<table class="timeline">\n'
|
||||||
|
profileHeaderStr += ' <colgroup>\n'
|
||||||
|
profileHeaderStr += ' <col span="1" class="column-left">\n'
|
||||||
|
profileHeaderStr += ' <col span="1" class="column-center">\n'
|
||||||
|
profileHeaderStr += ' <col span="1" class="column-right">\n'
|
||||||
|
profileHeaderStr += ' </colgroup>\n'
|
||||||
|
profileHeaderStr += ' <tbody>\n'
|
||||||
|
profileHeaderStr += ' <tr>\n'
|
||||||
|
profileHeaderStr += ' <td valign="top" class="col-left">\n'
|
||||||
|
iconsDir = getIconsDir(baseDir)
|
||||||
|
profileHeaderStr += \
|
||||||
|
getLeftColumnContent(baseDir, 'news', domainFull,
|
||||||
|
httpPrefix, translate,
|
||||||
|
iconsDir, False,
|
||||||
|
False, None)
|
||||||
|
profileHeaderStr += ' </td>\n'
|
||||||
|
profileHeaderStr += ' <td valign="top" class="col-center">\n'
|
||||||
|
else:
|
||||||
|
profileHeaderStr = '<div class="hero-image">\n'
|
||||||
|
profileHeaderStr += ' <div class="hero-text">\n'
|
||||||
profileHeaderStr += \
|
profileHeaderStr += \
|
||||||
' <img loading="lazy" src="' + profileJson['icon']['url'] + \
|
' <img loading="lazy" src="' + profileJson['icon']['url'] + \
|
||||||
'" title="' + avatarDescription + '" alt="' + \
|
'" title="' + avatarDescription + '" alt="' + \
|
||||||
avatarDescription + '" class="title">'
|
avatarDescription + '" class="title">\n'
|
||||||
profileHeaderStr += ' <h1>' + displayName + '</h1>'
|
profileHeaderStr += ' <h1>' + displayName + '</h1>\n'
|
||||||
iconsDir = getIconsDir(baseDir)
|
iconsDir = getIconsDir(baseDir)
|
||||||
profileHeaderStr += \
|
profileHeaderStr += \
|
||||||
'<p><b>@' + nickname + '@' + domainFull + '</b><br>'
|
'<p><b>@' + nickname + '@' + domainFull + '</b><br>'
|
||||||
|
|
@ -3429,17 +3454,19 @@ def htmlProfile(defaultTimeline: str,
|
||||||
'<a href="/users/' + nickname + \
|
'<a href="/users/' + nickname + \
|
||||||
'/qrcode.png" alt="' + translate['QR Code'] + '" title="' + \
|
'/qrcode.png" alt="' + translate['QR Code'] + '" title="' + \
|
||||||
translate['QR Code'] + '">' + \
|
translate['QR Code'] + '">' + \
|
||||||
'<img class="qrcode" src="/' + iconsDir + '/qrcode.png" /></a></p>'
|
'<img class="qrcode" src="/' + iconsDir + \
|
||||||
profileHeaderStr += ' <p>' + profileDescriptionShort + '</p>'
|
'/qrcode.png" /></a></p>\n'
|
||||||
|
profileHeaderStr += ' <p>' + profileDescriptionShort + '</p>\n'
|
||||||
profileHeaderStr += loginButton
|
profileHeaderStr += loginButton
|
||||||
profileHeaderStr += ' </div>'
|
profileHeaderStr += ' </div>\n'
|
||||||
profileHeaderStr += '</div>'
|
profileHeaderStr += '</div>\n'
|
||||||
|
|
||||||
profileStr = \
|
profileStr = \
|
||||||
linkToTimelineStart + profileHeaderStr + \
|
linkToTimelineStart + profileHeaderStr + \
|
||||||
linkToTimelineEnd + donateSection
|
linkToTimelineEnd + donateSection
|
||||||
profileStr += '<div class="container" id="buttonheader">\n'
|
profileStr += '<div class="container" id="buttonheader">\n'
|
||||||
profileStr += ' <center>'
|
profileStr += ' <center>'
|
||||||
|
if not isSystemAccount(nickname):
|
||||||
profileStr += \
|
profileStr += \
|
||||||
' <a href="' + usersPath + '#buttonheader"><button class="' + \
|
' <a href="' + usersPath + '#buttonheader"><button class="' + \
|
||||||
postsButton + '"><span>' + translate['Posts'] + \
|
postsButton + '"><span>' + translate['Posts'] + \
|
||||||
|
|
@ -3454,7 +3481,8 @@ def htmlProfile(defaultTimeline: str,
|
||||||
'"><span>' + translate['Followers'] + ' </span></button></a>'
|
'"><span>' + translate['Followers'] + ' </span></button></a>'
|
||||||
profileStr += \
|
profileStr += \
|
||||||
' <a href="' + usersPath + '/roles#buttonheader">' + \
|
' <a href="' + usersPath + '/roles#buttonheader">' + \
|
||||||
'<button class="' + rolesButton + '"><span>' + translate['Roles'] + \
|
'<button class="' + rolesButton + '"><span>' + \
|
||||||
|
translate['Roles'] + \
|
||||||
' </span></button></a>'
|
' </span></button></a>'
|
||||||
profileStr += \
|
profileStr += \
|
||||||
' <a href="' + usersPath + '/skills#buttonheader">' + \
|
' <a href="' + usersPath + '/skills#buttonheader">' + \
|
||||||
|
|
@ -3477,6 +3505,12 @@ def htmlProfile(defaultTimeline: str,
|
||||||
profileStyle = \
|
profileStyle = \
|
||||||
cssFile.read().replace('image.png',
|
cssFile.read().replace('image.png',
|
||||||
profileJson['image']['url'])
|
profileJson['image']['url'])
|
||||||
|
if isSystemAccount(nickname):
|
||||||
|
bannerFile, bannerFilename = \
|
||||||
|
getBannerFile(baseDir, nickname, domain)
|
||||||
|
profileStyle = \
|
||||||
|
profileStyle.replace('banner.png',
|
||||||
|
'/users/' + nickname + '/' + bannerFile)
|
||||||
|
|
||||||
licenseStr = \
|
licenseStr = \
|
||||||
'<a href="https://gitlab.com/bashrc2/epicyon">' + \
|
'<a href="https://gitlab.com/bashrc2/epicyon">' + \
|
||||||
|
|
@ -3522,8 +3556,27 @@ def htmlProfile(defaultTimeline: str,
|
||||||
htmlProfileShares(actor, translate,
|
htmlProfileShares(actor, translate,
|
||||||
nickname, domainFull,
|
nickname, domainFull,
|
||||||
extraJson) + licenseStr
|
extraJson) + licenseStr
|
||||||
|
|
||||||
|
# Footer which is only used for system accounts
|
||||||
|
profileFooterStr = ''
|
||||||
|
if isSystemAccount(nickname):
|
||||||
|
profileFooterStr = ' </td>\n'
|
||||||
|
profileFooterStr += ' <td valign="top" class="col-right">\n'
|
||||||
|
iconsDir = getIconsDir(baseDir)
|
||||||
|
profileFooterStr += \
|
||||||
|
getRightColumnContent(baseDir, 'news', domainFull,
|
||||||
|
httpPrefix, translate,
|
||||||
|
iconsDir, False, False,
|
||||||
|
newswire, False,
|
||||||
|
False, None, False)
|
||||||
|
profileFooterStr += ' </td>\n'
|
||||||
|
profileFooterStr += ' </tr>\n'
|
||||||
|
profileFooterStr += ' </tbody>\n'
|
||||||
|
profileFooterStr += '</table>\n'
|
||||||
|
|
||||||
profileStr = \
|
profileStr = \
|
||||||
htmlHeader(cssFilename, profileStyle) + profileStr + htmlFooter()
|
htmlHeader(cssFilename, profileStyle) + \
|
||||||
|
profileStr + profileFooterStr + htmlFooter()
|
||||||
return profileStr
|
return profileStr
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4426,13 +4479,21 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
if timeDiff > 100:
|
if timeDiff > 100:
|
||||||
print('TIMING INDIV ' + boxName + ' 7 = ' + str(timeDiff))
|
print('TIMING INDIV ' + boxName + ' 7 = ' + str(timeDiff))
|
||||||
|
|
||||||
|
if '/users/news/' not in avatarUrl:
|
||||||
avatarLink = ' <a class="imageAnchor" href="' + postActor + '">'
|
avatarLink = ' <a class="imageAnchor" href="' + postActor + '">'
|
||||||
avatarLink += \
|
avatarLink += \
|
||||||
' <img loading="lazy" src="' + avatarUrl + '" title="' + \
|
' <img loading="lazy" src="' + avatarUrl + '" title="' + \
|
||||||
translate['Show profile'] + '" alt=" "' + avatarPosition + '/></a>\n'
|
translate['Show profile'] + '" alt=" "' + avatarPosition + \
|
||||||
|
'/></a>\n'
|
||||||
|
else:
|
||||||
|
avatarLink += \
|
||||||
|
' <img loading="lazy" src="' + avatarUrl + '" title="' + \
|
||||||
|
translate['Show profile'] + '" alt=" "' + avatarPosition + \
|
||||||
|
'/>\n'
|
||||||
|
|
||||||
if showAvatarOptions and \
|
if showAvatarOptions and \
|
||||||
fullDomain + '/users/' + nickname not in postActor:
|
fullDomain + '/users/' + nickname not in postActor:
|
||||||
|
if '/users/news/' not in avatarUrl:
|
||||||
avatarLink = \
|
avatarLink = \
|
||||||
' <a class="imageAnchor" href="/users/' + \
|
' <a class="imageAnchor" href="/users/' + \
|
||||||
nickname + '?options=' + postActor + \
|
nickname + '?options=' + postActor + \
|
||||||
|
|
@ -4441,6 +4502,12 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
' <img loading="lazy" title="' + \
|
' <img loading="lazy" title="' + \
|
||||||
translate['Show options for this person'] + \
|
translate['Show options for this person'] + \
|
||||||
'" src="' + avatarUrl + '" ' + avatarPosition + '/></a>\n'
|
'" src="' + avatarUrl + '" ' + avatarPosition + '/></a>\n'
|
||||||
|
else:
|
||||||
|
# don't link to the person options for the news account
|
||||||
|
avatarLink += \
|
||||||
|
' <img loading="lazy" title="' + \
|
||||||
|
translate['Show options for this person'] + \
|
||||||
|
'" src="' + avatarUrl + '" ' + avatarPosition + '/>\n'
|
||||||
avatarImageInPost = \
|
avatarImageInPost = \
|
||||||
' <div class="timeline-avatar">' + avatarLink.strip() + '</div>\n'
|
' <div class="timeline-avatar">' + avatarLink.strip() + '</div>\n'
|
||||||
|
|
||||||
|
|
@ -4929,13 +4996,17 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
|
|
||||||
if announceAvatarUrl:
|
if announceAvatarUrl:
|
||||||
idx = 'Show options for this person'
|
idx = 'Show options for this person'
|
||||||
|
if '/users/news/' not in announceAvatarUrl:
|
||||||
replyAvatarImageInPost = \
|
replyAvatarImageInPost = \
|
||||||
' ' \
|
' ' \
|
||||||
'<div class="timeline-avatar-reply">\n' \
|
'<div class=' + \
|
||||||
' <a class="imageAnchor" ' + \
|
'"timeline-avatar-reply">\n' \
|
||||||
|
' ' + \
|
||||||
|
'<a class="imageAnchor" ' + \
|
||||||
'href="/users/' + nickname + \
|
'href="/users/' + nickname + \
|
||||||
'?options=' + \
|
'?options=' + \
|
||||||
announceActor + ';' + str(pageNumber) + \
|
announceActor + ';' + \
|
||||||
|
str(pageNumber) + \
|
||||||
';' + announceAvatarUrl + \
|
';' + announceAvatarUrl + \
|
||||||
messageIdStr + '">' \
|
messageIdStr + '">' \
|
||||||
'<img loading="lazy" src="' + \
|
'<img loading="lazy" src="' + \
|
||||||
|
|
@ -5437,10 +5508,15 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
||||||
iconsDir + '/edit.png" /></a>\n'
|
iconsDir + '/edit.png" /></a>\n'
|
||||||
|
|
||||||
# RSS icon
|
# 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'
|
||||||
htmlStr += \
|
htmlStr += \
|
||||||
' <a href="' + \
|
' <a href="' + rssUrl + '">' + \
|
||||||
httpPrefix + '://' + domainFull + \
|
|
||||||
'/blog/' + nickname + '/rss.xml">' + \
|
|
||||||
'<img class="' + editImageClass + \
|
'<img class="' + editImageClass + \
|
||||||
'" loading="lazy" alt="' + \
|
'" loading="lazy" alt="' + \
|
||||||
translate['RSS feed for this site'] + \
|
translate['RSS feed for this site'] + \
|
||||||
|
|
@ -5513,7 +5589,7 @@ def votesIndicator(totalVotes: int, positiveVoting: bool) -> str:
|
||||||
return totalVotesStr
|
return totalVotesStr
|
||||||
|
|
||||||
|
|
||||||
def htmlNewswire(newswire: str, nickname: str, moderator: bool,
|
def htmlNewswire(newswire: {}, nickname: str, moderator: bool,
|
||||||
translate: {}, positiveVoting: bool, iconsDir: str) -> str:
|
translate: {}, positiveVoting: bool, iconsDir: str) -> str:
|
||||||
"""Converts a newswire dict into html
|
"""Converts a newswire dict into html
|
||||||
"""
|
"""
|
||||||
|
|
@ -5521,7 +5597,7 @@ def htmlNewswire(newswire: str, nickname: str, moderator: bool,
|
||||||
for dateStr, item in newswire.items():
|
for dateStr, item in newswire.items():
|
||||||
publishedDate = \
|
publishedDate = \
|
||||||
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S+00:00")
|
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S+00:00")
|
||||||
dateShown = publishedDate.strftime("%Y-%m-%d")
|
dateShown = publishedDate.strftime("%Y-%m-%d %H:%M")
|
||||||
|
|
||||||
dateStrLink = dateStr.replace('T', ' ')
|
dateStrLink = dateStr.replace('T', ' ')
|
||||||
dateStrLink = dateStrLink.replace('Z', '')
|
dateStrLink = dateStrLink.replace('Z', '')
|
||||||
|
|
@ -5584,7 +5660,8 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
|
||||||
httpPrefix: str, translate: {},
|
httpPrefix: str, translate: {},
|
||||||
iconsDir: str, moderator: bool, editor: bool,
|
iconsDir: str, moderator: bool, editor: bool,
|
||||||
newswire: {}, positiveVoting: bool,
|
newswire: {}, positiveVoting: bool,
|
||||||
showBackButton: bool, timelinePath: str) -> str:
|
showBackButton: bool, timelinePath: str,
|
||||||
|
showPublishButton: bool) -> str:
|
||||||
"""Returns html content for the right column
|
"""Returns html content for the right column
|
||||||
"""
|
"""
|
||||||
htmlStr = ''
|
htmlStr = ''
|
||||||
|
|
@ -5627,6 +5704,14 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
|
||||||
'<button class="cancelbtn">' + \
|
'<button class="cancelbtn">' + \
|
||||||
translate['Go Back'] + '</button></a>\n'
|
translate['Go Back'] + '</button></a>\n'
|
||||||
|
|
||||||
|
if showPublishButton:
|
||||||
|
htmlStr += \
|
||||||
|
' <a href="' + \
|
||||||
|
'/users/' + nickname + '/newblog" ' + \
|
||||||
|
'title="' + translate['Publish a news article'] + '">' + \
|
||||||
|
'<button class="publishbtn">' + \
|
||||||
|
translate['Publish'] + '</button></a>\n'
|
||||||
|
|
||||||
if editor:
|
if editor:
|
||||||
if os.path.isfile(baseDir + '/accounts/newswiremoderation.txt'):
|
if os.path.isfile(baseDir + '/accounts/newswiremoderation.txt'):
|
||||||
# show the edit icon highlighted
|
# show the edit icon highlighted
|
||||||
|
|
@ -5744,11 +5829,36 @@ def htmlNewswireMobile(baseDir: str, nickname: str,
|
||||||
httpPrefix, translate,
|
httpPrefix, translate,
|
||||||
iconsDir, moderator, editor,
|
iconsDir, moderator, editor,
|
||||||
newswire, positiveVoting,
|
newswire, positiveVoting,
|
||||||
True, timelinePath)
|
True, timelinePath, True)
|
||||||
htmlStr += htmlFooter()
|
htmlStr += htmlFooter()
|
||||||
return htmlStr
|
return htmlStr
|
||||||
|
|
||||||
|
|
||||||
|
def getBannerFile(baseDir: str, nickname: str, domain: str) -> (str, str):
|
||||||
|
"""
|
||||||
|
returns the banner filename
|
||||||
|
"""
|
||||||
|
# filename of the banner shown at the top
|
||||||
|
bannerFile = 'banner.png'
|
||||||
|
bannerFilename = baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/' + bannerFile
|
||||||
|
if not os.path.isfile(bannerFilename):
|
||||||
|
bannerFile = 'banner.jpg'
|
||||||
|
bannerFilename = baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/' + bannerFile
|
||||||
|
if not os.path.isfile(bannerFilename):
|
||||||
|
bannerFile = 'banner.gif'
|
||||||
|
bannerFilename = baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/' + bannerFile
|
||||||
|
if not os.path.isfile(bannerFilename):
|
||||||
|
bannerFile = 'banner.avif'
|
||||||
|
bannerFilename = baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/' + bannerFile
|
||||||
|
if not os.path.isfile(bannerFilename):
|
||||||
|
bannerFile = 'banner.webp'
|
||||||
|
return bannerFile, bannerFilename
|
||||||
|
|
||||||
|
|
||||||
def htmlTimeline(defaultTimeline: str,
|
def htmlTimeline(defaultTimeline: str,
|
||||||
recentPostsCache: {}, maxRecentPosts: int,
|
recentPostsCache: {}, maxRecentPosts: int,
|
||||||
translate: {}, pageNumber: int,
|
translate: {}, pageNumber: int,
|
||||||
|
|
@ -5824,23 +5934,7 @@ def htmlTimeline(defaultTimeline: str,
|
||||||
cssFilename = baseDir + '/epicyon.css'
|
cssFilename = baseDir + '/epicyon.css'
|
||||||
|
|
||||||
# filename of the banner shown at the top
|
# filename of the banner shown at the top
|
||||||
bannerFile = 'banner.png'
|
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain)
|
||||||
bannerFilename = baseDir + '/accounts/' + \
|
|
||||||
nickname + '@' + domain + '/' + bannerFile
|
|
||||||
if not os.path.isfile(bannerFilename):
|
|
||||||
bannerFile = 'banner.jpg'
|
|
||||||
bannerFilename = baseDir + '/accounts/' + \
|
|
||||||
nickname + '@' + domain + '/' + bannerFile
|
|
||||||
if not os.path.isfile(bannerFilename):
|
|
||||||
bannerFile = 'banner.gif'
|
|
||||||
bannerFilename = baseDir + '/accounts/' + \
|
|
||||||
nickname + '@' + domain + '/' + bannerFile
|
|
||||||
if not os.path.isfile(bannerFilename):
|
|
||||||
bannerFile = 'banner.avif'
|
|
||||||
bannerFilename = baseDir + '/accounts/' + \
|
|
||||||
nickname + '@' + domain + '/' + bannerFile
|
|
||||||
if not os.path.isfile(bannerFilename):
|
|
||||||
bannerFile = 'banner.webp'
|
|
||||||
|
|
||||||
# benchmark 1
|
# benchmark 1
|
||||||
timeDiff = int((time.time() - timelineStartTime) * 1000)
|
timeDiff = int((time.time() - timelineStartTime) * 1000)
|
||||||
|
|
@ -6136,7 +6230,7 @@ def htmlTimeline(defaultTimeline: str,
|
||||||
# typically the blogs button
|
# typically the blogs button
|
||||||
# but may change if this is a blogging oriented instance
|
# but may change if this is a blogging oriented instance
|
||||||
if defaultTimeline != 'tlblogs':
|
if defaultTimeline != 'tlblogs':
|
||||||
if not minimal:
|
if not minimal or defaultTimeline == 'tlnews':
|
||||||
tlStr += \
|
tlStr += \
|
||||||
' <a href="' + usersPath + \
|
' <a href="' + usersPath + \
|
||||||
'/tlblogs"><button class="' + \
|
'/tlblogs"><button class="' + \
|
||||||
|
|
@ -6422,7 +6516,7 @@ def htmlTimeline(defaultTimeline: str,
|
||||||
httpPrefix, translate, iconsDir,
|
httpPrefix, translate, iconsDir,
|
||||||
moderator, editor,
|
moderator, editor,
|
||||||
newswire, positiveVoting,
|
newswire, positiveVoting,
|
||||||
False, None)
|
False, None, True)
|
||||||
tlStr += ' <td valign="top" class="col-right">' + \
|
tlStr += ' <td valign="top" class="col-right">' + \
|
||||||
rightColumnStr + ' </td>\n'
|
rightColumnStr + ' </td>\n'
|
||||||
tlStr += ' </tr>\n'
|
tlStr += ' </tr>\n'
|
||||||
|
|
@ -7190,7 +7284,8 @@ def htmlUnfollowConfirm(translate: {}, baseDir: str,
|
||||||
|
|
||||||
|
|
||||||
def htmlPersonOptions(translate: {}, baseDir: str,
|
def htmlPersonOptions(translate: {}, baseDir: str,
|
||||||
domain: str, originPathStr: str,
|
domain: str, domainFull: str,
|
||||||
|
originPathStr: str,
|
||||||
optionsActor: str,
|
optionsActor: str,
|
||||||
optionsProfileUrl: str,
|
optionsProfileUrl: str,
|
||||||
optionsLink: str,
|
optionsLink: str,
|
||||||
|
|
@ -7328,24 +7423,37 @@ def htmlPersonOptions(translate: {}, baseDir: str,
|
||||||
'name="submitPetname">' + \
|
'name="submitPetname">' + \
|
||||||
translate['Submit'] + '</button><br>\n'
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
|
||||||
|
# checkbox for receiving calendar events
|
||||||
if isFollowingActor(baseDir, nickname, domain, optionsActor):
|
if isFollowingActor(baseDir, nickname, domain, optionsActor):
|
||||||
if receivingCalendarEvents(baseDir, nickname, domain,
|
checkboxStr = \
|
||||||
optionsNickname, optionsDomainFull):
|
|
||||||
optionsStr += \
|
|
||||||
' <input type="checkbox" ' + \
|
' <input type="checkbox" ' + \
|
||||||
'class="profilecheckbox" name="onCalendar" checked> ' + \
|
'class="profilecheckbox" name="onCalendar" checked> ' + \
|
||||||
translate['Receive calendar events from this account'] + \
|
translate['Receive calendar events from this account'] + \
|
||||||
'\n <button type="submit" class="buttonsmall" ' + \
|
'\n <button type="submit" class="buttonsmall" ' + \
|
||||||
'name="submitOnCalendar">' + \
|
'name="submitOnCalendar">' + \
|
||||||
translate['Submit'] + '</button><br>\n'
|
translate['Submit'] + '</button><br>\n'
|
||||||
else:
|
if not receivingCalendarEvents(baseDir, nickname, domain,
|
||||||
optionsStr += \
|
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" ' + \
|
' <input type="checkbox" ' + \
|
||||||
'class="profilecheckbox" name="onCalendar"> ' + \
|
'class="profilecheckbox" name="postsToNews" checked> ' + \
|
||||||
translate['Receive calendar events from this account'] + \
|
translate['Allow news posts'] + \
|
||||||
'\n <button type="submit" class="buttonsmall" ' + \
|
'\n <button type="submit" class="buttonsmall" ' + \
|
||||||
'name="submitOnCalendar">' + \
|
'name="submitPostToNews">' + \
|
||||||
translate['Submit'] + '</button><br>\n'
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
if os.path.isfile(newswireBlockedFilename):
|
||||||
|
checkboxStr = checkboxStr.replace(' checked>', '>')
|
||||||
|
optionsStr += checkboxStr
|
||||||
|
|
||||||
optionsStr += optionsLinkStr
|
optionsStr += optionsLinkStr
|
||||||
optionsStr += \
|
optionsStr += \
|
||||||
|
|
|
||||||