diff --git a/announce.py b/announce.py index 00804d2b..5916f55a 100644 --- a/announce.py +++ b/announce.py @@ -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], diff --git a/blocking.py b/blocking.py index 0c57af78..8d6436f3 100644 --- a/blocking.py +++ b/blocking.py @@ -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') diff --git a/bookmarks.py b/bookmarks.py index b7f1fa82..44b95197 100644 --- a/bookmarks.py +++ b/bookmarks.py @@ -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) diff --git a/daemon.py b/daemon.py index a4909021..dd45e6e2 100644 --- a/daemon.py +++ b/daemon.py @@ -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() diff --git a/delete.py b/delete.py index f7758544..1b537d39 100644 --- a/delete.py +++ b/delete.py @@ -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') diff --git a/inbox.py b/inbox.py index 4250f8d7..25113f72 100644 --- a/inbox.py +++ b/inbox.py @@ -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, diff --git a/like.py b/like.py index f3d127dc..fb38d810 100644 --- a/like.py +++ b/like.py @@ -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) diff --git a/outbox.py b/outbox.py index 8b99ba70..805d24b3 100644 --- a/outbox.py +++ b/outbox.py @@ -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 + \ diff --git a/person.py b/person.py index 2519a426..4f0477bc 100644 --- a/person.py +++ b/person.py @@ -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, diff --git a/posts.py b/posts.py index 1f802341..fbd956f9 100644 --- a/posts.py +++ b/posts.py @@ -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): diff --git a/tests.py b/tests.py index 69095aa4..6109dead 100644 --- a/tests.py +++ b/tests.py @@ -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() diff --git a/translations/ar.json b/translations/ar.json index 38afd2b6..56c889fa 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -258,6 +258,7 @@ "Notes": "ملاحظات", "Allow replies.": "السماح بالردود.", "Event": "حدث", + "Events": "الأحداث", "Create an event": "أنشئ حدثًا", "Describe the event": "صف الحدث", "Start Date": "تاريخ البدء", diff --git a/translations/ca.json b/translations/ca.json index aea20a99..3e420c60 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -258,6 +258,7 @@ "Notes": "Notes", "Allow replies.": "Permetre respostes.", "Event": "Esdeveniment", + "Events": "Esdeveniments", "Create an event": "Crea un esdeveniment", "Describe the event": "Descriviu l’esdeveniment", "Start Date": "Data d'inici", diff --git a/translations/cy.json b/translations/cy.json index e84bd97e..e7bd2c8b 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -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", diff --git a/translations/de.json b/translations/de.json index a3e57336..a1029eb3 100644 --- a/translations/de.json +++ b/translations/de.json @@ -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", diff --git a/translations/en.json b/translations/en.json index e920d2d3..d5f957fd 100644 --- a/translations/en.json +++ b/translations/en.json @@ -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", diff --git a/translations/es.json b/translations/es.json index 9e3096dc..907814a6 100644 --- a/translations/es.json +++ b/translations/es.json @@ -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", diff --git a/translations/fr.json b/translations/fr.json index 4bccf438..3cd15ff3 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -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", diff --git a/translations/ga.json b/translations/ga.json index 35d6463f..bc7237e4 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -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", diff --git a/translations/hi.json b/translations/hi.json index 4e1be87b..fea37de5 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -258,6 +258,7 @@ "Notes": "टिप्पणियाँ", "Allow replies.": "जवाब दें।", "Event": "प्रतिस्पर्धा", + "Events": "आयोजन", "Create an event": "एक घटना बनाएँ", "Describe the event": "घटना का वर्णन करें", "Start Date": "आरंभ करने की तिथि", diff --git a/translations/it.json b/translations/it.json index b73d94cc..cf22239e 100644 --- a/translations/it.json +++ b/translations/it.json @@ -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", diff --git a/translations/ja.json b/translations/ja.json index 01c8dc3b..6a024bcf 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -258,6 +258,7 @@ "Notes": "ノート", "Allow replies.": "返信を許可します。", "Event": "イベント", + "Events": "イベント", "Create an event": "イベントを作成する", "Describe the event": "イベントについて説明する", "Start Date": "開始日", diff --git a/translations/oc.json b/translations/oc.json index 96371bec..b38fdf8d 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -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", diff --git a/translations/pt.json b/translations/pt.json index cac28c6c..86cef14a 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -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", diff --git a/translations/ru.json b/translations/ru.json index 2be8bfc5..0bdedc6a 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -258,6 +258,7 @@ "Notes": "Ноты", "Allow replies.": "Разрешить ответы.", "Event": "Мероприятие", + "Events": "События", "Create an event": "Создать мероприятие", "Describe the event": "Опишите событие", "Start Date": "Дата начала", diff --git a/translations/zh.json b/translations/zh.json index 0fb87c97..d01c60b9 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -258,6 +258,7 @@ "Notes": "笔记", "Allow replies.": "允许回复。", "Event": "事件", + "Events": "大事记", "Create an event": "建立活动", "Describe the event": "描述事件", "Start Date": "开始日期", diff --git a/utils.py b/utils.py index 3d9720fb..3f36d25b 100644 --- a/utils.py +++ b/utils.py @@ -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 ' + diff --git a/webinterface.py b/webinterface.py index 4f6c8ce7..1eebed49 100644 --- a/webinterface.py +++ b/webinterface.py @@ -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 += \ '\n' + eventsButtonStr = \ + '\n' + tlStr = htmlHeader(cssFilename, profileStyle) if boxName != 'dm': @@ -4835,7 +4845,7 @@ def htmlTimeline(defaultTimeline: str, sentButton+'">' + translate['Outbox'] + \ '\n' tlStr += \ - sharesButtonStr + bookmarksButtonStr + \ + sharesButtonStr + bookmarksButtonStr + eventsButtonStr + \ moderationButtonStr + newPostButtonStr tlStr += \ '