Remove id endings in a better way

main
Bob Mottram 2020-08-23 12:13:35 +01:00
parent 8151f32196
commit 86f2e9d8ab
28 changed files with 282 additions and 69 deletions

View File

@ -129,7 +129,7 @@ def createAnnounce(session, baseDir: str, federationList: [],
'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
'atomUri': atomUriStr,
'cc': [],
'id': newAnnounceId+'/activity',
'id': newAnnounceId + '/activity',
'object': objectUrl,
'published': published,
'to': [toUrl],
@ -365,7 +365,7 @@ def sendAnnounceViaServer(baseDir: str, session,
'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname,
'atomUri': newAnnounceId,
'cc': [ccUrl],
'id': newAnnounceId+'/activity',
'id': newAnnounceId + '/activity',
'object': repeatObjectUrl,
'published': published,
'to': [toUrl],

View File

@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net"
__status__ = "Production"
import os
from utils import removeIdEnding
from utils import isEvil
from utils import locatePost
from utils import evilIncarnate
@ -214,7 +215,7 @@ def outboxBlock(baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: c2s block request arrived in outbox')
messageId = messageJson['object'].replace('/activity', '')
messageId = removeIdEnding(messageJson['object'])
if '/statuses/' not in messageId:
if debug:
print('DEBUG: c2s block object is not a status')
@ -293,7 +294,7 @@ def outboxUndoBlock(baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: c2s undo block request arrived in outbox')
messageId = messageJson['object']['object'].replace('/activity', '')
messageId = removeIdEnding(messageJson['object']['object'])
if '/statuses/' not in messageId:
if debug:
print('DEBUG: c2s undo block object is not a status')

View File

@ -8,6 +8,7 @@ __status__ = "Production"
import os
from pprint import pprint
from utils import removeIdEnding
from utils import removePostFromCache
from utils import urlPermitted
from utils import getNicknameFromActor
@ -607,7 +608,7 @@ def outboxBookmark(recentPostsCache: {},
if debug:
print('DEBUG: c2s bookmark request arrived in outbox')
messageId = messageJson['object'].replace('/activity', '')
messageId = removeIdEnding(messageJson['object'])
if ':' in domain:
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId)
@ -667,7 +668,7 @@ def outboxUndoBookmark(recentPostsCache: {},
if debug:
print('DEBUG: c2s undo bookmark request arrived in outbox')
messageId = messageJson['object']['object'].replace('/activity', '')
messageId = removeIdEnding(messageJson['object']['object'])
if ':' in domain:
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId)

104
daemon.py
View File

@ -127,6 +127,7 @@ from webinterface import htmlIndividualPost
from webinterface import htmlProfile
from webinterface import htmlInbox
from webinterface import htmlBookmarks
from webinterface import htmlEvents
from webinterface import htmlShares
from webinterface import htmlOutbox
from webinterface import htmlModeration
@ -154,6 +155,7 @@ from shares import getSharesFeedForPerson
from shares import addShare
from shares import removeShare
from shares import expireShares
from utils import removeIdEnding
from utils import updateLikesCollection
from utils import undoLikesCollectionEntry
from utils import deletePost
@ -2734,10 +2736,9 @@ class PubServer(BaseHTTPRequestHandler):
self._benchmarkGETtimings(GETstartTime, GETtimings, 32)
# unrepeatPrivate = False
if htmlGET and '?unrepeatprivate=' in self.path:
self.path = self.path.replace('?unrepeatprivate=', '?unrepeat=')
# unrepeatPrivate = True
# undo an announce/repeat from the web interface
if htmlGET and '?unrepeat=' in self.path:
pageNumber = 1
@ -4823,6 +4824,100 @@ class PubServer(BaseHTTPRequestHandler):
self.server.GETbusy = False
return
# get the events for a given person
if self.path.endswith('/tlevents') or \
'/tlevents?page=' in self.path or \
self.path.endswith('/events') or \
'/events?page=' in self.path:
if '/users/' in self.path:
if authorized:
eventsFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path,
self.server.httpPrefix,
maxPostsInFeed, 'tlevents',
authorized, self.server.ocapAlways)
if eventsFeed:
if self._requestHTTP():
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlevents', '')
nickname = nickname.replace('/events', '')
pageNumber = 1
if '?page=' in nickname:
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
if pageNumber.isdigit():
pageNumber = int(pageNumber)
else:
pageNumber = 1
if 'page=' not in self.path:
# if no page was specified then show the first
eventsFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
maxPostsInFeed,
'tlevents',
authorized,
self.server.ocapAlways)
msg = \
htmlEvents(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
eventsFeed,
self.server.allowDeletion,
self.server.httpPrefix,
self.server.projectVersion,
self._isMinimal(nickname),
self.server.YTReplacementDomain)
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
self._write(msg)
else:
# don't need authenticated fetch here because
# there is already the authorization check
msg = json.dumps(inboxFeed,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
self._write(msg)
self.server.GETbusy = False
return
else:
if self.server.debug:
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlevents', '')
nickname = nickname.replace('/events', '')
print('DEBUG: ' + nickname +
' was not authorized to access ' + self.path)
if self.server.debug:
print('DEBUG: GET access to events is unauthorized')
self.send_response(405)
self.end_headers()
self.server.GETbusy = False
return
self._benchmarkGETtimings(GETstartTime, GETtimings, 47)
# get outbox feed for a person
@ -7892,7 +7987,7 @@ class PubServer(BaseHTTPRequestHandler):
followId = followActor + '/statuses/' + str(statusNumber)
unfollowJson = {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': followId+'/undo',
'id': followId + '/undo',
'type': 'Undo',
'actor': followActor,
'object': {
@ -8682,8 +8777,7 @@ class PubServer(BaseHTTPRequestHandler):
if self.outboxAuthenticated:
if self._postToOutbox(messageJson, __version__):
if messageJson.get('id'):
locnStr = messageJson['id'].replace('/activity', '')
locnStr = locnStr.replace('/undo', '')
locnStr = removeIdEnding(messageJson['id'])
self.headers['Location'] = locnStr
self.send_response(201)
self.end_headers()

View File

@ -6,6 +6,7 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
from utils import removeIdEnding
from utils import getStatusNumber
from utils import urlPermitted
from utils import getNicknameFromActor
@ -257,7 +258,7 @@ def outboxDelete(baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: delete not permitted from other instances')
return
messageId = messageJson['object'].replace('/activity', '')
messageId = removeIdEnding(messageJson['object'])
if '/statuses/' not in messageId:
if debug:
print('DEBUG: c2s delete object is not a status')

View File

@ -10,6 +10,7 @@ import json
import os
import datetime
import time
from utils import removeIdEnding
from utils import getProtocolPrefixes
from utils import isBlogPost
from utils import removeAvatarFromCache
@ -93,7 +94,7 @@ def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
continue
tagName = tag['name'].replace('#', '').strip()
tagsFilename = tagsDir + '/' + tagName + '.txt'
postUrl = postJsonObject['id'].replace('/activity', '')
postUrl = removeIdEnding(postJsonObject['id'])
postUrl = postUrl.replace('/', '#')
daysDiff = datetime.datetime.utcnow() - datetime.datetime(1970, 1, 1)
daysSinceEpoch = daysDiff.days
@ -354,15 +355,13 @@ def savePostToInboxQueue(baseDir: str, httpPrefix: str,
return None
originalPostId = None
if postJsonObject.get('id'):
originalPostId = \
postJsonObject['id'].replace('/activity', '').replace('/undo', '')
originalPostId = removeIdEnding(postJsonObject['id'])
currTime = datetime.datetime.utcnow()
postId = None
if postJsonObject.get('id'):
postId = postJsonObject['id'].replace('/activity', '')
postId = postId.replace('/undo', '')
postId = removeIdEnding(postJsonObject['id'])
published = currTime.strftime("%Y-%m-%dT%H:%M:%SZ")
if not postId:
statusNumber, published = getStatusNumber()
@ -817,9 +816,7 @@ def receiveEventPost(recentPostsCache: {}, session, baseDir: str,
if port != 80 and port != 443:
handle += ':' + str(port)
postId = \
messageJson['id'].replace('/activity', '').replace('/', '#')
postId = postId.replace('/event', '')
postId = removeIdEnding(messageJson['id']).replace('/', '#')
saveEventPost(baseDir, handle, postId, messageJson['object'])
@ -910,7 +907,7 @@ def receiveUpdateToQuestion(recentPostsCache: {}, messageJson: {},
return
if not messageJson.get('actor'):
return
messageId = messageJson['id'].replace('/activity', '')
messageId = removeIdEnding(messageJson['id'])
if '#' in messageId:
messageId = messageId.split('#', 1)[0]
# find the question post
@ -1367,8 +1364,7 @@ def receiveDelete(session, handle: str, isGroup: bool, baseDir: str,
if not os.path.isdir(baseDir + '/accounts/' + handle):
print('DEBUG: unknown recipient of like - ' + handle)
# if this post in the outbox of the person?
messageId = messageJson['object'].replace('/activity', '')
messageId = messageId.replace('/undo', '')
messageId = removeIdEnding(messageJson['object'])
removeModerationPostFromIndex(baseDir, messageId, debug)
postFilename = locatePost(baseDir, handle.split('@')[0],
handle.split('@')[1], messageId)
@ -1653,8 +1649,7 @@ def populateReplies(baseDir: str, httpPrefix: str, domain: str,
return False
# populate a text file containing the ids of replies
postRepliesFilename = postFilename.replace('.json', '.replies')
messageId = messageJson['id'].replace('/activity', '')
messageId = messageId.replace('/undo', '')
messageId = removeIdEnding(messageJson['id'])
if os.path.isfile(postRepliesFilename):
numLines = sum(1 for line in open(postRepliesFilename))
if numLines > maxReplies:
@ -2070,8 +2065,7 @@ def inboxUpdateCalendar(baseDir: str, handle: str, postJsonObject: {}) -> None:
actorNickname, actorDomain):
return
postId = \
postJsonObject['id'].replace('/activity', '').replace('/', '#')
postId = removeIdEnding(postJsonObject['id']).replace('/', '#')
# look for events within the tags list
for tagDict in postJsonObject['object']['tag']:
@ -2375,6 +2369,9 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int,
if isBlogPost(postJsonObject):
# blogs index will be updated
updateIndexList.append('tlblogs')
elif isEventPost(postJsonObject):
# events index will be updated
updateIndexList.append('tlevents')
# get the avatar for a reply/announce
obtainAvatarForReplyPost(session, baseDir,

View File

@ -6,6 +6,7 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
from utils import removeIdEnding
from utils import urlPermitted
from utils import getNicknameFromActor
from utils import getDomainFromActor
@ -411,7 +412,7 @@ def outboxLike(recentPostsCache: {},
if debug:
print('DEBUG: c2s like request arrived in outbox')
messageId = messageJson['object'].replace('/activity', '')
messageId = removeIdEnding(messageJson['object'])
if ':' in domain:
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId)
@ -462,7 +463,7 @@ def outboxUndoLike(recentPostsCache: {},
if debug:
print('DEBUG: c2s undo like request arrived in outbox')
messageId = messageJson['object']['object'].replace('/activity', '')
messageId = removeIdEnding(messageJson['object']['object'])
if ':' in domain:
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId)

View File

@ -13,6 +13,7 @@ from posts import outboxMessageCreateWrap
from posts import savePostToBox
from posts import sendToFollowersThread
from posts import sendToNamedAddresses
from utils import removeIdEnding
from utils import getDomainFromActor
from blocking import isBlockedDomain
from blocking import outboxBlock
@ -159,8 +160,7 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str,
' is not a permitted activity type')
return False
if messageJson.get('id'):
postId = \
messageJson['id'].replace('/activity', '').replace('/undo', '')
postId = removeIdEnding(messageJson['id'])
if debug:
print('DEBUG: id attribute exists within POST to outbox')
else:
@ -172,13 +172,15 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str,
if messageJson['type'] != 'Upgrade':
outboxName = 'outbox'
# if this is a blog post then save to its own box
# if this is a blog post or an event then save to its own box
if messageJson['type'] == 'Create':
if messageJson.get('object'):
if isinstance(messageJson['object'], dict):
if messageJson['object'].get('type'):
if messageJson['object']['type'] == 'Article':
outboxName = 'tlblogs'
elif messageJson['object']['type'] == 'Event':
outboxName = 'tlevents'
savedFilename = \
savePostToBox(baseDir,
@ -196,7 +198,8 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str,
messageJson['type'] == 'Announce':
indexes = [outboxName, "inbox"]
for boxNameIndex in indexes:
if boxNameIndex == 'inbox' and outboxName == 'tlblogs':
if ((boxNameIndex == 'inbox') and
(outboxName == 'tlblogs' or outboxName == 'tlevents')):
continue
selfActor = \
httpPrefix + '://' + domainFull + \

View File

@ -25,6 +25,7 @@ from posts import createRepliesTimeline
from posts import createMediaTimeline
from posts import createBlogsTimeline
from posts import createBookmarksTimeline
from posts import createEventsTimeline
from posts import createInbox
from posts import createOutbox
from posts import createModeration
@ -598,7 +599,8 @@ def personBoxJson(recentPostsCache: {},
boxname != 'tlreplies' and boxname != 'tlmedia' and \
boxname != 'tlblogs' and \
boxname != 'outbox' and boxname != 'moderation' and \
boxname != 'tlbookmarks' and boxname != 'bookmarks':
boxname != 'tlbookmarks' and boxname != 'bookmarks' and \
boxname != 'tlevents' and boxname != 'events':
return None
if not '/' + boxname in path:
@ -646,6 +648,11 @@ def personBoxJson(recentPostsCache: {},
port, httpPrefix,
noOfItems, headerOnly, ocapAlways,
pageNumber)
elif boxname == 'tlevents' or boxname == 'events':
return createEventsTimeline(session, baseDir, nickname, domain,
port, httpPrefix,
noOfItems, headerOnly, ocapAlways,
pageNumber)
elif boxname == 'tlreplies':
return createRepliesTimeline(session, baseDir, nickname, domain,
port, httpPrefix,

View File

@ -29,6 +29,7 @@ from session import postJsonString
from session import postImage
from webfinger import webfingerHandle
from httpsig import createSignedHeader
from utils import removeIdEnding
from utils import siteIsActive
from utils import removePostFromCache
from utils import getCachedPostFilename
@ -502,7 +503,8 @@ def deleteAllPosts(baseDir: str,
nickname: str, domain: str, boxname: str) -> None:
"""Deletes all posts for a person from inbox or outbox
"""
if boxname != 'inbox' and boxname != 'outbox' and boxname != 'tlblogs':
if boxname != 'inbox' and boxname != 'outbox' and \
boxname != 'tlblogs' and boxname != 'tlevents':
return
boxDir = createPersonDir(nickname, domain, baseDir, boxname)
for deleteFilename in os.scandir(boxDir):
@ -524,7 +526,8 @@ def savePostToBox(baseDir: str, httpPrefix: str, postId: str,
Returns the filename
"""
if boxname != 'inbox' and boxname != 'outbox' and \
boxname != 'tlblogs' and boxname != 'scheduled':
boxname != 'tlblogs' and boxname != 'tlevents' and \
boxname != 'scheduled':
return None
originalDomain = domain
if ':' in domain:
@ -845,7 +848,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int,
'/statuses/' + statusNumber + '/replies'
newPost = {
'@context': postContext,
'id': newPostId+'/activity',
'id': newPostId + '/activity',
'capability': capabilityIdList,
'type': 'Create',
'actor': actorUrl,
@ -977,12 +980,15 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int,
'date and time values')
return newPost
elif saveToFile:
if not isArticle:
savePostToBox(baseDir, httpPrefix, newPostId,
nickname, domain, newPost, 'outbox')
else:
if isArticle:
savePostToBox(baseDir, httpPrefix, newPostId,
nickname, domain, newPost, 'tlblogs')
elif eventUUID:
savePostToBox(baseDir, httpPrefix, newPostId,
nickname, domain, newPost, 'tlevents')
else:
savePostToBox(baseDir, httpPrefix, newPostId,
nickname, domain, newPost, 'outbox')
return newPost
@ -1009,10 +1015,10 @@ def outboxMessageCreateWrap(httpPrefix: str,
capabilityUrl = []
newPost = {
"@context": "https://www.w3.org/ns/activitystreams",
'id': newPostId+'/activity',
'id': newPostId + '/activity',
'capability': capabilityUrl,
'type': 'Create',
'actor': httpPrefix+'://'+domain+'/users/'+nickname,
'actor': httpPrefix + '://' + domain + '/users/' + nickname,
'published': published,
'to': messageJson['to'],
'cc': cc,
@ -1473,8 +1479,9 @@ def createReportPost(baseDir: str,
continue
# update the inbox index with the report filename
# indexFilename=baseDir+'/accounts/'+handle+'/inbox.index'
# indexEntry=postJsonObject['id'].replace('/activity','').replace('/','#')+'.json'
# indexFilename = baseDir+'/accounts/'+handle+'/inbox.index'
# indexEntry = \
# removeIdEnding(postJsonObject['id']).replace('/','#') + '.json'
# if indexEntry not in open(indexFilename).read():
# try:
# with open(indexFilename, 'a+') as fp:
@ -2419,6 +2426,16 @@ def createBookmarksTimeline(session, baseDir: str, nickname: str, domain: str,
True, ocapAlways, pageNumber)
def createEventsTimeline(session, baseDir: str, nickname: str, domain: str,
port: int, httpPrefix: str, itemsPerPage: int,
headerOnly: bool, ocapAlways: bool,
pageNumber=None) -> {}:
return createBoxIndexed({}, session, baseDir, 'tlevents',
nickname, domain,
port, httpPrefix, itemsPerPage, headerOnly,
True, ocapAlways, pageNumber)
def createDMTimeline(session, baseDir: str, nickname: str, domain: str,
port: int, httpPrefix: str, itemsPerPage: int,
headerOnly: bool, ocapAlways: bool,
@ -2791,14 +2808,19 @@ def createBoxIndexed(recentPostsCache: {},
boxname != 'tlreplies' and boxname != 'tlmedia' and \
boxname != 'tlblogs' and \
boxname != 'outbox' and boxname != 'tlbookmarks' and \
boxname != 'bookmarks':
boxname != 'bookmarks' and \
boxname != 'tlevents' and boxname != 'events':
return None
# bookmarks timeline is like the inbox but has its own separate index
# bookmarks and events timelines are like the inbox
# but have their own separate index
indexBoxName = boxname
if boxname == "tlbookmarks":
boxname = "bookmarks"
indexBoxName = boxname
elif boxname == "tlevents":
boxname = "events"
indexBoxName = boxname
if port:
if port != 80 and port != 443:
@ -3489,7 +3511,7 @@ def mutePost(baseDir: str, nickname: str, domain: str, postId: str,
# if the post is in the recent posts cache then mark it as muted
if recentPostsCache.get('index'):
postId = \
postJsonObject['id'].replace('/activity', '').replace('/', '#')
removeIdEnding(postJsonObject['id']).replace('/', '#')
if postId in recentPostsCache['index']:
print('MUTE: ' + postId + ' is in recent posts cache')
if recentPostsCache['json'].get(postId):

View File

@ -31,6 +31,7 @@ from follow import clearFollows
from follow import clearFollowers
from follow import sendFollowRequestViaServer
from follow import sendUnfollowRequestViaServer
from utils import removeIdEnding
from utils import siteIsActive
from utils import updateRecentPostsCache
from utils import followPerson
@ -1364,7 +1365,7 @@ def testClientToServer():
outboxPostFilename = outboxPath + '/' + name
postJsonObject = loadJson(outboxPostFilename, 0)
if postJsonObject:
outboxPostId = postJsonObject['id'].replace('/activity', '')
outboxPostId = removeIdEnding(postJsonObject['id'])
assert outboxPostId
print('message id obtained: ' + outboxPostId)
assert validInbox(bobDir, 'bob', bobDomain)
@ -2014,8 +2015,35 @@ def testJsonPostAllowsComments():
assert not jsonPostAllowsComments(postJsonObject)
def testRemoveIdEnding():
print('testRemoveIdEnding')
testStr = 'https://activitypub.somedomain.net'
resultStr = removeIdEnding(testStr)
assert resultStr == 'https://activitypub.somedomain.net'
testStr = \
'https://activitypub.somedomain.net/users/foo/' + \
'statuses/34544814814/activity'
resultStr = removeIdEnding(testStr)
assert resultStr == \
'https://activitypub.somedomain.net/users/foo/statuses/34544814814'
testStr = \
'https://undo.somedomain.net/users/foo/statuses/34544814814/undo'
resultStr = removeIdEnding(testStr)
assert resultStr == \
'https://undo.somedomain.net/users/foo/statuses/34544814814'
testStr = \
'https://event.somedomain.net/users/foo/statuses/34544814814/event'
resultStr = removeIdEnding(testStr)
assert resultStr == \
'https://event.somedomain.net/users/foo/statuses/34544814814'
def runAllTests():
print('Running tests...')
testRemoveIdEnding()
testJsonPostAllowsComments()
runHtmlReplaceQuoteMarks()
testDangerousMarkup()

View File

@ -258,6 +258,7 @@
"Notes": "ملاحظات",
"Allow replies.": "السماح بالردود.",
"Event": "حدث",
"Events": "الأحداث",
"Create an event": "أنشئ حدثًا",
"Describe the event": "صف الحدث",
"Start Date": "تاريخ البدء",

View File

@ -258,6 +258,7 @@
"Notes": "Notes",
"Allow replies.": "Permetre respostes.",
"Event": "Esdeveniment",
"Events": "Esdeveniments",
"Create an event": "Crea un esdeveniment",
"Describe the event": "Descriviu lesdeveniment",
"Start Date": "Data d'inici",

View File

@ -258,6 +258,7 @@
"Notes": "Nodiadau",
"Allow replies.": "Caniatáu atebion.",
"Event": "Digwyddiad",
"Events": "Digwyddiadau",
"Create an event": "Creu digwyddiad",
"Describe the event": "Disgrifiwch y digwyddiad",
"Start Date": "Dyddiad cychwyn",

View File

@ -258,6 +258,7 @@
"Notes": "Anmerkungen",
"Allow replies.": "Antworten zulassen.",
"Event": "Veranstaltung",
"Events": "Veranstaltungen",
"Create an event": "Erstellen Sie ein Ereignis",
"Describe the event": "Beschreiben Sie das Ereignis",
"Start Date": "Anfangsdatum",

View File

@ -258,6 +258,7 @@
"Notes": "Notes",
"Allow replies.": "Allow replies.",
"Event": "Event",
"Events": "Events",
"Create an event": "Create an event",
"Describe the event": "Describe the event",
"Start Date": "Start Date",

View File

@ -258,6 +258,7 @@
"Notes": "Notas",
"Allow replies.": "Permitir respuestas.",
"Event": "Evento",
"Events": "Eventos",
"Create an event": "Crea un evento",
"Describe the event": "Describe el evento",
"Start Date": "Fecha de inicio",

View File

@ -258,6 +258,7 @@
"Notes": "Remarques",
"Allow replies.": "Autoriser les réponses.",
"Event": "un événement",
"Events": "Événements",
"Create an event": "Créer un événement",
"Describe the event": "Décrivez l'événement",
"Start Date": "Date de début",

View File

@ -258,6 +258,7 @@
"Notes": "Nótaí",
"Allow replies.": "Ceadaigh freagraí.",
"Event": "Imeacht",
"Events": "Imeachtaí",
"Create an event": "Cruthaigh imeacht",
"Describe the event": "Déan cur síos ar an ócáid",
"Start Date": "Dáta tosaigh",

View File

@ -258,6 +258,7 @@
"Notes": "टिप्पणियाँ",
"Allow replies.": "जवाब दें।",
"Event": "प्रतिस्पर्धा",
"Events": "आयोजन",
"Create an event": "एक घटना बनाएँ",
"Describe the event": "घटना का वर्णन करें",
"Start Date": "आरंभ करने की तिथि",

View File

@ -258,6 +258,7 @@
"Notes": "Appunti",
"Allow replies.": "Consenti risposte.",
"Event": "Evento",
"Events": "Eventi",
"Create an event": "Crea un evento",
"Describe the event": "Descrivi l'evento",
"Start Date": "Data d'inizio",

View File

@ -258,6 +258,7 @@
"Notes": "ノート",
"Allow replies.": "返信を許可します。",
"Event": "イベント",
"Events": "イベント",
"Create an event": "イベントを作成する",
"Describe the event": "イベントについて説明する",
"Start Date": "開始日",

View File

@ -254,6 +254,7 @@
"Notes": "Notes",
"Allow replies.": "Allow replies.",
"Event": "Event",
"Events": "Events",
"Create an event": "Create an event",
"Describe the event": "Describe the event",
"Start Date": "Start Date",

View File

@ -258,6 +258,7 @@
"Notes": "Notas",
"Allow replies.": "Permitir respostas.",
"Event": "Evento",
"Events": "Eventos",
"Create an event": "Crie um evento",
"Describe the event": "Descreva o evento",
"Start Date": "Data de início",

View File

@ -258,6 +258,7 @@
"Notes": "Ноты",
"Allow replies.": "Разрешить ответы.",
"Event": "Мероприятие",
"Events": "События",
"Create an event": "Создать мероприятие",
"Describe the event": "Опишите событие",
"Start Date": "Дата начала",

View File

@ -258,6 +258,7 @@
"Notes": "笔记",
"Allow replies.": "允许回复。",
"Event": "事件",
"Events": "大事记",
"Create an event": "建立活动",
"Describe the event": "描述事件",
"Start Date": "开始日期",

View File

@ -19,6 +19,18 @@ from calendar import monthrange
from followingCalendar import addPersonToCalendar
def removeIdEnding(idStr: str) -> str:
"""Removes endings such as /activity and /undo
"""
if idStr.endswith('/activity'):
idStr = idStr[:-len('/activity')]
elif idStr.endswith('/undo'):
idStr = idStr[:-len('/undo')]
elif idStr.endswith('/event'):
idStr = idStr[:-len('/event')]
return idStr
def getProtocolPrefixes() -> []:
"""Returns a list of valid prefixes
"""
@ -384,13 +396,13 @@ def locatePost(baseDir: str, nickname: str, domain: str,
extension = 'replies'
# if this post in the shared inbox?
postUrl = postUrl.replace('/', '#').replace('/activity', '').strip()
postUrl = removeIdEnding(postUrl.strip()).replace('/', '#')
# add the extension
postUrl = postUrl + '.' + extension
# search boxes
boxes = ('inbox', 'outbox', 'tlblogs')
boxes = ('inbox', 'outbox', 'tlblogs', 'tlevents')
accountDir = baseDir + '/accounts/' + nickname + '@' + domain + '/'
for boxName in boxes:
postFilename = accountDir + boxName + '/' + postUrl
@ -435,7 +447,7 @@ def removeModerationPostFromIndex(baseDir: str, postUrl: str,
moderationIndexFile = baseDir + '/accounts/moderation.txt'
if not os.path.isfile(moderationIndexFile):
return
postId = postUrl.replace('/activity', '')
postId = removeIdEnding(postUrl)
if postId in open(moderationIndexFile).read():
with open(moderationIndexFile, "r") as f:
lines = f.readlines()
@ -463,7 +475,7 @@ def isReplyToBlogPost(baseDir: str, nickname: str, domain: str,
nickname + '@' + domain + '/tlblogs.index'
if not os.path.isfile(blogsIndexFilename):
return False
postId = postJsonObject['object']['inReplyTo'].replace('/activity', '')
postId = removeIdEnding(postJsonObject['object']['inReplyTo'])
postId = postId.replace('/', '#')
if postId in open(blogsIndexFilename).read():
return True
@ -494,7 +506,7 @@ def deletePost(baseDir: str, httpPrefix: str,
# remove from recent posts cache in memory
if recentPostsCache:
postId = \
postJsonObject['id'].replace('/activity', '').replace('/', '#')
removeIdEnding(postJsonObject['id']).replace('/', '#')
if recentPostsCache.get('index'):
if postId in recentPostsCache['index']:
recentPostsCache['index'].remove(postId)
@ -526,7 +538,7 @@ def deletePost(baseDir: str, httpPrefix: str,
if isinstance(postJsonObject['object'], dict):
if postJsonObject['object'].get('moderationStatus'):
if postJsonObject.get('id'):
postId = postJsonObject['id'].replace('/activity', '')
postId = removeIdEnding(postJsonObject['id'])
removeModerationPostFromIndex(baseDir, postId, debug)
# remove any hashtags index entries
@ -540,8 +552,7 @@ def deletePost(baseDir: str, httpPrefix: str,
if postJsonObject['object'].get('id') and \
postJsonObject['object'].get('tag'):
# get the id of the post
postId = \
postJsonObject['object']['id'].replace('/activity', '')
postId = removeIdEnding(postJsonObject['object']['id'])
for tag in postJsonObject['object']['tag']:
if tag['type'] != 'Hashtag':
continue
@ -710,7 +721,7 @@ def getCachedPostFilename(baseDir: str, nickname: str, domain: str,
return None
cachedPostFilename = \
cachedPostDir + \
'/' + postJsonObject['id'].replace('/activity', '').replace('/', '#')
'/' + removeIdEnding(postJsonObject['id']).replace('/', '#')
cachedPostFilename = cachedPostFilename + '.html'
return cachedPostFilename
@ -727,7 +738,7 @@ def removePostFromCache(postJsonObject: {}, recentPostsCache: {}):
postId = postJsonObject['id']
if '#' in postId:
postId = postId.split('#', 1)[0]
postId = postId.replace('/activity', '').replace('/', '#')
postId = removeIdEnding(postId).replace('/', '#')
if postId not in recentPostsCache['index']:
return
@ -747,7 +758,7 @@ def updateRecentPostsCache(recentPostsCache: {}, maxRecentPosts: int,
postId = postJsonObject['id']
if '#' in postId:
postId = postId.split('#', 1)[0]
postId = postId.replace('/activity', '').replace('/', '#')
postId = removeIdEnding(postId).replace('/', '#')
if recentPostsCache.get('index'):
if postId in recentPostsCache['index']:
return
@ -1071,7 +1082,7 @@ def updateAnnounceCollection(recentPostsCache: {},
return
if not isinstance(postJsonObject['object'], dict):
return
postUrl = postJsonObject['id'].replace('/activity', '') + '/shares'
postUrl = removeIdEnding(postJsonObject['id']) + '/shares'
if not postJsonObject['object'].get('shares'):
if debug:
print('DEBUG: Adding initial shares (announcements) to ' +

View File

@ -25,6 +25,7 @@ from ssb import getSSBAddress
from tox import getToxAddress
from matrix import getMatrixAddress
from donate import getDonationUrl
from utils import removeIdEnding
from utils import getProtocolPrefixes
from utils import getFileCaseInsensitive
from utils import searchBoxPosts
@ -3313,7 +3314,7 @@ def insertQuestion(baseDir: str, translate: {},
return content
if len(postJsonObject['object']['oneOf']) == 0:
return content
messageId = postJsonObject['id'].replace('/activity', '')
messageId = removeIdEnding(postJsonObject['id'])
if '#' in messageId:
messageId = messageId.split('#', 1)[0]
pageNumberStr = ''
@ -3781,7 +3782,7 @@ def individualPostAsHtml(recentPostsCache: {}, maxRecentPosts: int,
avatarPosition = ''
messageId = ''
if postJsonObject.get('id'):
messageId = postJsonObject['id'].replace('/activity', '')
messageId = removeIdEnding(postJsonObject['id'])
messageIdStr = ''
if messageId:
@ -3870,7 +3871,7 @@ def individualPostAsHtml(recentPostsCache: {}, maxRecentPosts: int,
if boxName == 'tlbookmarks' or boxName == 'bookmarks':
return ''
timelinePostBookmark = postJsonObject['id'].replace('/activity', '')
timelinePostBookmark = removeIdEnding(postJsonObject['id'])
timelinePostBookmark = timelinePostBookmark.replace('://', '-')
timelinePostBookmark = timelinePostBookmark.replace('/', '-')
@ -4007,8 +4008,8 @@ def individualPostAsHtml(recentPostsCache: {}, maxRecentPosts: int,
editStr = ''
if fullDomain + '/users/' + nickname in postJsonObject['actor']:
if isBlogPost(postJsonObject):
if '/statuses/' in postJsonObject['object']['id']:
if '/statuses/' in postJsonObject['object']['id']:
if isBlogPost(postJsonObject):
editStr += \
'<a class="imageAnchor" href="/users/' + nickname + \
'/tlblogs?editblogpost=' + \
@ -4629,6 +4630,7 @@ def htmlTimeline(defaultTimeline: str,
repliesButton = 'buttonhighlighted'
mediaButton = 'button'
bookmarksButton = 'button'
eventsButton = 'button'
sentButton = 'button'
sharesButton = 'button'
if newShare:
@ -4662,6 +4664,8 @@ def htmlTimeline(defaultTimeline: str,
sharesButton = 'buttonselectedhighlighted'
elif boxName == 'tlbookmarks' or boxName == 'bookmarks':
bookmarksButton = 'buttonselected'
elif boxName == 'tlevents' or boxName == 'events':
eventsButton = 'buttonselected'
fullDomain = domain
if port != 80 and port != 443:
@ -4702,6 +4706,7 @@ def htmlTimeline(defaultTimeline: str,
sharesButtonStr = ''
bookmarksButtonStr = ''
eventsButtonStr = ''
if not minimal:
sharesButtonStr = \
'<a href="' + usersPath + '/tlshares"><button class="' + \
@ -4714,6 +4719,11 @@ def htmlTimeline(defaultTimeline: str,
bookmarksButton + '"><span>' + translate['Bookmarks'] + \
' </span></button></a>\n'
eventsButtonStr = \
'<a href="' + usersPath + '/tlevents"><button class="' + \
eventsButton + '"><span>' + translate['Events'] + \
' </span></button></a>\n'
tlStr = htmlHeader(cssFilename, profileStyle)
if boxName != 'dm':
@ -4835,7 +4845,7 @@ def htmlTimeline(defaultTimeline: str,
sentButton+'"><span>' + translate['Outbox'] + \
'</span></button></a>\n'
tlStr += \
sharesButtonStr + bookmarksButtonStr + \
sharesButtonStr + bookmarksButtonStr + eventsButtonStr + \
moderationButtonStr + newPostButtonStr
tlStr += \
' <a class="imageAnchor" href="' + usersPath + \
@ -4963,7 +4973,7 @@ def htmlTimeline(defaultTimeline: str,
if boxName != 'tlmedia' and \
recentPostsCache.get('index'):
postId = \
item['id'].replace('/activity', '').replace('/', '#')
removeIdEnding(item['id']).replace('/', '#')
if postId in recentPostsCache['index']:
if not item.get('muted'):
if recentPostsCache['html'].get(postId):
@ -5075,6 +5085,28 @@ def htmlBookmarks(defaultTimeline: str,
minimal, YTReplacementDomain)
def htmlEvents(defaultTimeline: str,
recentPostsCache: {}, maxRecentPosts: int,
translate: {}, pageNumber: int, itemsPerPage: int,
session, baseDir: str, wfRequest: {}, personCache: {},
nickname: str, domain: str, port: int, bookmarksJson: {},
allowDeletion: bool,
httpPrefix: str, projectVersion: str,
minimal: bool, YTReplacementDomain: str) -> str:
"""Show the events as html
"""
manuallyApproveFollowers = \
followerApprovalActive(baseDir, nickname, domain)
return htmlTimeline(defaultTimeline, recentPostsCache, maxRecentPosts,
translate, pageNumber,
itemsPerPage, session, baseDir, wfRequest, personCache,
nickname, domain, port, bookmarksJson,
'tlevents', allowDeletion,
httpPrefix, projectVersion, manuallyApproveFollowers,
minimal, YTReplacementDomain)
def htmlInboxDMs(defaultTimeline: str,
recentPostsCache: {}, maxRecentPosts: int,
translate: {}, pageNumber: int, itemsPerPage: int,
@ -5238,7 +5270,7 @@ def htmlIndividualPost(recentPostsCache: {}, maxRecentPosts: int,
httpPrefix, projectVersion, 'inbox',
YTReplacementDomain,
False, authorized, False, False, False)
messageId = postJsonObject['id'].replace('/activity', '')
messageId = removeIdEnding(postJsonObject['id'])
# show the previous posts
if isinstance(postJsonObject['object'], dict):