mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon into main
commit
b2defda3c9
|
|
@ -788,6 +788,13 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
|
||||||
prevWordStr = ''
|
prevWordStr = ''
|
||||||
continue
|
continue
|
||||||
elif firstChar == '#':
|
elif firstChar == '#':
|
||||||
|
# remove any endings from the hashtag
|
||||||
|
hashTagEndings = ('.', ':', ';', '-', '\n')
|
||||||
|
for ending in hashTagEndings:
|
||||||
|
if wordStr.endswith(ending):
|
||||||
|
wordStr = wordStr[:len(wordStr) - 1]
|
||||||
|
break
|
||||||
|
|
||||||
if _addHashTags(wordStr, httpPrefix, originalDomain,
|
if _addHashTags(wordStr, httpPrefix, originalDomain,
|
||||||
replaceHashTags, hashtags):
|
replaceHashTags, hashtags):
|
||||||
prevWordStr = ''
|
prevWordStr = ''
|
||||||
|
|
|
||||||
47
daemon.py
47
daemon.py
|
|
@ -218,6 +218,7 @@ from utils import loadJson
|
||||||
from utils import saveJson
|
from utils import saveJson
|
||||||
from utils import isSuspended
|
from utils import isSuspended
|
||||||
from utils import dangerousMarkup
|
from utils import dangerousMarkup
|
||||||
|
from utils import refreshNewswire
|
||||||
from manualapprove import manualDenyFollowRequest
|
from manualapprove import manualDenyFollowRequest
|
||||||
from manualapprove import manualApproveFollowRequest
|
from manualapprove import manualApproveFollowRequest
|
||||||
from announce import createAnnounce
|
from announce import createAnnounce
|
||||||
|
|
@ -255,7 +256,6 @@ from newswire import rss2Footer
|
||||||
from newswire import loadHashtagCategories
|
from newswire import loadHashtagCategories
|
||||||
from newsdaemon import runNewswireWatchdog
|
from newsdaemon import runNewswireWatchdog
|
||||||
from newsdaemon import runNewswireDaemon
|
from newsdaemon import runNewswireDaemon
|
||||||
from newsdaemon import refreshNewswire
|
|
||||||
from filters import isFiltered
|
from filters import isFiltered
|
||||||
from filters import addGlobalFilter
|
from filters import addGlobalFilter
|
||||||
from filters import removeGlobalFilter
|
from filters import removeGlobalFilter
|
||||||
|
|
@ -1948,12 +1948,50 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if postsToNews == 'on':
|
if postsToNews == 'on':
|
||||||
if os.path.isfile(newswireBlockedFilename):
|
if os.path.isfile(newswireBlockedFilename):
|
||||||
os.remove(newswireBlockedFilename)
|
os.remove(newswireBlockedFilename)
|
||||||
|
refreshNewswire(self.server.baseDir)
|
||||||
else:
|
else:
|
||||||
if os.path.isdir(accountDir):
|
if os.path.isdir(accountDir):
|
||||||
noNewswireFile = open(newswireBlockedFilename, "w+")
|
noNewswireFile = open(newswireBlockedFilename, "w+")
|
||||||
if noNewswireFile:
|
if noNewswireFile:
|
||||||
noNewswireFile.write('\n')
|
noNewswireFile.write('\n')
|
||||||
noNewswireFile.close()
|
noNewswireFile.close()
|
||||||
|
refreshNewswire(self.server.baseDir)
|
||||||
|
usersPathStr = \
|
||||||
|
usersPath + '/' + self.server.defaultTimeline + \
|
||||||
|
'?page=' + str(pageNumber)
|
||||||
|
self._redirect_headers(usersPathStr, cookie,
|
||||||
|
callingDomain)
|
||||||
|
self.server.POSTbusy = False
|
||||||
|
return
|
||||||
|
|
||||||
|
# person options screen, permission to post to featured articles
|
||||||
|
# See htmlPersonOptions
|
||||||
|
if '&submitPostToFeatures=' in optionsConfirmParams:
|
||||||
|
adminNickname = getConfigParam(self.server.baseDir, 'admin')
|
||||||
|
if (chooserNickname != optionsNickname and
|
||||||
|
(chooserNickname == adminNickname or
|
||||||
|
(isModerator(self.server.baseDir, chooserNickname) and
|
||||||
|
not isModerator(self.server.baseDir, optionsNickname)))):
|
||||||
|
postsToFeatures = None
|
||||||
|
if 'postsToFeatures=' in optionsConfirmParams:
|
||||||
|
postsToFeatures = \
|
||||||
|
optionsConfirmParams.split('postsToFeatures=')[1]
|
||||||
|
if '&' in postsToFeatures:
|
||||||
|
postsToFeatures = postsToFeatures.split('&')[0]
|
||||||
|
accountDir = self.server.baseDir + '/accounts/' + \
|
||||||
|
optionsNickname + '@' + optionsDomain
|
||||||
|
featuresBlockedFilename = accountDir + '/.nofeatures'
|
||||||
|
if postsToFeatures == 'on':
|
||||||
|
if os.path.isfile(featuresBlockedFilename):
|
||||||
|
os.remove(featuresBlockedFilename)
|
||||||
|
refreshNewswire(self.server.baseDir)
|
||||||
|
else:
|
||||||
|
if os.path.isdir(accountDir):
|
||||||
|
noFeaturesFile = open(featuresBlockedFilename, "w+")
|
||||||
|
if noFeaturesFile:
|
||||||
|
noFeaturesFile.write('\n')
|
||||||
|
noFeaturesFile.close()
|
||||||
|
refreshNewswire(self.server.baseDir)
|
||||||
usersPathStr = \
|
usersPathStr = \
|
||||||
usersPath + '/' + self.server.defaultTimeline + \
|
usersPath + '/' + self.server.defaultTimeline + \
|
||||||
'?page=' + str(pageNumber)
|
'?page=' + str(pageNumber)
|
||||||
|
|
@ -5469,7 +5507,9 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.dormantMonths,
|
self.server.dormantMonths,
|
||||||
backToPath,
|
backToPath,
|
||||||
lockedAccount,
|
lockedAccount,
|
||||||
movedTo, alsoKnownAs).encode('utf-8')
|
movedTo, alsoKnownAs,
|
||||||
|
self.server.textModeBanner,
|
||||||
|
self.server.newsInstance).encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
self._set_headers('text/html', msglen,
|
self._set_headers('text/html', msglen,
|
||||||
cookie, callingDomain)
|
cookie, callingDomain)
|
||||||
|
|
@ -11085,7 +11125,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.translate,
|
self.server.translate,
|
||||||
self.server.baseDir, self.path,
|
self.server.baseDir, self.path,
|
||||||
self.server.httpPrefix,
|
self.server.httpPrefix,
|
||||||
self.server.domainFull).encode('utf-8')
|
self.server.domainFull,
|
||||||
|
self.server.textModeBanner).encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
self._set_headers('text/html', msglen, cookie, callingDomain)
|
self._set_headers('text/html', msglen, cookie, callingDomain)
|
||||||
self._write(msg)
|
self._write(msg)
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"android": "android",
|
||||||
"popcorn": "1F37F",
|
"popcorn": "1F37F",
|
||||||
"1stplacemedal": "1F947",
|
"1stplacemedal": "1F947",
|
||||||
"abbutton": "1F18E",
|
"abbutton": "1F18E",
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,14 @@ a:focus {
|
||||||
border: 2px solid var(--focus-color);
|
border: 2px solid var(--focus-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transparent {
|
||||||
|
color: transparent;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 0px;
|
||||||
|
line-height: 0px;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.calendar__day__header,
|
.calendar__day__header,
|
||||||
.calendar__day__cell {
|
.calendar__day__cell {
|
||||||
border: 2px solid var(--lines-color);
|
border: 2px solid var(--lines-color);
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,14 @@ a:focus {
|
||||||
border: 2px solid var(--focus-color);
|
border: 2px solid var(--focus-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transparent {
|
||||||
|
color: transparent;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 0px;
|
||||||
|
line-height: 0px;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.follow {
|
.follow {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
||||||
|
|
@ -750,15 +750,3 @@ def runNewswireWatchdog(projectVersion: str, httpd) -> None:
|
||||||
newswireOriginal.clone(runNewswireDaemon)
|
newswireOriginal.clone(runNewswireDaemon)
|
||||||
httpd.thrNewswireDaemon.start()
|
httpd.thrNewswireDaemon.start()
|
||||||
print('Restarting newswire daemon...')
|
print('Restarting newswire daemon...')
|
||||||
|
|
||||||
|
|
||||||
def refreshNewswire(baseDir: str) -> None:
|
|
||||||
"""Causes the newswire to be updated.
|
|
||||||
This creates a file which is then detected by the daemon
|
|
||||||
"""
|
|
||||||
refreshFilename = baseDir + '/accounts/.refresh_newswire'
|
|
||||||
if os.path.isfile(refreshFilename):
|
|
||||||
return
|
|
||||||
refreshFile = open(refreshFilename, 'w+')
|
|
||||||
refreshFile.write('\n')
|
|
||||||
refreshFile.close()
|
|
||||||
|
|
|
||||||
137
newswire.py
137
newswire.py
|
|
@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net"
|
||||||
__status__ = "Production"
|
__status__ = "Production"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
import requests
|
import requests
|
||||||
from socket import error as SocketError
|
from socket import error as SocketError
|
||||||
import errno
|
import errno
|
||||||
|
|
@ -332,12 +333,14 @@ def _xml2StrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||||
result, pubDateStr,
|
result, pubDateStr,
|
||||||
title, link,
|
title, link,
|
||||||
votesStatus, postFilename,
|
votesStatus, postFilename,
|
||||||
description, moderated, mirrored)
|
description, moderated,
|
||||||
|
mirrored)
|
||||||
postCtr += 1
|
postCtr += 1
|
||||||
if postCtr >= maxPostsPerSource:
|
if postCtr >= maxPostsPerSource:
|
||||||
break
|
break
|
||||||
if postCtr > 0:
|
if postCtr > 0:
|
||||||
print('Added ' + str(postCtr) + ' rss 2.0 feed items to newswire')
|
print('Added ' + str(postCtr) +
|
||||||
|
' rss 2.0 feed items to newswire')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -416,12 +419,14 @@ def _xml1StrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||||
result, pubDateStr,
|
result, pubDateStr,
|
||||||
title, link,
|
title, link,
|
||||||
votesStatus, postFilename,
|
votesStatus, postFilename,
|
||||||
description, moderated, mirrored)
|
description, moderated,
|
||||||
|
mirrored)
|
||||||
postCtr += 1
|
postCtr += 1
|
||||||
if postCtr >= maxPostsPerSource:
|
if postCtr >= maxPostsPerSource:
|
||||||
break
|
break
|
||||||
if postCtr > 0:
|
if postCtr > 0:
|
||||||
print('Added ' + str(postCtr) + ' rss 1.0 feed items to newswire')
|
print('Added ' + str(postCtr) +
|
||||||
|
' rss 1.0 feed items to newswire')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -488,12 +493,124 @@ def _atomFeedToDict(baseDir: str, domain: str, xmlStr: str,
|
||||||
result, pubDateStr,
|
result, pubDateStr,
|
||||||
title, link,
|
title, link,
|
||||||
votesStatus, postFilename,
|
votesStatus, postFilename,
|
||||||
description, moderated, mirrored)
|
description, moderated,
|
||||||
|
mirrored)
|
||||||
postCtr += 1
|
postCtr += 1
|
||||||
if postCtr >= maxPostsPerSource:
|
if postCtr >= maxPostsPerSource:
|
||||||
break
|
break
|
||||||
if postCtr > 0:
|
if postCtr > 0:
|
||||||
print('Added ' + str(postCtr) + ' atom feed items to newswire')
|
print('Added ' + str(postCtr) +
|
||||||
|
' atom feed items to newswire')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _jsonFeedV1ToDict(baseDir: str, domain: str, xmlStr: str,
|
||||||
|
moderated: bool, mirrored: bool,
|
||||||
|
maxPostsPerSource: int,
|
||||||
|
maxFeedItemSizeKb: int) -> {}:
|
||||||
|
"""Converts a json feed string to a dictionary
|
||||||
|
See https://jsonfeed.org/version/1.1
|
||||||
|
"""
|
||||||
|
if '"items"' not in xmlStr:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
feedJson = json.loads(xmlStr)
|
||||||
|
except BaseException:
|
||||||
|
return {}
|
||||||
|
maxBytes = maxFeedItemSizeKb * 1024
|
||||||
|
if not feedJson.get('version'):
|
||||||
|
return {}
|
||||||
|
if not feedJson['version'].startswith('https://jsonfeed.org/version/1'):
|
||||||
|
return {}
|
||||||
|
if not feedJson.get('items'):
|
||||||
|
return {}
|
||||||
|
if not isinstance(feedJson['items'], list):
|
||||||
|
return {}
|
||||||
|
postCtr = 0
|
||||||
|
result = {}
|
||||||
|
for jsonFeedItem in feedJson['items']:
|
||||||
|
if not jsonFeedItem:
|
||||||
|
continue
|
||||||
|
if not isinstance(jsonFeedItem, dict):
|
||||||
|
continue
|
||||||
|
if not jsonFeedItem.get('url'):
|
||||||
|
continue
|
||||||
|
if not isinstance(jsonFeedItem['url'], str):
|
||||||
|
continue
|
||||||
|
if not jsonFeedItem.get('date_published'):
|
||||||
|
if not jsonFeedItem.get('date_modified'):
|
||||||
|
continue
|
||||||
|
if not jsonFeedItem.get('content_text'):
|
||||||
|
if not jsonFeedItem.get('content_html'):
|
||||||
|
continue
|
||||||
|
if jsonFeedItem.get('content_html'):
|
||||||
|
if not isinstance(jsonFeedItem['content_html'], str):
|
||||||
|
continue
|
||||||
|
title = removeHtml(jsonFeedItem['content_html'])
|
||||||
|
else:
|
||||||
|
if not isinstance(jsonFeedItem['content_text'], str):
|
||||||
|
continue
|
||||||
|
title = removeHtml(jsonFeedItem['content_text'])
|
||||||
|
if len(title) > maxBytes:
|
||||||
|
print('WARN: json feed title is too long')
|
||||||
|
continue
|
||||||
|
description = ''
|
||||||
|
if jsonFeedItem.get('description'):
|
||||||
|
if not isinstance(jsonFeedItem['description'], str):
|
||||||
|
continue
|
||||||
|
description = removeHtml(jsonFeedItem['description'])
|
||||||
|
if len(description) > maxBytes:
|
||||||
|
print('WARN: json feed description is too long')
|
||||||
|
continue
|
||||||
|
if jsonFeedItem.get('tags'):
|
||||||
|
if isinstance(jsonFeedItem['tags'], list):
|
||||||
|
for tagName in jsonFeedItem['tags']:
|
||||||
|
if not isinstance(tagName, str):
|
||||||
|
continue
|
||||||
|
if ' ' in tagName:
|
||||||
|
continue
|
||||||
|
if not tagName.startswith('#'):
|
||||||
|
tagName = '#' + tagName
|
||||||
|
if tagName not in description:
|
||||||
|
description += ' ' + tagName
|
||||||
|
|
||||||
|
link = jsonFeedItem['url']
|
||||||
|
if '://' not in link:
|
||||||
|
continue
|
||||||
|
if len(link) > maxBytes:
|
||||||
|
print('WARN: json feed link is too long')
|
||||||
|
continue
|
||||||
|
itemDomain = link.split('://')[1]
|
||||||
|
if '/' in itemDomain:
|
||||||
|
itemDomain = itemDomain.split('/')[0]
|
||||||
|
if isBlockedDomain(baseDir, itemDomain):
|
||||||
|
continue
|
||||||
|
if jsonFeedItem.get('date_published'):
|
||||||
|
if not isinstance(jsonFeedItem['date_published'], str):
|
||||||
|
continue
|
||||||
|
pubDate = jsonFeedItem['date_published']
|
||||||
|
else:
|
||||||
|
if not isinstance(jsonFeedItem['date_modified'], str):
|
||||||
|
continue
|
||||||
|
pubDate = jsonFeedItem['date_modified']
|
||||||
|
|
||||||
|
pubDateStr = parseFeedDate(pubDate)
|
||||||
|
if pubDateStr:
|
||||||
|
if _validFeedDate(pubDateStr):
|
||||||
|
postFilename = ''
|
||||||
|
votesStatus = []
|
||||||
|
_addNewswireDictEntry(baseDir, domain,
|
||||||
|
result, pubDateStr,
|
||||||
|
title, link,
|
||||||
|
votesStatus, postFilename,
|
||||||
|
description, moderated,
|
||||||
|
mirrored)
|
||||||
|
postCtr += 1
|
||||||
|
if postCtr >= maxPostsPerSource:
|
||||||
|
break
|
||||||
|
if postCtr > 0:
|
||||||
|
print('Added ' + str(postCtr) +
|
||||||
|
' json feed items to newswire')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -593,6 +710,10 @@ def _xmlStrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||||
return _atomFeedToDict(baseDir, domain,
|
return _atomFeedToDict(baseDir, domain,
|
||||||
xmlStr, moderated, mirrored,
|
xmlStr, moderated, mirrored,
|
||||||
maxPostsPerSource, maxFeedItemSizeKb)
|
maxPostsPerSource, maxFeedItemSizeKb)
|
||||||
|
elif 'https://jsonfeed.org/version/1' in xmlStr:
|
||||||
|
return _jsonFeedV1ToDict(baseDir, domain,
|
||||||
|
xmlStr, moderated, mirrored,
|
||||||
|
maxPostsPerSource, maxFeedItemSizeKb)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -794,7 +915,7 @@ def _addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str,
|
||||||
locatePost(baseDir, nickname,
|
locatePost(baseDir, nickname,
|
||||||
domain, postUrl, False)
|
domain, postUrl, False)
|
||||||
if not fullPostFilename:
|
if not fullPostFilename:
|
||||||
print('Unable to locate post ' + postUrl)
|
print('Unable to locate post for newswire ' + postUrl)
|
||||||
ctr += 1
|
ctr += 1
|
||||||
if ctr >= maxBlogsPerAccount:
|
if ctr >= maxBlogsPerAccount:
|
||||||
break
|
break
|
||||||
|
|
@ -840,7 +961,7 @@ def _addBlogsToNewswire(baseDir: str, domain: str, newswire: {},
|
||||||
for handle in dirs:
|
for handle in dirs:
|
||||||
if '@' not in handle:
|
if '@' not in handle:
|
||||||
continue
|
continue
|
||||||
if 'inbox@' in handle:
|
if 'inbox@' in handle or 'news@' in handle:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
nickname = handle.split('@')[0]
|
nickname = handle.split('@')[0]
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ from utils import getFullDomain
|
||||||
from utils import removeIdEnding
|
from utils import removeIdEnding
|
||||||
from utils import getDomainFromActor
|
from utils import getDomainFromActor
|
||||||
from utils import dangerousMarkup
|
from utils import dangerousMarkup
|
||||||
|
from utils import isFeaturedWriter
|
||||||
from blocking import isBlockedDomain
|
from blocking import isBlockedDomain
|
||||||
from blocking import outboxBlock
|
from blocking import outboxBlock
|
||||||
from blocking import outboxUndoBlock
|
from blocking import outboxUndoBlock
|
||||||
|
|
@ -211,8 +212,10 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str,
|
||||||
# save all instance blogs to the news actor
|
# save all instance blogs to the news actor
|
||||||
if postToNickname != 'news' and outboxName == 'tlblogs':
|
if postToNickname != 'news' and outboxName == 'tlblogs':
|
||||||
if '/' in savedFilename:
|
if '/' in savedFilename:
|
||||||
|
if isFeaturedWriter(baseDir, postToNickname, domain):
|
||||||
savedPostId = savedFilename.split('/')[-1]
|
savedPostId = savedFilename.split('/')[-1]
|
||||||
blogsDir = baseDir + '/accounts/news@' + domain + '/tlblogs'
|
blogsDir = \
|
||||||
|
baseDir + '/accounts/news@' + domain + '/tlblogs'
|
||||||
if not os.path.isdir(blogsDir):
|
if not os.path.isdir(blogsDir):
|
||||||
os.mkdir(blogsDir)
|
os.mkdir(blogsDir)
|
||||||
copyfile(savedFilename, blogsDir + '/' + savedPostId)
|
copyfile(savedFilename, blogsDir + '/' + savedPostId)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ from utils import loadJson
|
||||||
from utils import saveJson
|
from utils import saveJson
|
||||||
from utils import setConfigParam
|
from utils import setConfigParam
|
||||||
from utils import getConfigParam
|
from utils import getConfigParam
|
||||||
|
from utils import refreshNewswire
|
||||||
|
|
||||||
|
|
||||||
def generateRSAKey() -> (str, str):
|
def generateRSAKey() -> (str, str):
|
||||||
|
|
@ -915,6 +916,9 @@ def removeAccount(baseDir: str, nickname: str,
|
||||||
os.remove(baseDir + '/wfdeactivated/' + handle + '.json')
|
os.remove(baseDir + '/wfdeactivated/' + handle + '.json')
|
||||||
if os.path.isdir(baseDir + '/sharefilesdeactivated/' + nickname):
|
if os.path.isdir(baseDir + '/sharefilesdeactivated/' + nickname):
|
||||||
shutil.rmtree(baseDir + '/sharefilesdeactivated/' + nickname)
|
shutil.rmtree(baseDir + '/sharefilesdeactivated/' + nickname)
|
||||||
|
|
||||||
|
refreshNewswire(baseDir)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -944,6 +948,9 @@ def deactivateAccount(baseDir: str, nickname: str, domain: str) -> bool:
|
||||||
os.mkdir(deactivatedSharefilesDir)
|
os.mkdir(deactivatedSharefilesDir)
|
||||||
shutil.move(baseDir + '/sharefiles/' + nickname,
|
shutil.move(baseDir + '/sharefiles/' + nickname,
|
||||||
deactivatedSharefilesDir + '/' + nickname)
|
deactivatedSharefilesDir + '/' + nickname)
|
||||||
|
|
||||||
|
refreshNewswire(baseDir)
|
||||||
|
|
||||||
return os.path.isdir(deactivatedDir + '/' + nickname + '@' + domain)
|
return os.path.isdir(deactivatedDir + '/' + nickname + '@' + domain)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -970,6 +977,8 @@ def activateAccount(baseDir: str, nickname: str, domain: str) -> None:
|
||||||
shutil.move(deactivatedSharefilesDir + '/' + nickname,
|
shutil.move(deactivatedSharefilesDir + '/' + nickname,
|
||||||
baseDir + '/sharefiles/' + nickname)
|
baseDir + '/sharefiles/' + nickname)
|
||||||
|
|
||||||
|
refreshNewswire(baseDir)
|
||||||
|
|
||||||
|
|
||||||
def isPersonSnoozed(baseDir: str, nickname: str, domain: str,
|
def isPersonSnoozed(baseDir: str, nickname: str, domain: str,
|
||||||
snoozeActor: str) -> bool:
|
snoozeActor: str) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "تخطي إلى الجدول الزمني",
|
"Skip to timeline": "تخطي إلى الجدول الزمني",
|
||||||
"Skip to Newswire": "انتقل إلى Newswire",
|
"Skip to Newswire": "انتقل إلى Newswire",
|
||||||
"Skip to Links": "تخطي إلى روابط الويب",
|
"Skip to Links": "تخطي إلى روابط الويب",
|
||||||
"Publish a blog article": "نشر مقال بلوق"
|
"Publish a blog article": "نشر مقال بلوق",
|
||||||
|
"Featured writer": "كاتب متميز"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Ves a la cronologia",
|
"Skip to timeline": "Ves a la cronologia",
|
||||||
"Skip to Newswire": "Vés a Newswire",
|
"Skip to Newswire": "Vés a Newswire",
|
||||||
"Skip to Links": "Vés als enllaços web",
|
"Skip to Links": "Vés als enllaços web",
|
||||||
"Publish a blog article": "Publicar un article del bloc"
|
"Publish a blog article": "Publicar un article del bloc",
|
||||||
|
"Featured writer": "Escriptor destacat"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Neidio i'r llinell amser",
|
"Skip to timeline": "Neidio i'r llinell amser",
|
||||||
"Skip to Newswire": "Neidio i Newswire",
|
"Skip to Newswire": "Neidio i Newswire",
|
||||||
"Skip to Links": "Neidio i Dolenni Gwe",
|
"Skip to Links": "Neidio i Dolenni Gwe",
|
||||||
"Publish a blog article": "Cyhoeddi erthygl blog"
|
"Publish a blog article": "Cyhoeddi erthygl blog",
|
||||||
|
"Featured writer": "Awdur dan sylw"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Zur Zeitleiste springen",
|
"Skip to timeline": "Zur Zeitleiste springen",
|
||||||
"Skip to Newswire": "Springe zu Newswire",
|
"Skip to Newswire": "Springe zu Newswire",
|
||||||
"Skip to Links": "Springe zu Weblinks",
|
"Skip to Links": "Springe zu Weblinks",
|
||||||
"Publish a blog article": "Veröffentlichen Sie einen Blog-Artikel"
|
"Publish a blog article": "Veröffentlichen Sie einen Blog-Artikel",
|
||||||
|
"Featured writer": "Ausgewählter Schriftsteller"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Skip to timeline",
|
"Skip to timeline": "Skip to timeline",
|
||||||
"Skip to Newswire": "Skip to Newswire",
|
"Skip to Newswire": "Skip to Newswire",
|
||||||
"Skip to Links": "Skip to Links",
|
"Skip to Links": "Skip to Links",
|
||||||
"Publish a blog article": "Publish a blog article"
|
"Publish a blog article": "Publish a blog article",
|
||||||
|
"Featured writer": "Featured writer"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Saltar a la línea de tiempo",
|
"Skip to timeline": "Saltar a la línea de tiempo",
|
||||||
"Skip to Newswire": "Saltar a Newswire",
|
"Skip to Newswire": "Saltar a Newswire",
|
||||||
"Skip to Links": "Saltar a enlaces web",
|
"Skip to Links": "Saltar a enlaces web",
|
||||||
"Publish a blog article": "Publica un artículo de blog"
|
"Publish a blog article": "Publica un artículo de blog",
|
||||||
|
"Featured writer": "Escritora destacada"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Passer à la chronologie",
|
"Skip to timeline": "Passer à la chronologie",
|
||||||
"Skip to Newswire": "Passer à Newswire",
|
"Skip to Newswire": "Passer à Newswire",
|
||||||
"Skip to Links": "Passer aux liens Web",
|
"Skip to Links": "Passer aux liens Web",
|
||||||
"Publish a blog article": "Publier un article de blog"
|
"Publish a blog article": "Publier un article de blog",
|
||||||
|
"Featured writer": "Écrivain en vedette"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Scipeáil chuig an amlíne",
|
"Skip to timeline": "Scipeáil chuig an amlíne",
|
||||||
"Skip to Newswire": "Scipeáil chuig Newswire",
|
"Skip to Newswire": "Scipeáil chuig Newswire",
|
||||||
"Skip to Links": "Scipeáil chuig Naisc Ghréasáin",
|
"Skip to Links": "Scipeáil chuig Naisc Ghréasáin",
|
||||||
"Publish a blog article": "Foilsigh alt blagála"
|
"Publish a blog article": "Foilsigh alt blagála",
|
||||||
|
"Featured writer": "Scríbhneoir mór le rá"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "टाइमलाइन पर जाएं",
|
"Skip to timeline": "टाइमलाइन पर जाएं",
|
||||||
"Skip to Newswire": "Newswire पर जाएं",
|
"Skip to Newswire": "Newswire पर जाएं",
|
||||||
"Skip to Links": "वेब लिंक पर जाएं",
|
"Skip to Links": "वेब लिंक पर जाएं",
|
||||||
"Publish a blog article": "एक ब्लॉग लेख प्रकाशित करें"
|
"Publish a blog article": "एक ब्लॉग लेख प्रकाशित करें",
|
||||||
|
"Featured writer": "फीचर्ड लेखक"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Passa alla sequenza temporale",
|
"Skip to timeline": "Passa alla sequenza temporale",
|
||||||
"Skip to Newswire": "Passa a Newswire",
|
"Skip to Newswire": "Passa a Newswire",
|
||||||
"Skip to Links": "Passa a collegamenti Web",
|
"Skip to Links": "Passa a collegamenti Web",
|
||||||
"Publish a blog article": "Pubblica un articolo sul blog"
|
"Publish a blog article": "Pubblica un articolo sul blog",
|
||||||
|
"Featured writer": "Scrittore in primo piano"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "タイムラインにスキップ",
|
"Skip to timeline": "タイムラインにスキップ",
|
||||||
"Skip to Newswire": "Newswireにスキップ",
|
"Skip to Newswire": "Newswireにスキップ",
|
||||||
"Skip to Links": "Webリンクにスキップ",
|
"Skip to Links": "Webリンクにスキップ",
|
||||||
"Publish a blog article": "ブログ記事を公開する"
|
"Publish a blog article": "ブログ記事を公開する",
|
||||||
|
"Featured writer": "注目の作家"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -363,5 +363,6 @@
|
||||||
"Skip to timeline": "Skip to timeline",
|
"Skip to timeline": "Skip to timeline",
|
||||||
"Skip to Newswire": "Skip to Newswire",
|
"Skip to Newswire": "Skip to Newswire",
|
||||||
"Skip to Links": "Skip to Links",
|
"Skip to Links": "Skip to Links",
|
||||||
"Publish a blog article": "Publish a blog article"
|
"Publish a blog article": "Publish a blog article",
|
||||||
|
"Featured writer": "Featured writer"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Pular para a linha do tempo",
|
"Skip to timeline": "Pular para a linha do tempo",
|
||||||
"Skip to Newswire": "Pular para Newswire",
|
"Skip to Newswire": "Pular para Newswire",
|
||||||
"Skip to Links": "Pular para links da web",
|
"Skip to Links": "Pular para links da web",
|
||||||
"Publish a blog article": "Publique um artigo de blog"
|
"Publish a blog article": "Publique um artigo de blog",
|
||||||
|
"Featured writer": "Escritor em destaque"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "Перейти к временной шкале",
|
"Skip to timeline": "Перейти к временной шкале",
|
||||||
"Skip to Newswire": "Перейти к ленте новостей",
|
"Skip to Newswire": "Перейти к ленте новостей",
|
||||||
"Skip to Links": "Перейти к веб-ссылкам",
|
"Skip to Links": "Перейти к веб-ссылкам",
|
||||||
"Publish a blog article": "Опубликовать статью в блоге"
|
"Publish a blog article": "Опубликовать статью в блоге",
|
||||||
|
"Featured writer": "Избранный писатель"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,5 +367,6 @@
|
||||||
"Skip to timeline": "跳到时间线",
|
"Skip to timeline": "跳到时间线",
|
||||||
"Skip to Newswire": "跳到新闻专线",
|
"Skip to Newswire": "跳到新闻专线",
|
||||||
"Skip to Links": "跳到网页链接",
|
"Skip to Links": "跳到网页链接",
|
||||||
"Publish a blog article": "发布博客文章"
|
"Publish a blog article": "发布博客文章",
|
||||||
|
"Featured writer": "特色作家"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
utils.py
21
utils.py
|
|
@ -26,6 +26,27 @@ invalidCharacters = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def isFeaturedWriter(baseDir: str, nickname: str, domain: str) -> bool:
|
||||||
|
"""Is the given account a featured writer, appearing in the features
|
||||||
|
timeline on news instances?
|
||||||
|
"""
|
||||||
|
featuresBlockedFilename = \
|
||||||
|
baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/.nofeatures'
|
||||||
|
return not os.path.isfile(featuresBlockedFilename)
|
||||||
|
|
||||||
|
|
||||||
|
def refreshNewswire(baseDir: str):
|
||||||
|
"""Causes the newswire to be updates after a change to user accounts
|
||||||
|
"""
|
||||||
|
refreshNewswireFilename = baseDir + '/accounts/.refresh_newswire'
|
||||||
|
if os.path.isfile(refreshNewswireFilename):
|
||||||
|
return
|
||||||
|
refreshFile = open(refreshNewswireFilename, 'w+')
|
||||||
|
refreshFile.write('\n')
|
||||||
|
refreshFile.close()
|
||||||
|
|
||||||
|
|
||||||
def getSHA256(msg: str):
|
def getSHA256(msg: str):
|
||||||
"""Returns a SHA256 hash of the given string
|
"""Returns a SHA256 hash of the given string
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ from happening import getCalendarEvents
|
||||||
from webapp_utils import htmlHeaderWithExternalStyle
|
from webapp_utils import htmlHeaderWithExternalStyle
|
||||||
from webapp_utils import htmlFooter
|
from webapp_utils import htmlFooter
|
||||||
from webapp_utils import getAltPath
|
from webapp_utils import getAltPath
|
||||||
|
from webapp_utils import htmlHideFromScreenReader
|
||||||
|
from webapp_utils import htmlKeyboardNavigation
|
||||||
|
|
||||||
|
|
||||||
def htmlCalendarDeleteConfirm(cssCache: {}, translate: {}, baseDir: str,
|
def htmlCalendarDeleteConfirm(cssCache: {}, translate: {}, baseDir: str,
|
||||||
|
|
@ -200,7 +202,8 @@ def _htmlCalendarDay(cssCache: {}, translate: {},
|
||||||
|
|
||||||
def htmlCalendar(cssCache: {}, translate: {},
|
def htmlCalendar(cssCache: {}, translate: {},
|
||||||
baseDir: str, path: str,
|
baseDir: str, path: str,
|
||||||
httpPrefix: str, domainFull: str) -> str:
|
httpPrefix: str, domainFull: str,
|
||||||
|
textModeBanner: str) -> str:
|
||||||
"""Show the calendar for a person
|
"""Show the calendar for a person
|
||||||
"""
|
"""
|
||||||
domain = domainFull
|
domain = domainFull
|
||||||
|
|
@ -297,8 +300,10 @@ def htmlCalendar(cssCache: {}, translate: {},
|
||||||
|
|
||||||
instanceTitle = \
|
instanceTitle = \
|
||||||
getConfigParam(baseDir, 'instanceTitle')
|
getConfigParam(baseDir, 'instanceTitle')
|
||||||
calendarStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
|
headerStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
|
||||||
calendarStr += '<main><table class="calendar">\n'
|
|
||||||
|
# the main graphical calendar as a table
|
||||||
|
calendarStr = '<main><table class="calendar">\n'
|
||||||
calendarStr += '<caption class="calendar__banner--month">\n'
|
calendarStr += '<caption class="calendar__banner--month">\n'
|
||||||
calendarStr += \
|
calendarStr += \
|
||||||
' <a href="' + calActor + '/calendar?year=' + str(prevYear) + \
|
' <a href="' + calActor + '/calendar?year=' + str(prevYear) + \
|
||||||
|
|
@ -320,24 +325,30 @@ def htmlCalendar(cssCache: {}, translate: {},
|
||||||
calendarStr += '</caption>\n'
|
calendarStr += '</caption>\n'
|
||||||
calendarStr += '<thead>\n'
|
calendarStr += '<thead>\n'
|
||||||
calendarStr += '<tr>\n'
|
calendarStr += '<tr>\n'
|
||||||
calendarStr += ' <th class="calendar__day__header">' + \
|
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
|
||||||
translate['Sun'] + '</th>\n'
|
translate['Sun'] + '</th>\n'
|
||||||
calendarStr += ' <th class="calendar__day__header">' + \
|
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
|
||||||
translate['Mon'] + '</th>\n'
|
translate['Mon'] + '</th>\n'
|
||||||
calendarStr += ' <th class="calendar__day__header">' + \
|
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
|
||||||
translate['Tue'] + '</th>\n'
|
translate['Tue'] + '</th>\n'
|
||||||
calendarStr += ' <th class="calendar__day__header">' + \
|
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
|
||||||
translate['Wed'] + '</th>\n'
|
translate['Wed'] + '</th>\n'
|
||||||
calendarStr += ' <th class="calendar__day__header">' + \
|
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
|
||||||
translate['Thu'] + '</th>\n'
|
translate['Thu'] + '</th>\n'
|
||||||
calendarStr += ' <th class="calendar__day__header">' + \
|
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
|
||||||
translate['Fri'] + '</th>\n'
|
translate['Fri'] + '</th>\n'
|
||||||
calendarStr += ' <th class="calendar__day__header">' + \
|
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
|
||||||
translate['Sat'] + '</th>\n'
|
translate['Sat'] + '</th>\n'
|
||||||
calendarStr += '</tr>\n'
|
calendarStr += '</tr>\n'
|
||||||
calendarStr += '</thead>\n'
|
calendarStr += '</thead>\n'
|
||||||
calendarStr += '<tbody>\n'
|
calendarStr += '<tbody>\n'
|
||||||
|
|
||||||
|
# beginning of the links used for accessibility
|
||||||
|
navLinks = {}
|
||||||
|
timelineLinkStr = htmlHideFromScreenReader('🏠') + ' ' + \
|
||||||
|
translate['Switch to timeline view']
|
||||||
|
navLinks[timelineLinkStr] = calActor + '/inbox'
|
||||||
|
|
||||||
dayOfMonth = 0
|
dayOfMonth = 0
|
||||||
dow = weekDayOfMonthStart(monthNumber, year)
|
dow = weekDayOfMonthStart(monthNumber, year)
|
||||||
for weekOfMonth in range(1, 7):
|
for weekOfMonth in range(1, 7):
|
||||||
|
|
@ -358,8 +369,15 @@ def htmlCalendar(cssCache: {}, translate: {},
|
||||||
url = calActor + '/calendar?year=' + \
|
url = calActor + '/calendar?year=' + \
|
||||||
str(year) + '?month=' + \
|
str(year) + '?month=' + \
|
||||||
str(monthNumber) + '?day=' + str(dayOfMonth)
|
str(monthNumber) + '?day=' + str(dayOfMonth)
|
||||||
dayLink = '<a href="' + url + '">' + \
|
dayDescription = monthName + ' ' + str(dayOfMonth)
|
||||||
|
dayLink = '<a href="' + url + '" ' + \
|
||||||
|
'title="' + dayDescription + '">' + \
|
||||||
str(dayOfMonth) + '</a>'
|
str(dayOfMonth) + '</a>'
|
||||||
|
# accessibility menu links
|
||||||
|
menuOptionStr = \
|
||||||
|
htmlHideFromScreenReader('📅') + ' ' + \
|
||||||
|
dayDescription
|
||||||
|
navLinks[menuOptionStr] = url
|
||||||
# there are events for this day
|
# there are events for this day
|
||||||
if not isToday:
|
if not isToday:
|
||||||
calendarStr += \
|
calendarStr += \
|
||||||
|
|
@ -387,5 +405,17 @@ def htmlCalendar(cssCache: {}, translate: {},
|
||||||
|
|
||||||
calendarStr += '</tbody>\n'
|
calendarStr += '</tbody>\n'
|
||||||
calendarStr += '</table></main>\n'
|
calendarStr += '</table></main>\n'
|
||||||
calendarStr += htmlFooter()
|
|
||||||
return calendarStr
|
# end of the links used for accessibility
|
||||||
|
nextMonthStr = \
|
||||||
|
htmlHideFromScreenReader('→') + ' ' + translate['Next month']
|
||||||
|
navLinks[nextMonthStr] = calActor + '/calendar?year=' + str(nextYear) + \
|
||||||
|
'?month=' + str(nextMonthNumber)
|
||||||
|
prevMonthStr = \
|
||||||
|
htmlHideFromScreenReader('←') + ' ' + translate['Previous month']
|
||||||
|
navLinks[prevMonthStr] = calActor + '/calendar?year=' + str(prevYear) + \
|
||||||
|
'?month=' + str(prevMonthNumber)
|
||||||
|
screenReaderCal = \
|
||||||
|
htmlKeyboardNavigation(textModeBanner, navLinks, monthName)
|
||||||
|
|
||||||
|
return headerStr + screenReaderCal + calendarStr + htmlFooter()
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ from utils import isDormant
|
||||||
from utils import removeHtml
|
from utils import removeHtml
|
||||||
from utils import getDomainFromActor
|
from utils import getDomainFromActor
|
||||||
from utils import getNicknameFromActor
|
from utils import getNicknameFromActor
|
||||||
|
from utils import isFeaturedWriter
|
||||||
from blocking import isBlocked
|
from blocking import isBlocked
|
||||||
from follow import isFollowerOfPerson
|
from follow import isFollowerOfPerson
|
||||||
from follow import isFollowingActor
|
from follow import isFollowingActor
|
||||||
|
|
@ -24,6 +25,7 @@ from followingCalendar import receivingCalendarEvents
|
||||||
from webapp_utils import htmlHeaderWithExternalStyle
|
from webapp_utils import htmlHeaderWithExternalStyle
|
||||||
from webapp_utils import htmlFooter
|
from webapp_utils import htmlFooter
|
||||||
from webapp_utils import getBrokenLinkSubstitute
|
from webapp_utils import getBrokenLinkSubstitute
|
||||||
|
from webapp_utils import htmlKeyboardNavigation
|
||||||
|
|
||||||
|
|
||||||
def htmlPersonOptions(defaultTimeline: str,
|
def htmlPersonOptions(defaultTimeline: str,
|
||||||
|
|
@ -49,7 +51,9 @@ def htmlPersonOptions(defaultTimeline: str,
|
||||||
backToPath: str,
|
backToPath: str,
|
||||||
lockedAccount: bool,
|
lockedAccount: bool,
|
||||||
movedTo: str,
|
movedTo: str,
|
||||||
alsoKnownAs: []) -> str:
|
alsoKnownAs: [],
|
||||||
|
textModeBanner: str,
|
||||||
|
newsInstance: bool) -> str:
|
||||||
"""Show options for a person: view/follow/block/report
|
"""Show options for a person: view/follow/block/report
|
||||||
"""
|
"""
|
||||||
optionsDomain, optionsPort = getDomainFromActor(optionsActor)
|
optionsDomain, optionsPort = getDomainFromActor(optionsActor)
|
||||||
|
|
@ -108,12 +112,13 @@ def htmlPersonOptions(defaultTimeline: str,
|
||||||
if donateUrl:
|
if donateUrl:
|
||||||
donateStr = \
|
donateStr = \
|
||||||
' <a href="' + donateUrl + \
|
' <a href="' + donateUrl + \
|
||||||
'"><button class="button" name="submitDonate">' + \
|
' tabindex="-1""><button class="button" name="submitDonate">' + \
|
||||||
translate['Donate'] + '</button></a>\n'
|
translate['Donate'] + '</button></a>\n'
|
||||||
|
|
||||||
instanceTitle = \
|
instanceTitle = \
|
||||||
getConfigParam(baseDir, 'instanceTitle')
|
getConfigParam(baseDir, 'instanceTitle')
|
||||||
optionsStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
|
optionsStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
|
||||||
|
optionsStr += htmlKeyboardNavigation(textModeBanner, {})
|
||||||
optionsStr += '<br><br>\n'
|
optionsStr += '<br><br>\n'
|
||||||
optionsStr += '<div class="options">\n'
|
optionsStr += '<div class="options">\n'
|
||||||
optionsStr += ' <div class="optionsAvatar">\n'
|
optionsStr += ' <div class="optionsAvatar">\n'
|
||||||
|
|
@ -284,6 +289,24 @@ def htmlPersonOptions(defaultTimeline: str,
|
||||||
checkboxStr = checkboxStr.replace(' checked>', '>')
|
checkboxStr = checkboxStr.replace(' checked>', '>')
|
||||||
optionsStr += checkboxStr
|
optionsStr += checkboxStr
|
||||||
|
|
||||||
|
# checkbox for permission to post to featured articles
|
||||||
|
if newsInstance and optionsDomainFull == domainFull:
|
||||||
|
adminNickname = getConfigParam(baseDir, 'admin')
|
||||||
|
if (nickname == adminNickname or
|
||||||
|
(isModerator(baseDir, nickname) and
|
||||||
|
not isModerator(baseDir, optionsNickname))):
|
||||||
|
checkboxStr = \
|
||||||
|
' <input type="checkbox" ' + \
|
||||||
|
'class="profilecheckbox" name="postsToFeatures" checked> ' + \
|
||||||
|
translate['Featured writer'] + \
|
||||||
|
'\n <button type="submit" class="buttonsmall" ' + \
|
||||||
|
'name="submitPostToFeatures">' + \
|
||||||
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
if not isFeaturedWriter(baseDir, optionsNickname,
|
||||||
|
optionsDomain):
|
||||||
|
checkboxStr = checkboxStr.replace(' checked>', '>')
|
||||||
|
optionsStr += checkboxStr
|
||||||
|
|
||||||
optionsStr += optionsLinkStr
|
optionsStr += optionsLinkStr
|
||||||
backPath = '/'
|
backPath = '/'
|
||||||
if nickname:
|
if nickname:
|
||||||
|
|
|
||||||
|
|
@ -327,6 +327,10 @@ def _getEditIconHtml(baseDir: str, nickname: str, domainFull: str,
|
||||||
"""
|
"""
|
||||||
editStr = ''
|
editStr = ''
|
||||||
actor = postJsonObject['actor']
|
actor = postJsonObject['actor']
|
||||||
|
# This should either be a post which you created,
|
||||||
|
# or it could be generated from the newswire (see
|
||||||
|
# _addBlogsToNewswire) in which case anyone with
|
||||||
|
# editor status should be able to alter it
|
||||||
if (actor.endswith('/' + domainFull + '/users/' + nickname) or
|
if (actor.endswith('/' + domainFull + '/users/' + nickname) or
|
||||||
(isEditor(baseDir, nickname) and
|
(isEditor(baseDir, nickname) and
|
||||||
actor.endswith('/' + domainFull + '/users/news'))):
|
actor.endswith('/' + domainFull + '/users/news'))):
|
||||||
|
|
|
||||||
|
|
@ -1745,6 +1745,10 @@ def _individualFollowAsHtml(translate: {},
|
||||||
if avatarUrl2:
|
if avatarUrl2:
|
||||||
avatarUrl = avatarUrl2
|
avatarUrl = avatarUrl2
|
||||||
if displayName:
|
if displayName:
|
||||||
|
displayName = \
|
||||||
|
addEmojiToDisplayName(baseDir, httpPrefix,
|
||||||
|
actorNickname, domain,
|
||||||
|
displayName, False)
|
||||||
titleStr = displayName
|
titleStr = displayName
|
||||||
|
|
||||||
if dormant:
|
if dormant:
|
||||||
|
|
|
||||||
|
|
@ -432,7 +432,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
||||||
}
|
}
|
||||||
if moderator:
|
if moderator:
|
||||||
navLinks[menuModeration] = usersPath + '/moderation#modtimeline'
|
navLinks[menuModeration] = usersPath + '/moderation#modtimeline'
|
||||||
tlStr += htmlKeyboardNavigation(textModeBanner, navLinks,
|
tlStr += htmlKeyboardNavigation(textModeBanner, navLinks, None,
|
||||||
usersPath, translate, followApprovals)
|
usersPath, translate, followApprovals)
|
||||||
|
|
||||||
# banner and row of buttons
|
# banner and row of buttons
|
||||||
|
|
|
||||||
|
|
@ -887,6 +887,7 @@ def htmlHideFromScreenReader(htmlStr: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def htmlKeyboardNavigation(banner: str, links: {},
|
def htmlKeyboardNavigation(banner: str, links: {},
|
||||||
|
subHeading=None,
|
||||||
usersPath=None, translate=None,
|
usersPath=None, translate=None,
|
||||||
followApprovals=False) -> str:
|
followApprovals=False) -> str:
|
||||||
"""Given a set of links return the html for keyboard navigation
|
"""Given a set of links return the html for keyboard navigation
|
||||||
|
|
@ -896,6 +897,10 @@ def htmlKeyboardNavigation(banner: str, links: {},
|
||||||
if banner:
|
if banner:
|
||||||
htmlStr += '<pre aria-label="">' + banner + '<br><br></pre>'
|
htmlStr += '<pre aria-label="">' + banner + '<br><br></pre>'
|
||||||
|
|
||||||
|
if subHeading:
|
||||||
|
htmlStr += '<strong><label class="transparent">' + \
|
||||||
|
subHeading + '</label></strong><br>'
|
||||||
|
|
||||||
# show new follower approvals
|
# show new follower approvals
|
||||||
if usersPath and translate and followApprovals:
|
if usersPath and translate and followApprovals:
|
||||||
htmlStr += '<strong><label class="transparent">' + \
|
htmlStr += '<strong><label class="transparent">' + \
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue