From 28830ed660e9d7d56d8849337160cc2e76003116 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 20 Aug 2020 12:34:39 +0100 Subject: [PATCH 01/84] Consistent append --- auth.py | 2 +- follow.py | 2 +- inbox.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/auth.py b/auth.py index d2aab5917..a9b83a938 100644 --- a/auth.py +++ b/auth.py @@ -130,7 +130,7 @@ def storeBasicCredentials(baseDir: str, nickname: str, password: str) -> bool: os.rename(passwordFile + '.new', passwordFile) else: # append to password file - with open(passwordFile, "a") as passfile: + with open(passwordFile, 'a+') as passfile: passfile.write(storeStr + '\n') else: with open(passwordFile, "w") as passfile: diff --git a/follow.py b/follow.py index 3c6c6e1d0..e14ab804e 100644 --- a/follow.py +++ b/follow.py @@ -520,7 +520,7 @@ def storeFollowRequest(baseDir: str, approveFollowsFilename = accountsDir + '/followrequests.txt' if os.path.isfile(approveFollowsFilename): if approveHandle not in open(approveFollowsFilename).read(): - with open(approveFollowsFilename, "a") as fp: + with open(approveFollowsFilename, 'a+') as fp: fp.write(approveHandle + '\n') else: if debug: diff --git a/inbox.py b/inbox.py index 692b89c17..e4a27672d 100644 --- a/inbox.py +++ b/inbox.py @@ -1581,7 +1581,7 @@ def populateReplies(baseDir: str, httpPrefix: str, domain: str, if numLines > maxReplies: return False if messageId not in open(postRepliesFilename).read(): - repliesFile = open(postRepliesFilename, "a") + repliesFile = open(postRepliesFilename, 'a+') repliesFile.write(messageId + '\n') repliesFile.close() else: From a771ce3041b57a6352bca1be05d28233c4842c4d Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 20 Aug 2020 13:11:07 +0100 Subject: [PATCH 02/84] Tidying --- follow.py | 4 ++-- inbox.py | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/follow.py b/follow.py index e14ab804e..a2da2d991 100644 --- a/follow.py +++ b/follow.py @@ -202,14 +202,14 @@ def unfollowPerson(baseDir: str, nickname: str, domain: str, if debug: print('DEBUG: follow file ' + filename + ' was not found') return False - if handleToUnfollow.lower() not in open(filename).read().lower(): + handleToUnfollowLower = handleToUnfollow.lower() + if handleToUnfollowLower not in open(filename).read().lower(): if debug: print('DEBUG: handle to unfollow ' + handleToUnfollow + ' is not in ' + filename) return with open(filename, "r") as f: lines = f.readlines() - handleToUnfollowLower = handleToUnfollow.lower() with open(filename, "w") as f: for line in lines: if line.strip("\n").strip("\r").lower() != handleToUnfollowLower: diff --git a/inbox.py b/inbox.py index e4a27672d..6141a2682 100644 --- a/inbox.py +++ b/inbox.py @@ -706,10 +706,9 @@ def receiveUndoFollow(session, baseDir: str, httpPrefix: str, nicknameFollowing, domainFollowingFull, nicknameFollower, domainFollowerFull, debug): - if debug: - print('DEBUG: Follower ' + - nicknameFollower + '@' + domainFollowerFull + - ' was removed') + print(nicknameFollowing + '@' + domainFollowingFull + ': ' + 'Follower ' + nicknameFollower + '@' + domainFollowerFull + + ' was removed') return True if debug: From f263cef8843fbea7bbd38b6d254351210cd01406 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 20 Aug 2020 17:51:48 +0100 Subject: [PATCH 03/84] Receiving event posts --- happening.py | 5 ++-- inbox.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++-- outbox.py | 3 ++- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/happening.py b/happening.py index 438ffa3ad..c25307efd 100644 --- a/happening.py +++ b/happening.py @@ -43,8 +43,8 @@ def removeEventFromTimeline(eventId: str, tlEventsFilename: str) -> None: pass -def saveEvent(baseDir: str, handle: str, postId: str, - eventJson: {}) -> bool: +def saveEventPost(baseDir: str, handle: str, postId: str, + eventJson: {}) -> bool: """Saves an event to the calendar and/or the events timeline If an event has extra fields, as per Mobilizon, Then it is saved as a separate entity and added to the @@ -71,6 +71,7 @@ def saveEvent(baseDir: str, handle: str, postId: str, eventJson.get('uuid') and eventJson.get('content'): if not validUuid(eventJson['uuid']): return False + print('Mobilizon type event') # if this is a full description of an event then save it # as a separate json file eventsPath = baseDir + '/accounts/' + handle + '/events' diff --git a/inbox.py b/inbox.py index 6141a2682..d7c1baf71 100644 --- a/inbox.py +++ b/inbox.py @@ -64,7 +64,7 @@ from git import isGitPatch from git import receiveGitPatch from followingCalendar import receivingCalendarEvents from content import dangerousMarkup -from happening import saveEvent +from happening import saveEventPost def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None: @@ -770,6 +770,56 @@ def receiveUndo(session, baseDir: str, httpPrefix: str, return False +def isEventPost(messageJson: {}) -> bool: + """Is the given post a mobilizon-type event activity? + """ + if not messageJson.get('id'): + return False + if not messageJson.get('actor'): + return False + if not messageJson.get('object'): + return False + if not isinstance(messageJson['object'], dict): + return False + if not messageJson['object'].get('type'): + return False + if messageJson['object']['type'] != 'Event': + return False + if not messageJson['object'].get('startTime'): + return False + if not messageJson['object'].get('actor'): + return False + if not messageJson['object'].get('content'): + return False + if not messageJson['object'].get('name'): + return False + if not messageJson['object'].get('uuid'): + return False + return True + + +def receiveEventPost(recentPostsCache: {}, session, baseDir: str, + httpPrefix: str, domain: str, port: int, + sendThreads: [], postLog: [], cachedWebfingers: {}, + personCache: {}, messageJson: {}, federationList: [], + nickname: str, debug: bool) -> bool: + """Receive a mobilizon-type event activity + """ + if not isEventPost(messageJson): + return + print('Receiving event: ' + str(messageJson['object'])) + handle = nickname + '@' + domain + if port: + if port != 80 and port != 443: + handle += ':' + str(port) + + postId = \ + messageJson['id'].replace('/activity', '').replace('/', '#') + postId = postId.replace('/event', '') + + saveEventPost(baseDir, handle, postId, messageJson['object']) + + def personReceiveUpdate(baseDir: str, domain: str, port: int, updateNickname: str, updateDomain: str, @@ -1991,7 +2041,7 @@ def inboxUpdateCalendar(baseDir: str, handle: str, postJsonObject: {}) -> None: continue if not tagDict.get('startTime'): continue - saveEvent(baseDir, handle, postId, tagDict) + saveEventPost(baseDir, handle, postId, tagDict) def inboxUpdateIndex(boxname: str, baseDir: str, handle: str, @@ -2715,6 +2765,23 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, queue.pop(0) continue + if receiveEventPost(recentPostsCache, session, + baseDir, httpPrefix, + domain, port, + sendThreads, postLog, + cachedWebfingers, + personCache, + queueJson['post'], + federationList, + queueJson['postNickname'], + debug): + print('Queue: Event activity accepted from ' + keyId) + if os.path.isfile(queueFilename): + os.remove(queueFilename) + if len(queue) > 0: + queue.pop(0) + continue + if receiveUpdate(recentPostsCache, session, baseDir, httpPrefix, domain, port, diff --git a/outbox.py b/outbox.py index bbf863f22..8b99ba705 100644 --- a/outbox.py +++ b/outbox.py @@ -152,7 +152,7 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str, permittedOutboxTypes = ('Create', 'Announce', 'Like', 'Follow', 'Undo', 'Update', 'Add', 'Remove', 'Block', 'Delete', - 'Delegate', 'Skill', 'Bookmark') + 'Delegate', 'Skill', 'Bookmark', 'Event') if messageJson['type'] not in permittedOutboxTypes: if debug: print('DEBUG: POST to outbox - ' + messageJson['type'] + @@ -191,6 +191,7 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str, messageJson['type'] == 'Note' or \ messageJson['type'] == 'EncryptedMessage' or \ messageJson['type'] == 'Article' or \ + messageJson['type'] == 'Event' or \ messageJson['type'] == 'Patch' or \ messageJson['type'] == 'Announce': indexes = [outboxName, "inbox"] From fa0434ca9cef2a0e09229c5ea86ef5b81b22d9b4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 20 Aug 2020 18:08:25 +0100 Subject: [PATCH 04/84] Link to event parameters --- happening.py | 2 ++ inbox.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/happening.py b/happening.py index c25307efd..1c81e0d5c 100644 --- a/happening.py +++ b/happening.py @@ -49,6 +49,8 @@ def saveEventPost(baseDir: str, handle: str, postId: str, If an event has extra fields, as per Mobilizon, Then it is saved as a separate entity and added to the events timeline + See https://framagit.org/framasoft/mobilizon/-/blob/ + master/lib/federation/activity_stream/converter/event.ex """ calendarPath = baseDir + '/accounts/' + handle + '/calendar' if not os.path.isdir(calendarPath): diff --git a/inbox.py b/inbox.py index d7c1baf71..1721105fb 100644 --- a/inbox.py +++ b/inbox.py @@ -772,6 +772,8 @@ def receiveUndo(session, baseDir: str, httpPrefix: str, def isEventPost(messageJson: {}) -> bool: """Is the given post a mobilizon-type event activity? + See https://framagit.org/framasoft/mobilizon/-/blob/ + master/lib/federation/activity_stream/converter/event.ex """ if not messageJson.get('id'): return False @@ -804,6 +806,8 @@ def receiveEventPost(recentPostsCache: {}, session, baseDir: str, personCache: {}, messageJson: {}, federationList: [], nickname: str, debug: bool) -> bool: """Receive a mobilizon-type event activity + See https://framagit.org/framasoft/mobilizon/-/blob/ + master/lib/federation/activity_stream/converter/event.ex """ if not isEventPost(messageJson): return From 43d8fc1863ae8cd30928bbaec855356f91152378 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 21 Aug 2020 11:08:31 +0000 Subject: [PATCH 05/84] Add endpoint for new event --- daemon.py | 38 +++++++++++++++++++++++++++++ posts.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 98 insertions(+), 11 deletions(-) diff --git a/daemon.py b/daemon.py index 072d637b0..1a3479617 100644 --- a/daemon.py +++ b/daemon.py @@ -69,6 +69,7 @@ from posts import createBlogPost from posts import createReportPost from posts import createUnlistedPost from posts import createFollowersOnlyPost +from posts import createEventPost from posts import createDirectMessagePost from posts import populateRepliesJson from posts import addToField @@ -3619,6 +3620,7 @@ class PubServer(BaseHTTPRequestHandler): self.path.endswith('/newfollowers') or self.path.endswith('/newdm') or self.path.endswith('/newreminder') or + self.path.endswith('/newevent') or self.path.endswith('/newreport') or self.path.endswith('/newquestion') or self.path.endswith('/newshare'))): @@ -5709,6 +5711,42 @@ class PubServer(BaseHTTPRequestHandler): return 1 else: return -1 + elif postType == 'newevent': + # A Mobilizon-type event is posted + + # if there is no image dscription then make it the same + # as the event title + if not fields.get('imageDescription'): + fields['imageDescription'] = fields['subject'] + # Events are public by default, with opt-in + # followers only status + if not fields.get('followersOnlyEvent'): + fields['followersOnlyEvent'] = False + + messageJson = \ + createEventPost(self.server.baseDir, + nickname, + self.server.domain, + self.server.port, + self.server.httpPrefix, + mentionsStr + fields['message'], + fields['followersOnlyEvent'], + False, False, + filename, attachmentMediaType, + fields['imageDescription'], + self.server.useBlurHash, + fields['subject'], + fields['schedulePost'], + fields['eventDate'], + fields['eventTime'], + fields['location']) + if messageJson: + if fields['schedulePost']: + return 1 + if self._postToOutbox(messageJson, __version__, nickname): + return 1 + else: + return -1 elif postType == 'newdm': messageJson = None print('A DM was posted') diff --git a/posts.py b/posts.py index 05b081e7f..7005f4c14 100644 --- a/posts.py +++ b/posts.py @@ -13,6 +13,7 @@ import os import shutil import sys import time +import uuid from socket import error as SocketError from time import gmtime, strftime from collections import OrderedDict @@ -614,7 +615,8 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, useBlurhash: bool, isModerationReport: bool, isArticle: bool, inReplyTo=None, inReplyToAtomUri=None, subject=None, schedulePost=False, - eventDate=None, eventTime=None, location=None) -> {}: + eventDate=None, eventTime=None, location=None, + eventUUID=None) -> {}: """Creates a message """ mentionedRecipients = \ @@ -755,6 +757,11 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, for ccRemoval in removeFromCC: toCC.remove(ccRemoval) + # the type of post to be made + postObjectType = 'Note' + if eventUUID: + postObjectType = 'Event' + if not clientToServer: actorUrl = httpPrefix + '://' + domain + '/users/' + nickname @@ -783,7 +790,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, 'cc': toCC, 'object': { 'id': newPostId, - 'type': 'Note', + 'type': postObjectType, 'summary': summary, 'inReplyTo': inReplyTo, 'published': published, @@ -824,7 +831,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, newPost = { "@context": postContext, 'id': newPostId, - 'type': 'Note', + 'type': postObjectType, 'summary': summary, 'inReplyTo': inReplyTo, 'published': published, @@ -1028,7 +1035,7 @@ def createPublicPost(baseDir: str, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, inReplyTo, inReplyToAtomUri, subject, - schedulePost, eventDate, eventTime, location) + schedulePost, eventDate, eventTime, location, None) def createBlogPost(baseDir: str, @@ -1079,7 +1086,7 @@ def createQuestionPost(baseDir: str, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, None, None, subject, - False, None, None, None) + False, None, None, None, None) messageJson['object']['type'] = 'Question' messageJson['object']['oneOf'] = [] messageJson['object']['votersCount'] = 0 @@ -1126,7 +1133,7 @@ def createUnlistedPost(baseDir: str, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, inReplyTo, inReplyToAtomUri, subject, - schedulePost, eventDate, eventTime, location) + schedulePost, eventDate, eventTime, location, None) def createFollowersOnlyPost(baseDir: str, @@ -1157,7 +1164,48 @@ def createFollowersOnlyPost(baseDir: str, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, inReplyTo, inReplyToAtomUri, subject, - schedulePost, eventDate, eventTime, location) + schedulePost, eventDate, eventTime, location, None) + + +def createEventPost(baseDir: str, + nickname: str, domain: str, port: int, + httpPrefix: str, + content: str, followersOnly: bool, + saveToFile: bool, + clientToServer: bool, + attachImageFilename: str, mediaType: str, + imageDescription: str, useBlurhash: bool, + subject=None, schedulePost=False, + eventDate=None, eventTime=None, + location=None) -> {}: + """Mobilizon-type Event post + """ + if not attachImageFilename: + return None + domainFull = domain + if port: + if port != 80 and port != 443: + if ':' not in domain: + domainFull = domain + ':' + str(port) + + # create event uuid + eventUUID = str(uuid.uuid1()) + + toStr1 = 'https://www.w3.org/ns/activitystreams#Public' + toStr2 = httpPrefix + '://' + domainFull + '/users/' + \ + nickname + '/followers', + if followersOnly: + toStr1 = toStr2 + toStr2 = None + return createPostBase(baseDir, nickname, domain, port, + toStr1, toStr2, + httpPrefix, content, followersOnly, saveToFile, + clientToServer, + attachImageFilename, mediaType, + imageDescription, useBlurhash, + False, False, None, None, subject, + schedulePost, eventDate, eventTime, location, + eventUUID) def getMentionedPeople(baseDir: str, httpPrefix: str, @@ -1226,7 +1274,7 @@ def createDirectMessagePost(baseDir: str, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, inReplyTo, inReplyToAtomUri, subject, - schedulePost, eventDate, eventTime, location) + schedulePost, eventDate, eventTime, location, None) # mentioned recipients go into To rather than Cc messageJson['to'] = messageJson['object']['cc'] messageJson['object']['to'] = messageJson['to'] @@ -1318,7 +1366,7 @@ def createReportPost(baseDir: str, attachImageFilename, mediaType, imageDescription, useBlurhash, True, False, None, None, subject, - False, None, None, None) + False, None, None, None, None) if not postJsonObject: continue @@ -1474,7 +1522,7 @@ def sendPost(projectVersion: str, imageDescription, useBlurhash, False, isArticle, inReplyTo, inReplyToAtomUri, subject, - False, None, None, None) + False, None, None, None, None) # get the senders private key privateKeyPem = getPersonKey(nickname, domain, baseDir, 'private') @@ -1618,7 +1666,7 @@ def sendPostViaServer(projectVersion: str, imageDescription, useBlurhash, False, isArticle, inReplyTo, inReplyToAtomUri, subject, - False, None, None, None) + False, None, None, None, None) authHeader = createBasicAuthHeader(fromNickname, password) @@ -2441,6 +2489,7 @@ def isImageMedia(session, baseDir: str, httpPrefix: str, if postJsonObject['object'].get('moderationStatus'): return False if postJsonObject['object']['type'] != 'Note' and \ + postJsonObject['object']['type'] != 'Event' and \ postJsonObject['object']['type'] != 'Article': return False if not postJsonObject['object'].get('attachment'): From 25e1724691564cc36342c4d787273e4f97a7b04e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 21 Aug 2020 16:10:47 +0000 Subject: [PATCH 06/84] Extra event attributes --- daemon.py | 20 +++++-- posts.py | 173 +++++++++++++++++++++++++++++++++++++++++++----------- utils.py | 2 +- 3 files changed, 153 insertions(+), 42 deletions(-) diff --git a/daemon.py b/daemon.py index 1a3479617..965c54a9d 100644 --- a/daemon.py +++ b/daemon.py @@ -316,12 +316,14 @@ class PubServer(BaseHTTPRequestHandler): print('Voting on message ' + messageId) print('Vote for: ' + answer) + commentsEnabled = True messageJson = \ createPublicPost(self.server.baseDir, nickname, self.server.domain, self.server.port, self.server.httpPrefix, answer, False, False, False, + commentsEnabled, None, None, None, True, messageId, messageId, None, False, None, None, None) @@ -5517,6 +5519,9 @@ class PubServer(BaseHTTPRequestHandler): mentionsStr = '' if fields.get('mentions'): mentionsStr = fields['mentions'].strip() + ' ' + commentsEnabled = True + if fields.get('commentsEnabled'): + commentsEnabled = fields['commentsEnabled'] if postType == 'newpost': messageJson = \ createPublicPost(self.server.baseDir, @@ -5525,7 +5530,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.port, self.server.httpPrefix, mentionsStr + fields['message'], - False, False, False, + False, False, False, commentsEnabled, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, @@ -5552,7 +5557,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain, self.server.port, self.server.httpPrefix, fields['message'], - False, False, False, + False, False, False, commentsEnabled, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, @@ -5655,7 +5660,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain, self.server.port, self.server.httpPrefix, mentionsStr + fields['message'], - False, False, False, + False, False, False, commentsEnabled, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, @@ -5688,6 +5693,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix, mentionsStr + fields['message'], True, False, False, + commentsEnabled, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, @@ -5731,7 +5737,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix, mentionsStr + fields['message'], fields['followersOnlyEvent'], - False, False, + False, False, commentsEnabled, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, @@ -5760,6 +5766,7 @@ class PubServer(BaseHTTPRequestHandler): mentionsStr + fields['message'], True, False, False, + commentsEnabled, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, @@ -5799,7 +5806,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.port, self.server.httpPrefix, mentionsStr + fields['message'], - True, False, False, + True, False, False, False, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, @@ -5832,7 +5839,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain, self.server.port, self.server.httpPrefix, mentionsStr + fields['message'], - True, False, False, + True, False, False, True, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, @@ -5863,6 +5870,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix, fields['message'], qOptions, False, False, False, + commentsEnabled, filename, attachmentMediaType, fields['imageDescription'], self.server.useBlurHash, diff --git a/posts.py b/posts.py index 7005f4c14..a86cbaf55 100644 --- a/posts.py +++ b/posts.py @@ -610,13 +610,20 @@ def addSchedulePost(baseDir: str, nickname: str, domain: str, def createPostBase(baseDir: str, nickname: str, domain: str, port: int, toUrl: str, ccUrl: str, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, clientToServer: bool, + commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, isModerationReport: bool, - isArticle: bool, inReplyTo=None, + isArticle: bool, + inReplyTo=None, inReplyToAtomUri=None, subject=None, schedulePost=False, eventDate=None, eventTime=None, location=None, - eventUUID=None) -> {}: + eventUUID=None, category=None, joinMode=None, + endDate=None, endTime=None, + maximumAttendeeCapacity=None, + repliesModerationOption=None, + anonymousParticipationEnabled=None, + eventStatus=None) -> {}: """Creates a message """ mentionedRecipients = \ @@ -705,6 +712,24 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, sensitive = True if replyToJson['object'].get('summary'): summary = replyToJson['object']['summary'] + + # get the ending date and time + endDateStr = None + if endDate: + eventName = summary + if not eventName: + eventName = content + endDateStr = endDate + if endTime: + if endTime.endswith('Z'): + endDateStr = endDate + 'T' + endTime + else: + endDateStr = endDate + 'T' + endTime + \ + ':00' + strftime("%z", gmtime()) + else: + endDateStr = endDate + 'T12:00:00Z' + + # get the starting date and time eventDateStr = None if eventDate: eventName = summary @@ -719,15 +744,17 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, ':00' + strftime("%z", gmtime()) else: eventDateStr = eventDate + 'T12:00:00Z' - if not schedulePost: + if not endDateStr: + endDateStr = eventDateStr + if not schedulePost and not eventUUID: tags.append({ "@context": "https://www.w3.org/ns/activitystreams", "type": "Event", "name": eventName, "startTime": eventDateStr, - "endTime": eventDateStr + "endTime": endDateStr }) - if location: + if location and not eventUUID: tags.append({ "@context": "https://www.w3.org/ns/activitystreams", "type": "Place", @@ -801,6 +828,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, 'sensitive': sensitive, 'atomUri': newPostId, 'inReplyToAtomUri': inReplyToAtomUri, + 'commentsEnabled': commentsEnabled, 'mediaType': 'text/html', 'content': content, 'contentMap': { @@ -824,6 +852,27 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, attachMedia(baseDir, httpPrefix, domain, port, newPost['object'], attachImageFilename, mediaType, imageDescription, useBlurhash) + if eventUUID: + # add attributes for Mobilizon-type events + if eventStatus: + newPost['object']['ical:status'] = eventStatus + if anonymousParticipationEnabled: + newPost['object']['anonymousParticipationEnabled'] = \ + anonymousParticipationEnabled + if repliesModerationOption: + newPost['object']['repliesModerationOption'] = \ + repliesModerationOption + if category: + newPost['object']['category'] = category + if joinMode: + newPost['object']['joinMode'] = joinMode + newPost['object']['startTime'] = eventDateStr + newPost['object']['endTime'] = endDateStr + if location: + newPost['object']['location'] = location + if maximumAttendeeCapacity: + newPost['object']['maximumAttendeeCapacity'] = \ + maximumAttendeeCapacity else: idStr = \ httpPrefix + '://' + domain + '/users/' + nickname + \ @@ -842,6 +891,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, 'sensitive': sensitive, 'atomUri': newPostId, 'inReplyToAtomUri': inReplyToAtomUri, + 'commentsEnabled': commentsEnabled, 'mediaType': 'text/html', 'content': content, 'contentMap': { @@ -864,6 +914,27 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, attachMedia(baseDir, httpPrefix, domain, port, newPost, attachImageFilename, mediaType, imageDescription, useBlurhash) + if eventUUID: + # add attributes for Mobilizon-type events + if eventStatus: + newPost['ical:status'] = eventStatus + if anonymousParticipationEnabled: + newPost['anonymousParticipationEnabled'] = \ + anonymousParticipationEnabled + if repliesModerationOption: + newPost['repliesModerationOption'] = \ + repliesModerationOption + if category: + newPost['category'] = category + if joinMode: + newPost['joinMode'] = joinMode + newPost['startTime'] = eventDateStr + newPost['endTime'] = endDateStr + if location: + newPost['location'] = location + if maximumAttendeeCapacity: + newPost['maximumAttendeeCapacity'] = \ + maximumAttendeeCapacity if ccUrl: if len(ccUrl) > 0: newPost['cc'] = [ccUrl] @@ -1013,7 +1084,7 @@ def postIsAddressedToPublic(baseDir: str, postJsonObject: {}) -> bool: def createPublicPost(baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, - clientToServer: bool, + clientToServer: bool, commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, inReplyTo=None, inReplyToAtomUri=None, subject=None, @@ -1031,11 +1102,13 @@ def createPublicPost(baseDir: str, httpPrefix + '://' + domainFull + '/users/' + nickname + '/followers', httpPrefix, content, followersOnly, saveToFile, - clientToServer, + clientToServer, commentsEnabled, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, inReplyTo, inReplyToAtomUri, subject, - schedulePost, eventDate, eventTime, location, None) + schedulePost, eventDate, eventTime, location, + None, None, None, None, None, + None, None, None, None) def createBlogPost(baseDir: str, @@ -1065,7 +1138,7 @@ def createQuestionPost(baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, content: str, qOptions: [], followersOnly: bool, saveToFile: bool, - clientToServer: bool, + clientToServer: bool, commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, subject: str, durationDays: int) -> {}: @@ -1082,11 +1155,13 @@ def createQuestionPost(baseDir: str, httpPrefix + '://' + domainFull + '/users/' + nickname + '/followers', httpPrefix, content, followersOnly, saveToFile, - clientToServer, + clientToServer, commentsEnabled, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, None, None, subject, - False, None, None, None, None) + False, None, None, None, None, None, + None, None, None, + None, None, None, None) messageJson['object']['type'] = 'Question' messageJson['object']['oneOf'] = [] messageJson['object']['votersCount'] = 0 @@ -1111,7 +1186,7 @@ def createQuestionPost(baseDir: str, def createUnlistedPost(baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, - clientToServer: bool, + clientToServer: bool, commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, inReplyTo=None, inReplyToAtomUri=None, subject=None, @@ -1129,11 +1204,13 @@ def createUnlistedPost(baseDir: str, nickname + '/followers', 'https://www.w3.org/ns/activitystreams#Public', httpPrefix, content, followersOnly, saveToFile, - clientToServer, + clientToServer, commentsEnabled, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, inReplyTo, inReplyToAtomUri, subject, - schedulePost, eventDate, eventTime, location, None) + schedulePost, eventDate, eventTime, location, + None, None, None, None, None, + None, None, None, None) def createFollowersOnlyPost(baseDir: str, @@ -1141,7 +1218,7 @@ def createFollowersOnlyPost(baseDir: str, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, - clientToServer: bool, + clientToServer: bool, commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, inReplyTo=None, inReplyToAtomUri=None, @@ -1160,11 +1237,13 @@ def createFollowersOnlyPost(baseDir: str, nickname + '/followers', None, httpPrefix, content, followersOnly, saveToFile, - clientToServer, + clientToServer, commentsEnabled, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, inReplyTo, inReplyToAtomUri, subject, - schedulePost, eventDate, eventTime, location, None) + schedulePost, eventDate, eventTime, location, + None, None, None, None, None, + None, None, None, None) def createEventPost(baseDir: str, @@ -1172,16 +1251,23 @@ def createEventPost(baseDir: str, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, - clientToServer: bool, + clientToServer: bool, commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, subject=None, schedulePost=False, eventDate=None, eventTime=None, - location=None) -> {}: + location=None, category=None, joinMode=None, + endDate=None, endTime=None, + maximumAttendeeCapacity=None, + repliesModerationOption=None, + anonymousParticipationEnabled=None, + eventStatus=None) -> {}: """Mobilizon-type Event post """ if not attachImageFilename: return None + if not category: + return None domainFull = domain if port: if port != 80 and port != 443: @@ -1197,15 +1283,19 @@ def createEventPost(baseDir: str, if followersOnly: toStr1 = toStr2 toStr2 = None - return createPostBase(baseDir, nickname, domain, port, - toStr1, toStr2, - httpPrefix, content, followersOnly, saveToFile, - clientToServer, - attachImageFilename, mediaType, - imageDescription, useBlurhash, - False, False, None, None, subject, - schedulePost, eventDate, eventTime, location, - eventUUID) + createPostBase(baseDir, nickname, domain, port, + toStr1, toStr2, + httpPrefix, content, followersOnly, saveToFile, + clientToServer, commentsEnabled, + attachImageFilename, mediaType, + imageDescription, useBlurhash, + False, False, None, None, subject, + schedulePost, eventDate, eventTime, location, + eventUUID, category, joinMode, + endDate, endTime, maximumAttendeeCapacity, + repliesModerationOption, + anonymousParticipationEnabled, + eventStatus) def getMentionedPeople(baseDir: str, httpPrefix: str, @@ -1248,6 +1338,7 @@ def createDirectMessagePost(baseDir: str, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, clientToServer: bool, + commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, inReplyTo=None, inReplyToAtomUri=None, @@ -1270,11 +1361,13 @@ def createDirectMessagePost(baseDir: str, createPostBase(baseDir, nickname, domain, port, postTo, postCc, httpPrefix, content, followersOnly, saveToFile, - clientToServer, + clientToServer, commentsEnabled, attachImageFilename, mediaType, imageDescription, useBlurhash, False, False, inReplyTo, inReplyToAtomUri, subject, - schedulePost, eventDate, eventTime, location, None) + schedulePost, eventDate, eventTime, location, + None, None, None, None, None, + None, None, None, None) # mentioned recipients go into To rather than Cc messageJson['to'] = messageJson['object']['cc'] messageJson['object']['to'] = messageJson['to'] @@ -1289,7 +1382,7 @@ def createDirectMessagePost(baseDir: str, def createReportPost(baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, - clientToServer: bool, + clientToServer: bool, commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, debug: bool, subject=None) -> {}: @@ -1362,11 +1455,13 @@ def createReportPost(baseDir: str, createPostBase(baseDir, nickname, domain, port, toUrl, postCc, httpPrefix, content, followersOnly, saveToFile, - clientToServer, + clientToServer, commentsEnabled, attachImageFilename, mediaType, imageDescription, useBlurhash, True, False, None, None, subject, - False, None, None, None, None) + False, None, None, None, None, None, + None, None, None, + None, None, None, None) if not postJsonObject: continue @@ -1450,6 +1545,7 @@ def sendPost(projectVersion: str, toNickname: str, toDomain: str, toPort: int, cc: str, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, clientToServer: bool, + commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, federationList: [], sendThreads: [], postLog: [], @@ -1518,11 +1614,14 @@ def sendPost(projectVersion: str, createPostBase(baseDir, nickname, domain, port, toPersonId, cc, httpPrefix, content, followersOnly, saveToFile, clientToServer, + commentsEnabled, attachImageFilename, mediaType, imageDescription, useBlurhash, False, isArticle, inReplyTo, inReplyToAtomUri, subject, - False, None, None, None, None) + False, None, None, None, None, None, + None, None, None, + None, None, None, None) # get the senders private key privateKeyPem = getPersonKey(nickname, domain, baseDir, 'private') @@ -1576,6 +1675,7 @@ def sendPostViaServer(projectVersion: str, fromDomain: str, fromPort: int, toNickname: str, toDomain: str, toPort: int, cc: str, httpPrefix: str, content: str, followersOnly: bool, + commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, useBlurhash: bool, cachedWebfingers: {}, personCache: {}, @@ -1662,11 +1762,14 @@ def sendPostViaServer(projectVersion: str, fromNickname, fromDomain, fromPort, toPersonId, cc, httpPrefix, content, followersOnly, saveToFile, clientToServer, + commentsEnabled, attachImageFilename, mediaType, imageDescription, useBlurhash, False, isArticle, inReplyTo, inReplyToAtomUri, subject, - False, None, None, None, None) + False, None, None, None, None, None, + None, None, None, + None, None, None, None) authHeader = createBasicAuthHeader(fromNickname, password) diff --git a/utils.py b/utils.py index d5601f651..3d9720fb1 100644 --- a/utils.py +++ b/utils.py @@ -402,7 +402,7 @@ def locatePost(baseDir: str, nickname: str, domain: str, if os.path.isfile(postFilename): return postFilename - print('WARN: unable to locate ' + nickname + ' ' + postUrl) + # print('WARN: unable to locate ' + nickname + ' ' + postUrl) return None From 1983f8935e29fb334db7f6d6118c718dfc502091 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 21 Aug 2020 18:40:50 +0100 Subject: [PATCH 07/84] Fix tests --- epicyon.py | 23 ++++++++++++++--------- tests.py | 27 +++++++++++++++++---------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/epicyon.py b/epicyon.py index c7809ecbd..a1915c138 100644 --- a/epicyon.py +++ b/epicyon.py @@ -166,6 +166,11 @@ parser.add_argument('--json', dest='json', type=str, default=None, help='Show the json for a given activitypub url') parser.add_argument('-f', '--federate', nargs='+', dest='federationList', help='Specify federation list separated by spaces') +parser.add_argument("--repliesEnabled", "--commentsEnabled", + dest='commentsEnabled', + type=str2bool, nargs='?', + const=True, default=True, + help="Enable replies to a post") parser.add_argument("--noapproval", type=str2bool, nargs='?', const=True, default=False, help="Allow followers without approval") @@ -829,7 +834,7 @@ if args.message: domain, port, toNickname, toDomain, toPort, ccUrl, httpPrefix, sendMessage, followersOnly, - attach, mediaType, + args.commentsEnabled, attach, mediaType, attachedImageDescription, useBlurhash, cachedWebfingers, personCache, isArticle, args.debug, replyTo, replyTo, subject) @@ -1751,30 +1756,30 @@ if args.testdata: deleteAllPosts(baseDir, nickname, domain, 'outbox') createPublicPost(baseDir, nickname, domain, port, httpPrefix, "like, this is totally just a #test, man", - False, True, False, None, None, useBlurhash) + False, True, False, True, None, None, useBlurhash) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Zoiks!!!", - False, True, False, None, None, useBlurhash) + False, True, False, True, None, None, useBlurhash) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Hey scoob we need like a hundred more #milkshakes", - False, True, False, None, None, useBlurhash) + False, True, False, True, None, None, useBlurhash) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Getting kinda spooky around here", - False, True, False, None, None, useBlurhash, 'someone') + False, True, False, True, None, None, useBlurhash, 'someone') createPublicPost(baseDir, nickname, domain, port, httpPrefix, "And they would have gotten away with it too" + "if it wasn't for those pesky hackers", - False, True, False, 'img/logo.png', + False, True, False, True, 'img/logo.png', 'Description of image', useBlurhash) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "man, these centralized sites are, like, the worst!", - False, True, False, None, None, useBlurhash) + False, True, False, True, None, None, useBlurhash) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "another mystery solved #test", - False, True, False, None, None, useBlurhash) + False, True, False, True, None, None, useBlurhash) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "let's go bowling", - False, True, False, None, None, useBlurhash) + False, True, False, True, None, None, useBlurhash) domainFull = domain + ':' + str(port) clearFollows(baseDir, nickname, domain) diff --git a/tests.py b/tests.py index f7a27aaa3..b6ac4239b 100644 --- a/tests.py +++ b/tests.py @@ -270,14 +270,16 @@ def createServerAlice(path: str, domain: str, port: int, clientToServer = False createPublicPost(path, nickname, domain, port, httpPrefix, "No wise fish would go anywhere without a porpoise", - False, True, clientToServer, None, None, useBlurhash) + False, True, clientToServer, True, + None, None, useBlurhash) createPublicPost(path, nickname, domain, port, httpPrefix, "Curiouser and curiouser!", False, True, - clientToServer, None, None, useBlurhash) + clientToServer, True, None, None, useBlurhash) createPublicPost(path, nickname, domain, port, httpPrefix, "In the gardens of memory, in the palace " + "of dreams, that is where you and I shall meet", - False, True, clientToServer, None, None, useBlurhash) + False, True, clientToServer, True, + None, None, useBlurhash) global testServerAliceRunning testServerAliceRunning = True maxMentions = 10 @@ -335,14 +337,17 @@ def createServerBob(path: str, domain: str, port: int, if hasPosts: createPublicPost(path, nickname, domain, port, httpPrefix, "It's your life, live it your way.", - False, True, clientToServer, None, None, useBlurhash) + False, True, clientToServer, True, + None, None, useBlurhash) createPublicPost(path, nickname, domain, port, httpPrefix, "One of the things I've realised is that " + "I am very simple", - False, True, clientToServer, None, None, useBlurhash) + False, True, clientToServer, True, + None, None, useBlurhash) createPublicPost(path, nickname, domain, port, httpPrefix, "Quantum physics is a bit of a passion of mine", - False, True, clientToServer, None, None, useBlurhash) + False, True, clientToServer, True, + None, None, useBlurhash) global testServerBobRunning testServerBobRunning = True maxMentions = 10 @@ -503,7 +508,8 @@ def testPostMessageBetweenServers(): 'Why is a mouse when it spins? ' + 'यह एक परीक्षण है #sillyquestion', followersOnly, - saveToFile, clientToServer, attachedImageFilename, mediaType, + saveToFile, clientToServer, True, + attachedImageFilename, mediaType, attachedImageDescription, useBlurhash, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers, alicePersonCache, isArticle, inReplyTo, @@ -788,7 +794,8 @@ def testFollowBetweenServers(): sessionAlice, aliceDir, 'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Alice message', followersOnly, saveToFile, - clientToServer, None, None, None, useBlurhash, federationList, + clientToServer, True, + None, None, None, useBlurhash, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers, alicePersonCache, isArticle, inReplyTo, inReplyToAtomUri, subject) @@ -1092,7 +1099,7 @@ def testCreatePerson(): archivePostsForPerson(nickname, domain, baseDir, 'outbox', None, {}, 4) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "G'day world!", False, True, clientToServer, - None, None, useBlurhash, None, None, + True, None, None, useBlurhash, None, None, 'Not suitable for Vogons') os.chdir(currDir) @@ -1315,7 +1322,7 @@ def testClientToServer(): aliceDomain, alicePort, 'bob', bobDomain, bobPort, None, httpPrefix, 'Sent from my ActivityPub client', - followersOnly, + followersOnly, True, attachedImageFilename, mediaType, attachedImageDescription, useBlurhash, cachedWebfingers, personCache, isArticle, From c64a8aadc163fa78ef323a89b5b73ef495da7e00 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 21 Aug 2020 19:32:16 +0100 Subject: [PATCH 08/84] Check for allowed comments --- epicyon.py | 3 ++- inbox.py | 36 ++++++++++++++++++++++++++++++++++++ tests.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/epicyon.py b/epicyon.py index a1915c138..e4c7f00b5 100644 --- a/epicyon.py +++ b/epicyon.py @@ -1765,7 +1765,8 @@ if args.testdata: False, True, False, True, None, None, useBlurhash) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Getting kinda spooky around here", - False, True, False, True, None, None, useBlurhash, 'someone') + False, True, False, True, None, None, + useBlurhash, 'someone') createPublicPost(baseDir, nickname, domain, port, httpPrefix, "And they would have gotten away with it too" + "if it wasn't for those pesky hackers", diff --git a/inbox.py b/inbox.py index 1721105fb..4250f8d75 100644 --- a/inbox.py +++ b/inbox.py @@ -1585,6 +1585,28 @@ def receiveUndoAnnounce(recentPostsCache: {}, return True +def jsonPostAllowsComments(postJsonObject: {}) -> bool: + """Returns true if the given post allows comments/replies + """ + if 'commentsEnabled' in postJsonObject: + return postJsonObject['commentsEnabled'] + if postJsonObject.get('object'): + if not isinstance(postJsonObject['object'], dict): + return False + if 'commentsEnabled' in postJsonObject['object']: + return postJsonObject['object']['commentsEnabled'] + return True + + +def postAllowsComments(postFilename: str) -> bool: + """Returns true if the given post allows comments/replies + """ + postJsonObject = loadJson(postFilename) + if not postJsonObject: + return False + return jsonPostAllowsComments(postJsonObject) + + def populateReplies(baseDir: str, httpPrefix: str, domain: str, messageJson: {}, maxReplies: int, debug: bool) -> bool: """Updates the list of replies for a post on this domain if @@ -1625,6 +1647,10 @@ def populateReplies(baseDir: str, httpPrefix: str, domain: str, if debug: print('DEBUG: post may have expired - ' + replyTo) return False + if not postAllowsComments(postFilename): + if debug: + print('DEBUG: post does not allow comments - ' + replyTo) + return False # populate a text file containing the ids of replies postRepliesFilename = postFilename.replace('.json', '.replies') messageId = messageJson['id'].replace('/activity', '') @@ -1720,6 +1746,16 @@ def validPostContent(baseDir: str, nickname: str, domain: str, messageJson['object']['content']): print('REJECT: content filtered') return False + if messageJson['object'].get('inReplyTo'): + if isinstance(messageJson['object']['inReplyTo'], str): + originalPostId = messageJson['object']['inReplyTo'] + postPostFilename = locatePost(baseDir, nickname, domain, + originalPostId) + if postPostFilename: + if not postAllowsComments(postPostFilename): + print('REJECT: reply to post which does not ' + + 'allow comments: ' + originalPostId) + return False print('ACCEPT: post content is valid') return True diff --git a/tests.py b/tests.py index b6ac4239b..69095aa43 100644 --- a/tests.py +++ b/tests.py @@ -62,6 +62,7 @@ from announce import sendAnnounceViaServer from media import getMediaPath from media import getAttachmentMediaType from delete import sendDeleteViaServer +from inbox import jsonPostAllowsComments from inbox import validInbox from inbox import validInboxFilenames from content import htmlReplaceQuoteMarks @@ -1981,8 +1982,41 @@ def runHtmlReplaceQuoteMarks(): assert result == '“hello” “test” html' +def testJsonPostAllowsComments(): + print('testJsonPostAllowsComments') + postJsonObject = { + "id": "123" + } + assert jsonPostAllowsComments(postJsonObject) + postJsonObject = { + "id": "123", + "commentsEnabled": False + } + assert not jsonPostAllowsComments(postJsonObject) + postJsonObject = { + "id": "123", + "commentsEnabled": True + } + assert jsonPostAllowsComments(postJsonObject) + postJsonObject = { + "id": "123", + "object": { + "commentsEnabled": True + } + } + assert jsonPostAllowsComments(postJsonObject) + postJsonObject = { + "id": "123", + "object": { + "commentsEnabled": False + } + } + assert not jsonPostAllowsComments(postJsonObject) + + def runAllTests(): print('Running tests...') + testJsonPostAllowsComments() runHtmlReplaceQuoteMarks() testDangerousMarkup() testRemoveHtml() From 109caa2d5df145e8a79ed3bf05867fa62aa4cace Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 21 Aug 2020 19:39:31 +0100 Subject: [PATCH 09/84] Don't show reply icon for posts where comments are not enabled --- webinterface.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 79205e8c6..6d766c8fe 100644 --- a/webinterface.py +++ b/webinterface.py @@ -3828,7 +3828,13 @@ def individualPostAsHtml(recentPostsCache: {}, maxRecentPosts: int, iconsDir + '/dm.png" class="DMicon"/>\n' replyStr = '' - if showIcons: + # check if replying is permitted + commentsEnabled = True + if 'commentsEnabled' in postJsonObject['object']: + if postJsonObject['object']['commentsEnabled'] is False: + commentsEnabled = False + if showIcons and commentsEnabled: + # reply is permitted - create reply icon replyToLink = postJsonObject['object']['id'] if postJsonObject['object'].get('attributedTo'): if isinstance(postJsonObject['object']['attributedTo'], str): From b619533866cdfdc800fbe7efd43a5e80ed296a0b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 21 Aug 2020 20:51:35 +0100 Subject: [PATCH 10/84] Option to disable replies on posts --- daemon.py | 4 +++- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- webinterface.py | 5 +++++ 17 files changed, 38 insertions(+), 16 deletions(-) diff --git a/daemon.py b/daemon.py index 965c54a9d..c230a8302 100644 --- a/daemon.py +++ b/daemon.py @@ -5490,11 +5490,13 @@ class PubServer(BaseHTTPRequestHandler): fields['subject'] = None if not fields.get('replyTo'): fields['replyTo'] = None + if not fields.get('schedulePost'): fields['schedulePost'] = False else: fields['schedulePost'] = True print('DEBUG: shedulePost ' + str(fields['schedulePost'])) + if not fields.get('eventDate'): fields['eventDate'] = None if not fields.get('eventTime'): @@ -5520,7 +5522,7 @@ class PubServer(BaseHTTPRequestHandler): if fields.get('mentions'): mentionsStr = fields['mentions'].strip() + ' ' commentsEnabled = True - if fields.get('commentsEnabled'): + if 'commentsEnabled' in fields: commentsEnabled = fields['commentsEnabled'] if postType == 'newpost': messageJson = \ diff --git a/translations/ar.json b/translations/ar.json index b13483b47..dfc191fa3 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -255,5 +255,6 @@ "Liked by": "نال إعجاب", "Solidaric": "تضامن", "YouTube Replacement Domain": "استبدال نطاق يوتيوب", - "Notes": "ملاحظات" + "Notes": "ملاحظات", + "Allow replies.": "السماح بالردود." } diff --git a/translations/ca.json b/translations/ca.json index 774fd067a..97b26f583 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -255,5 +255,6 @@ "Liked by": "M'agrada", "Solidaric": "Solidaritat", "YouTube Replacement Domain": "Domini de substitució de YouTube", - "Notes": "Notes" + "Notes": "Notes", + "Allow replies.": "Permetre respostes." } diff --git a/translations/cy.json b/translations/cy.json index 28f8cdc38..6b5448c6c 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -255,5 +255,6 @@ "Liked by": "Hoffi", "Solidaric": "Undod", "YouTube Replacement Domain": "Parth Amnewid YouTube", - "Notes": "Nodiadau" + "Notes": "Nodiadau", + "Allow replies.": "Caniatáu atebion." } diff --git a/translations/de.json b/translations/de.json index 86f695959..f755d6916 100644 --- a/translations/de.json +++ b/translations/de.json @@ -255,5 +255,6 @@ "Liked by": "Gefallen von", "Solidaric": "Solidarität", "YouTube Replacement Domain": "YouTube-Ersatzdomain", - "Notes": "Anmerkungen" + "Notes": "Anmerkungen", + "Allow replies.": "Antworten zulassen." } diff --git a/translations/en.json b/translations/en.json index 4a559bb42..803778838 100644 --- a/translations/en.json +++ b/translations/en.json @@ -255,5 +255,6 @@ "Liked by": "Liked by", "Solidaric": "Solidaric", "YouTube Replacement Domain": "YouTube Replacement Domain", - "Notes": "Notes" + "Notes": "Notes", + "Allow replies.": "Allow replies." } diff --git a/translations/es.json b/translations/es.json index e19e01a6b..849df9d52 100644 --- a/translations/es.json +++ b/translations/es.json @@ -255,5 +255,6 @@ "Liked by": "Apreciado por", "Solidaric": "Solidaridad", "YouTube Replacement Domain": "Dominio de reemplazo de YouTube", - "Notes": "Notas" + "Notes": "Notas", + "Allow replies.": "Permitir respuestas." } diff --git a/translations/fr.json b/translations/fr.json index 17dafb27d..c55fc9566 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -255,5 +255,6 @@ "Liked by": "Aimé par", "Solidaric": "Solidarité", "YouTube Replacement Domain": "Domaine de remplacement YouTube", - "Notes": "Remarques" + "Notes": "Remarques", + "Allow replies.": "Autoriser les réponses." } diff --git a/translations/ga.json b/translations/ga.json index b012a8098..4c922cd28 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -255,5 +255,6 @@ "Liked by": "Thaitin", "Solidaric": "Dlúthpháirtíocht", "YouTube Replacement Domain": "Fearann Athsholáthair YouTube", - "Notes": "Nótaí" + "Notes": "Nótaí", + "Allow replies.": "Ceadaigh freagraí." } diff --git a/translations/hi.json b/translations/hi.json index a05b9e387..593ac3486 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -255,5 +255,6 @@ "Liked by": "द्वारा पसंद किया गया", "Solidaric": "एकजुटता", "YouTube Replacement Domain": "YouTube रिप्लेसमेंट डोमेन", - "Notes": "टिप्पणियाँ" + "Notes": "टिप्पणियाँ", + "Allow replies.": "जवाब दें।" } diff --git a/translations/it.json b/translations/it.json index 356112f64..e01fb2f1e 100644 --- a/translations/it.json +++ b/translations/it.json @@ -255,5 +255,6 @@ "Liked by": "Mi è piaciuto", "Solidaric": "Solidarietà", "YouTube Replacement Domain": "Dominio sostitutivo di YouTube", - "Notes": "Appunti" + "Notes": "Appunti", + "Allow replies.": "Consenti risposte." } diff --git a/translations/ja.json b/translations/ja.json index 89351b413..36b76b476 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -255,5 +255,6 @@ "Liked by": "好き", "Solidaric": "連帯", "YouTube Replacement Domain": "YouTube交換ドメイン", - "Notes": "ノート" + "Notes": "ノート", + "Allow replies.": "返信を許可します。" } diff --git a/translations/oc.json b/translations/oc.json index 71eddf628..449e85c86 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -251,5 +251,6 @@ "Liked by": "Liked by", "Solidaric": "Solidaric", "YouTube Replacement Domain": "YouTube Replacement Domain", - "Notes": "Notes" + "Notes": "Notes", + "Allow replies.": "Allow replies." } diff --git a/translations/pt.json b/translations/pt.json index 79c2a7cee..72343c206 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -255,5 +255,6 @@ "Liked by": "Curtida por", "Solidaric": "Solidariedade", "YouTube Replacement Domain": "Domínio de substituição do YouTube", - "Notes": "Notas" + "Notes": "Notas", + "Allow replies.": "Permitir respostas." } diff --git a/translations/ru.json b/translations/ru.json index e9befe81c..0e57ff62d 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -255,5 +255,6 @@ "Liked by": "Понравилось", "Solidaric": "солидарность", "YouTube Replacement Domain": "Запасной домен YouTube", - "Notes": "Ноты" + "Notes": "Ноты", + "Allow replies.": "Разрешить ответы." } diff --git a/translations/zh.json b/translations/zh.json index 2afa7f0c0..888a7ada9 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -255,5 +255,6 @@ "Liked by": "喜欢的人", "Solidaric": "团结互助", "YouTube Replacement Domain": "YouTube替换域", - "Notes": "笔记" + "Notes": "笔记", + "Allow replies.": "允许回复。" } diff --git a/webinterface.py b/webinterface.py index 6d766c8fe..9122e490d 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2029,6 +2029,11 @@ def htmlNewPost(mediaInstance: bool, translate: {}, endpoint != 'newquestion': dateAndLocation = '
' + dateAndLocation += \ + '

\n' + if not inReplyTo: dateAndLocation += \ '

Date: Fri, 21 Aug 2020 21:08:17 +0100 Subject: [PATCH 11/84] Get comments enabled state --- daemon.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/daemon.py b/daemon.py index c230a8302..c2e1ea994 100644 --- a/daemon.py +++ b/daemon.py @@ -5521,9 +5521,10 @@ class PubServer(BaseHTTPRequestHandler): mentionsStr = '' if fields.get('mentions'): mentionsStr = fields['mentions'].strip() + ' ' - commentsEnabled = True - if 'commentsEnabled' in fields: - commentsEnabled = fields['commentsEnabled'] + if not fields.get('commentsEnabled'): + commentsEnabled = False + else: + commentsEnabled = True if postType == 'newpost': messageJson = \ createPublicPost(self.server.baseDir, From fede8b56d6f7a0b25eb9a51276c57fb61d8b7cff Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 21 Aug 2020 22:13:50 +0100 Subject: [PATCH 12/84] Extra fields for events --- daemon.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index c2e1ea994..f17ae4304 100644 --- a/daemon.py +++ b/daemon.py @@ -5748,7 +5748,15 @@ class PubServer(BaseHTTPRequestHandler): fields['schedulePost'], fields['eventDate'], fields['eventTime'], - fields['location']) + fields['location'], + fields['category'], + fields['joinMode'], + fields['endDate'], + fields['endTime'], + fields['maximumAttendeeCapacity'], + fields['repliesModerationOption'], + fields['anonymousParticipationEnabled'], + fields['eventStatus']) if messageJson: if fields['schedulePost']: return 1 From 5326583c18460cb49025664042416bb1879e9515 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 21 Aug 2020 22:42:49 +0100 Subject: [PATCH 13/84] Add uuid to event --- daemon.py | 7 ++++++- posts.py | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index f17ae4304..c11e4c549 100644 --- a/daemon.py +++ b/daemon.py @@ -5732,6 +5732,11 @@ class PubServer(BaseHTTPRequestHandler): if not fields.get('followersOnlyEvent'): fields['followersOnlyEvent'] = False + if not fields.get('anonymousParticipationEnabled'): + anonymousParticipationEnabled = False + else: + anonymousParticipationEnabled = True + messageJson = \ createEventPost(self.server.baseDir, nickname, @@ -5755,7 +5760,7 @@ class PubServer(BaseHTTPRequestHandler): fields['endTime'], fields['maximumAttendeeCapacity'], fields['repliesModerationOption'], - fields['anonymousParticipationEnabled'], + anonymousParticipationEnabled, fields['eventStatus']) if messageJson: if fields['schedulePost']: diff --git a/posts.py b/posts.py index a86cbaf55..d3ba98b2a 100644 --- a/posts.py +++ b/posts.py @@ -854,6 +854,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, mediaType, imageDescription, useBlurhash) if eventUUID: # add attributes for Mobilizon-type events + newPost['object']['uuid'] = eventUUID if eventStatus: newPost['object']['ical:status'] = eventStatus if anonymousParticipationEnabled: @@ -916,6 +917,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, mediaType, imageDescription, useBlurhash) if eventUUID: # add attributes for Mobilizon-type events + newPost['uuid'] = eventUUID if eventStatus: newPost['ical:status'] = eventStatus if anonymousParticipationEnabled: From 3693850be318536e4a4f3328a3f4e5f362889966 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 10:15:56 +0100 Subject: [PATCH 14/84] Tidying --- posts.py | 93 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/posts.py b/posts.py index d3ba98b2a..1f8023417 100644 --- a/posts.py +++ b/posts.py @@ -607,6 +607,43 @@ def addSchedulePost(baseDir: str, nickname: str, domain: str, scheduleFile.close() +def appendEventFields(newPost: {}, + eventUUID: str, eventStatus: str, + anonymousParticipationEnabled: bool, + repliesModerationOption: str, + category: str, + joinMode: str, + eventDateStr: str, + endDateStr: str, + location: str, + maximumAttendeeCapacity: int) -> {}: + """Appends Mobilizon-type event fields to a post + """ + if not eventUUID: + return newPost + + # add attributes for Mobilizon-type events + newPost['uuid'] = eventUUID + if eventStatus: + newPost['ical:status'] = eventStatus + if anonymousParticipationEnabled: + newPost['anonymousParticipationEnabled'] = \ + anonymousParticipationEnabled + if repliesModerationOption: + newPost['repliesModerationOption'] = repliesModerationOption + if category: + newPost['category'] = category + if joinMode: + newPost['joinMode'] = joinMode + newPost['startTime'] = eventDateStr + newPost['endTime'] = endDateStr + if location: + newPost['location'] = location + if maximumAttendeeCapacity: + newPost['maximumAttendeeCapacity'] = maximumAttendeeCapacity + return newPost + + def createPostBase(baseDir: str, nickname: str, domain: str, port: int, toUrl: str, ccUrl: str, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, clientToServer: bool, @@ -852,28 +889,12 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, attachMedia(baseDir, httpPrefix, domain, port, newPost['object'], attachImageFilename, mediaType, imageDescription, useBlurhash) - if eventUUID: - # add attributes for Mobilizon-type events - newPost['object']['uuid'] = eventUUID - if eventStatus: - newPost['object']['ical:status'] = eventStatus - if anonymousParticipationEnabled: - newPost['object']['anonymousParticipationEnabled'] = \ - anonymousParticipationEnabled - if repliesModerationOption: - newPost['object']['repliesModerationOption'] = \ - repliesModerationOption - if category: - newPost['object']['category'] = category - if joinMode: - newPost['object']['joinMode'] = joinMode - newPost['object']['startTime'] = eventDateStr - newPost['object']['endTime'] = endDateStr - if location: - newPost['object']['location'] = location - if maximumAttendeeCapacity: - newPost['object']['maximumAttendeeCapacity'] = \ - maximumAttendeeCapacity + newPost = appendEventFields(newPost['object'], eventUUID, eventStatus, + anonymousParticipationEnabled, + repliesModerationOption, + category, joinMode, + eventDateStr, endDateStr, + location, maximumAttendeeCapacity) else: idStr = \ httpPrefix + '://' + domain + '/users/' + nickname + \ @@ -915,28 +936,12 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, attachMedia(baseDir, httpPrefix, domain, port, newPost, attachImageFilename, mediaType, imageDescription, useBlurhash) - if eventUUID: - # add attributes for Mobilizon-type events - newPost['uuid'] = eventUUID - if eventStatus: - newPost['ical:status'] = eventStatus - if anonymousParticipationEnabled: - newPost['anonymousParticipationEnabled'] = \ - anonymousParticipationEnabled - if repliesModerationOption: - newPost['repliesModerationOption'] = \ - repliesModerationOption - if category: - newPost['category'] = category - if joinMode: - newPost['joinMode'] = joinMode - newPost['startTime'] = eventDateStr - newPost['endTime'] = endDateStr - if location: - newPost['location'] = location - if maximumAttendeeCapacity: - newPost['maximumAttendeeCapacity'] = \ - maximumAttendeeCapacity + newPost = appendEventFields(newPost, eventUUID, eventStatus, + anonymousParticipationEnabled, + repliesModerationOption, + category, joinMode, + eventDateStr, endDateStr, + location, maximumAttendeeCapacity) if ccUrl: if len(ccUrl) > 0: newPost['cc'] = [ccUrl] From 17f41ad713dfcbfc16b73323b82eddad1810a209 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 11:21:06 +0100 Subject: [PATCH 15/84] Event option on dropdown --- img/icons/hacker/scope_event.png | Bin 0 -> 2450 bytes img/icons/henge/scope_event.png | Bin 0 -> 1364 bytes img/icons/lcd/scope_event.png | Bin 0 -> 1366 bytes img/icons/light/scope_event.png | Bin 0 -> 2404 bytes img/icons/night/scope_event.png | Bin 0 -> 2404 bytes img/icons/purple/scope_event.png | Bin 0 -> 2446 bytes img/icons/scope_event.png | Bin 0 -> 2404 bytes img/icons/solidaric/scope_event.png | Bin 0 -> 1346 bytes img/icons/starlight/scope_event.png | Bin 0 -> 1362 bytes img/icons/zen/scope_event.png | Bin 0 -> 1364 bytes translations/ar.json | 4 +++- translations/ca.json | 4 +++- translations/cy.json | 4 +++- translations/de.json | 4 +++- translations/en.json | 4 +++- translations/es.json | 4 +++- translations/fr.json | 4 +++- translations/ga.json | 4 +++- translations/hi.json | 4 +++- translations/it.json | 4 +++- translations/ja.json | 4 +++- translations/oc.json | 4 +++- translations/pt.json | 4 +++- translations/ru.json | 4 +++- translations/zh.json | 4 +++- webinterface.py | 8 ++++++++ 26 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 img/icons/hacker/scope_event.png create mode 100644 img/icons/henge/scope_event.png create mode 100644 img/icons/lcd/scope_event.png create mode 100644 img/icons/light/scope_event.png create mode 100644 img/icons/night/scope_event.png create mode 100644 img/icons/purple/scope_event.png create mode 100644 img/icons/scope_event.png create mode 100644 img/icons/solidaric/scope_event.png create mode 100644 img/icons/starlight/scope_event.png create mode 100644 img/icons/zen/scope_event.png diff --git a/img/icons/hacker/scope_event.png b/img/icons/hacker/scope_event.png new file mode 100644 index 0000000000000000000000000000000000000000..80ae04060390ba58cb649332c8f4af6978e0ad93 GIT binary patch literal 2450 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 Qb^rhX07*qoM6N<$g7NB zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=KJvg9TVh2L34mVhLL#Bwk{RkMREKL>30?Y?s- znVF<2FKya@1s3x8WV$lb@? z3@}WBR?bVS*Zc~*e%x@cLC32-_%*_QIl2aJhq6{?boz6Uu=8oBE0R#g*~!K2In;yC zw%sB7$2sq?>}=L`cs3-1N?oy#Fb^S#yY@2zInRnSqb5O6s8?U5gl)1b5(x70cy}A) zvp_E*-@Wiz{YUSU_*^f$e9|&s8lm&$0O{A_Z;784mPbVVI$(0U`a+{T}>H^ z<}0JN*mS$W!~>GczAd8|PDS5#DJULmY(U;)iw8=hLJTT1P@+ziCXL-TY6vm$U_;B? zShmf@xNwujDXl`2O^S4Juxvbn2+aJ5h4#8-uh+=2awkk>f-}M`KP=(z27jf5Iopzm zqCd1kT)grcVJvd1#w-A#d2!P%@Lewa@hN|hsvxLaX2%28c$_W{rH|Z_%~>!{(Vnjm z1?!gsNQAW&!H|GJ%p^*gqA?p0baVi!h@2(y0Rp7T9YJyu!G2`yyw=7&Mtd$RXI^IP zO#q>In+$AJ}otExmoj8TJ7AWW1Hx? zTlZdi9TWz3ijjtnJZ$(VqfX@7l$oZ^JZ<_cvp%R@RKF{~K#eYHyh*Kj_Mir9FuPsQ z+D>#a12Ikn;(c976 z(c976(c97g?#L#7Jm61k_zMLfJJY}yj$i-)0fcEoLr_UWLm+T+Z)Rz1WdHzpoPCi! zNW(xJ#a~mkA}t;4AmWgrI$01a;;2<9LWNK(wCZ4T=@&FQ#B6R=47{QbA%qb} zTxOOrCrK%Ij<0+8_<9%PS>EUV93eGtF~BDh&oaZbi8qL+H*JISK5>MVWR>`wc-*85 z5RA3`Ut4@lAH0>um{KKwaB9}t0G8j1)P=yBB^@IPx?{2L^ ze6p1kOaQ$vj`J}J1a^UD-EqE;9jAE$_@99*z2mPofSFIy>m4n61cbMNi|dZ2>;acM zz|fN+o3bnUX$pk`@P0<$lmiBDfq}JFZ|!}YJ^&f&Ds=-K90FrS%3k+)cYk+p|DI|0 z_XEABa>Z71XFC7@00vM@R7C&)0D*RU`y#%)00001bW%=J06^y0W&i*H0b)x>L;#2d z9Y_EG010qNS#tmY4c7nw4c7reD4Tcy000McNliru zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=HImgFW3h2L34mVhLL#Bwk{=gbbW{9LeIUDeZ* z%uMo-hcd^21s3xC1XW@D`E78l(jOW)1QNcoliSmk%ThNPA+cGp&op; z?GD*L?sWYPgc?e0|wVx5lc~{(un#D1X#LySB={+9S@VR=NvuLG8DS6_%cyU#h!ud69T z(R^jp7MpH2n0P>P*{@|3!>#DsE(OJ7jSa|qZ1F&8RER-k21?Ya(xkE5Mhzh*9&Bit z8_Tx27#D7`IHgr+vPqFH4t|YC0JqOD81FT{@15 zp1XDLrPo1W;G`I7=*Yu{k22~+u1%R~>de!o&ob+S+C}xd@(a}HqQ;xlnr9Dcum-c+ z1+C*m7c&s!L?CX901}!Pv*?r(FLH}nER0Q|j39Nf37r-(AWVZ;Cq3AGkozfaLH(z= z@jsCZ3*CP~E&$#4+`gdJ*Y8~0#Liu~G>wAYHw8j?sC+b>7fMOjPt;omxVJ9;~M zJ9;~MJ9;~MJNh3u!awzie;R)Q(@i{vgFA+40004mX+uL$Nkc;*aB^>EX>4Tx0C=2z zkv&MmKpe$iTZ}?mh0_0scmXsb<$WplX(pP9}tGZdK@hMGz4T zU<5IVnfjb4rrU7TlmpZjz4DS49tK7n|a>4rtTK|H-_>74h8!>lAJ#OK6g z23?T&k?XR{Z=8z``*~*A$fW0q!^A?dgXIopB|{~iB91DmM*04%%L?Z$&T6H`TKD8H z4CS?zWv5bW zxZDATo^;8O94SE4Unl_YXY@@uVDJ{`U2}VD?c?+T$WT|Q8{ps&h!rV&-Q(RooxS~g zrq$mMj*N1$A2}#q00006P)t-s0000pISuoF69E7K00DGTPE!Ct=GbNc0004EOGiWi zhy@);00009a7bBm000fw000fw0YWI7cmMzZ2XskIMF->s91tchkN~;(0000vNklYH7;J#~KM;Tz>>x%1jBxf`1ONa4 literal 0 HcmV?d00001 diff --git a/img/icons/light/scope_event.png b/img/icons/light/scope_event.png new file mode 100644 index 0000000000000000000000000000000000000000..6d5789c3a5141a9e10c43bf1ce8058bbd6dddcbc GIT binary patch literal 2404 zcmV-q37htbP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U;0blH@21{nshx2nHdRgaTo7r;W&)AC%@|ND2)Klm|ABqt&I5J`*DiQBr zJyed@zj3G))Ydag><~c46c>QJk3CPxQEzCvy%bj~2)1%ew45 zJnHv|n$9+d7%YSk$MZFhBj8VPALfy8#4T?XkhiU3!-D%{EKnI_oM|STVfwsQQ=y`> z-3DB(t(NClrkf)S%v)I^ikNF` z@x+<2PHfU%Kn7P)9^;U;bn*fBc=KyRHXMp9LAbx_2h@mVjPDH!}6gEkcJ$vWHdmp?Oxg0pS zAi;+aVn`uJjxI{{F~k^C%*iH34V2_lNHL|9v!#rUGGt|}&loATaB)S7FQLSeO0Ec> z>Z(*_ke}g$rsJr4SAgM$3sPWW!-wY{bk!#1tRKD2#|NQbEuVM%`N;!`DH&!28^rFFB`y4py_2D8`9q zqNuN>SVG2KS3|vz6Jf>&p7hQ9?(s849o|<`R zGUvKCZR*k#E77VmHWbVd=;{F1+^2DK5*G9-F3!Uub=#cU50aCYBiv>nS7>Psmo2TQ z&JZC}MAY7-&$V(vK;$rbEz(4|8}G%min2J>q>lQ138I!?znohz(&shP}?;pGWVz|3Xs&;}ng*xXC;&=X$lwR1!JO%a(e zB1NuKh?`d$BR4>_E@qA~ZPb`jY$N79oKqP}EKFW#thmrLbef5zP&?0H`5?aO zss5Omkme>IZ`1M1;txIqIZ&}e&)(DnwH=wd{E}kSZ=V$`X(HK?u4BXZZSvGZEb=a2 zr-;KI6>)13wwMM%qiNG88GH1dyVoyodpZj8S!v6&B7@QP9!xl|jK|ybtCjIUf<9?B z&q!z(-eHj_#+9ZomJlRt4 z2@%+%d#?0cqH{!*>Mkt5_^EeXxz@9R*tB4wVl+2FT)^6QB|h}Zy;JDKD{j_Klulv&FIzElwH214YG$XbE72n~ zk1CHHoV1Xdc0#8m^adN6evuzA05@|p9;rz`Wi*e?l)lqV->{lWuejLNCW!nXcj=tf zJinaZ6guTKR6pZ2U)D)_sB}MOHuUvZ2OKQwPiTy<0P)NTE`My7bl)%EwadKPr9E$# zbl)#OYL|N^YAM_)3X@qg7h0cu>T7B za|CG;(f{Zy+Sq0$b8`Ry0flKpLr_UWLm+T+Z)Rz1WdHzpoPCi!NW(xJ#a~lPMJf(0 zBI1yt3W5bu5l5{;5h{dQp;ZTyOTVB=Lz3d+D7Y3J{8+3yxH#+T;3^1$A0SSSPKqv4 z;{TFDi&zhi`|y2b%bx6M={E@rZ;V(=9K^dk%rqcXFMIY~;QpQBgJSq$)r#IwvWZQ>2$=}p_TyR<7 znGrLcnkSABi-k5;+L#qhjd+SUs%kpr3mK19&Rd+da+Njj$zK@C=_|`zr#XZK7O@10 zI#N(Y31!%b(W;YTAw~P~4*o&cFOf?jR|$+93#dSY?E1m~;CHuHesaP~3dMoIi{pF@ z1HoOOQFEN{W5;Qn0RCs-N^kiqbztU`^jb@c9szyZz{Pb-Q}%$%9bn+ekWJZ@{4|Ap z9(X^aZ^{DUTcBsn>#ezu(+40;T_taTgF|4XK-udZ-re2a+rMX;{rv!wa&ol!Z$p>> z000b7OjJbx0000000000U}=OS)N5e?0004WQchCF7H zfd4-fzy$1J0te6qKA;Qyp+Mj>gnu3`z`*bahNve%hQo%iwWK?YA3biF(PMuUAOHX@ WM9KYZ)W;S80000 zaB^>EX>4U6ba`-PAZ2)IW&i+q+U;0blH@21{nshx2nHdRgaTo7r;W&)AC%@|ND2)Klm|ABqt&I5J`*DiQBr zJyed@zj3G))Ydag><~c46c>QJk3CPxQEzCvy%bj~2)1%ew45 zJnHv|n$9+d7%YSk$MZFhBj8VPALfy8#4T?XkhiU3!-D%{EKnI_oM|STVfwsQQ=y`> z-3DB(t(NClrkf)S%v)I^ikNF` z@x+<2PHfU%Kn7P)9^;U;bn*fBc=KyRHXMp9LAbx_2h@mVjPDH!}6gEkcJ$vWHdmp?Oxg0pS zAi;+aVn`uJjxI{{F~k^C%*iH34V2_lNHL|9v!#rUGGt|}&loATaB)S7FQLSeO0Ec> z>Z(*_ke}g$rsJr4SAgM$3sPWW!-wY{bk!#1tRKD2#|NQbEuVM%`N;!`DH&!28^rFFB`y4py_2D8`9q zqNuN>SVG2KS3|vz6Jf>&p7hQ9?(s849o|<`R zGUvKCZR*k#E77VmHWbVd=;{F1+^2DK5*G9-F3!Uub=#cU50aCYBiv>nS7>Psmo2TQ z&JZC}MAY7-&$V(vK;$rbEz(4|8}G%min2J>q>lQ138I!?znohz(&shP}?;pGWVz|3Xs&;}ng*xXC;&=X$lwR1!JO%a(e zB1NuKh?`d$BR4>_E@qA~ZPb`jY$N79oKqP}EKFW#thmrLbef5zP&?0H`5?aO zss5Omkme>IZ`1M1;txIqIZ&}e&)(DnwH=wd{E}kSZ=V$`X(HK?u4BXZZSvGZEb=a2 zr-;KI6>)13wwMM%qiNG88GH1dyVoyodpZj8S!v6&B7@QP9!xl|jK|ybtCjIUf<9?B z&q!z(-eHj_#+9ZomJlRt4 z2@%+%d#?0cqH{!*>Mkt5_^EeXxz@9R*tB4wVl+2FT)^6QB|h}Zy;JDKD{j_Klulv&FIzElwH214YG$XbE72n~ zk1CHHoV1Xdc0#8m^adN6evuzA05@|p9;rz`Wi*e?l)lqV->{lWuejLNCW!nXcj=tf zJinaZ6guTKR6pZ2U)D)_sB}MOHuUvZ2OKQwPiTy<0P)NTE`My7bl)%EwadKPr9E$# zbl)#OYL|N^YAM_)3X@qg7h0cu>T7B za|CG;(f{Zy+Sq0$b8`Ry0flKpLr_UWLm+T+Z)Rz1WdHzpoPCi!NW(xJ#a~lPMJf(0 zBI1yt3W5bu5l5{;5h{dQp;ZTyOTVB=Lz3d+D7Y3J{8+3yxH#+T;3^1$A0SSSPKqv4 z;{TFDi&zhi`|y2b%bx6M={E@rZ;V(=9K^dk%rqcXFMIY~;QpQBgJSq$)r#IwvWZQ>2$=}p_TyR<7 znGrLcnkSABi-k5;+L#qhjd+SUs%kpr3mK19&Rd+da+Njj$zK@C=_|`zr#XZK7O@10 zI#N(Y31!%b(W;YTAw~P~4*o&cFOf?jR|$+93#dSY?E1m~;CHuHesaP~3dMoIi{pF@ z1HoOOQFEN{W5;Qn0RCs-N^kiqbztU`^jb@c9szyZz{Pb-Q}%$%9bn+ekWJZ@{4|Ap z9(X^aZ^{DUTcBsn>#ezu(+40;T_taTgF|4XK-udZ-re2a+rMX;{rv!wa&ol!Z$p>> z000b7OjJbx0000000000U}=OS)N5e?0004WQchCF7H zfd4-fzy$1J0te6qKA;Qyp+Mj>gnu3`z`*bahNve%hQo%iwWK?YA3biF(PMuUAOHX@ WM9KYZ)W;S80000 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 WM9KYZ)W;S80000 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' + translate['Reminder'] + \ '
' + translate['Scheduled note to yourself'] + \ '\n' + dropDownContent += " " \ + '

  • ' + translate['Event'] + \ + '
    ' + translate['Create an event'] + \ + '
  • \n' dropDownContent += " " \ '
  • Date: Sat, 22 Aug 2020 14:40:48 +0100 Subject: [PATCH 17/84] Adding new event screen --- daemon.py | 6 +++- translations/ar.json | 12 +++++++- translations/ca.json | 12 +++++++- translations/cy.json | 12 +++++++- translations/de.json | 12 +++++++- translations/en.json | 12 +++++++- translations/es.json | 12 +++++++- translations/fr.json | 12 +++++++- translations/ga.json | 12 +++++++- translations/hi.json | 12 +++++++- translations/it.json | 12 +++++++- translations/ja.json | 12 +++++++- translations/oc.json | 12 +++++++- translations/pt.json | 12 +++++++- translations/ru.json | 12 +++++++- translations/zh.json | 12 +++++++- webinterface.py | 72 +++++++++++++++++++++++++++++++++++++------- 17 files changed, 231 insertions(+), 27 deletions(-) diff --git a/daemon.py b/daemon.py index c11e4c549..f6330eadf 100644 --- a/daemon.py +++ b/daemon.py @@ -5525,6 +5525,10 @@ class PubServer(BaseHTTPRequestHandler): commentsEnabled = False else: commentsEnabled = True + if not fields.get('privateEvent'): + privateEvent = False + else: + privateEvent = True if postType == 'newpost': messageJson = \ createPublicPost(self.server.baseDir, @@ -5744,7 +5748,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.port, self.server.httpPrefix, mentionsStr + fields['message'], - fields['followersOnlyEvent'], + privateEvent, False, False, commentsEnabled, filename, attachmentMediaType, fields['imageDescription'], diff --git a/translations/ar.json b/translations/ar.json index 9968a7e63..0179e241b 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -258,5 +258,15 @@ "Notes": "ملاحظات", "Allow replies.": "السماح بالردود.", "Event": "حدث", - "Create an event": "أنشئ حدثًا" + "Create an event": "أنشئ حدثًا", + "Describe the event": "صف الحدث", + "Start Date": "تاريخ البدء", + "End Date": "تاريخ الانتهاء", + "Categories": "التصنيفات", + "This is a private event.": "هذا هو الحدث الخاص.", + "Allow anonymous participation.": "السماح بالمشاركة المجهولة.", + "Anyone can join": "يمكن لأي شخص الانضمام", + "Apply to join": "تقديم طلب للانضمام", + "Invitation only": "المدعوون فقط", + "Joining": "انضمام" } diff --git a/translations/ca.json b/translations/ca.json index 1f670ff2a..4e552fabc 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -258,5 +258,15 @@ "Notes": "Notes", "Allow replies.": "Permetre respostes.", "Event": "Esdeveniment", - "Create an event": "Crea un esdeveniment" + "Create an event": "Crea un esdeveniment", + "Describe the event": "Descriviu l’esdeveniment", + "Start Date": "Data d'inici", + "End Date": "Data de finalització", + "Categories": "Categories", + "This is a private event.": "Aquest és un esdeveniment privat.", + "Allow anonymous participation.": "Permet una participació anònima.", + "Anyone can join": "Qualsevol persona s’hi pot apuntar", + "Apply to join": "Sol·liciteu participar", + "Invitation only": "Només invitació", + "Joining": "Unir-se" } diff --git a/translations/cy.json b/translations/cy.json index 2d148bceb..c1a47db05 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -258,5 +258,15 @@ "Notes": "Nodiadau", "Allow replies.": "Caniatáu atebion.", "Event": "Digwyddiad", - "Create an event": "Creu digwyddiad" + "Create an event": "Creu digwyddiad", + "Describe the event": "Disgrifiwch y digwyddiad", + "Start Date": "Dyddiad cychwyn", + "End Date": "Dyddiad Gorffen", + "Categories": "Categorïau", + "This is a private event.": "Digwyddiad preifat yw hwn.", + "Allow anonymous participation.": "Caniatáu cyfranogiad dienw.", + "Anyone can join": "Gall unrhyw un ymuno", + "Apply to join": "Gwnewch gais i ymuno", + "Invitation only": "Gwahoddiad yn unig", + "Joining": "Yn ymuno" } diff --git a/translations/de.json b/translations/de.json index 16773b7ca..d3a67ebbb 100644 --- a/translations/de.json +++ b/translations/de.json @@ -258,5 +258,15 @@ "Notes": "Anmerkungen", "Allow replies.": "Antworten zulassen.", "Event": "Veranstaltung", - "Create an event": "Erstellen Sie ein Ereignis" + "Create an event": "Erstellen Sie ein Ereignis", + "Describe the event": "Beschreiben Sie das Ereignis", + "Start Date": "Anfangsdatum", + "End Date": "Endtermin", + "Categories": "Kategorien", + "This is a private event.": "Dies ist eine private Veranstaltung.", + "Allow anonymous participation.": "Anonyme Teilnahme zulassen.", + "Anyone can join": "Jeder kann mitmachen", + "Apply to join": "Sich anmelden um teilzunehmen", + "Invitation only": "Nur Einladungen", + "Joining": "Beitritt" } diff --git a/translations/en.json b/translations/en.json index 955c3f67d..f06e079aa 100644 --- a/translations/en.json +++ b/translations/en.json @@ -258,5 +258,15 @@ "Notes": "Notes", "Allow replies.": "Allow replies.", "Event": "Event", - "Create an event": "Create an event" + "Create an event": "Create an event", + "Describe the event": "Describe the event", + "Start Date": "Start Date", + "End Date": "End Date", + "Categories": "Categories", + "This is a private event.": "This is a private event.", + "Allow anonymous participation.": "Allow anonymous participation.", + "Anyone can join": "Anyone can join", + "Apply to join": "Apply to join", + "Invitation only": "Invitation only", + "Joining": "Joining" } diff --git a/translations/es.json b/translations/es.json index c960362c2..992e086c4 100644 --- a/translations/es.json +++ b/translations/es.json @@ -258,5 +258,15 @@ "Notes": "Notas", "Allow replies.": "Permitir respuestas.", "Event": "Evento", - "Create an event": "Crea un evento" + "Create an event": "Crea un evento", + "Describe the event": "Describe el evento", + "Start Date": "Fecha de inicio", + "End Date": "Fecha final", + "Categories": "Categorías", + "This is a private event.": "Este es un evento privado.", + "Allow anonymous participation.": "Permitir la participación anónima.", + "Anyone can join": "Cualquiera puede unirse", + "Apply to join": "Aplica para unirte", + "Invitation only": "Sólo con Invitación", + "Joining": "Unión" } diff --git a/translations/fr.json b/translations/fr.json index 003cef88f..2b0595501 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -258,5 +258,15 @@ "Notes": "Remarques", "Allow replies.": "Autoriser les réponses.", "Event": "un événement", - "Create an event": "Créer un événement" + "Create an event": "Créer un événement", + "Describe the event": "Décrivez l'événement", + "Start Date": "Date de début", + "End Date": "Date de fin", + "Categories": "Catégories", + "This is a private event.": "Ceci est un événement privé.", + "Allow anonymous participation.": "Autorisez la participation anonyme.", + "Anyone can join": "Tout le monde peut joindre", + "Apply to join": "Postuler pour rejoindre", + "Invitation only": "Invitation uniquement", + "Joining": "Joindre" } diff --git a/translations/ga.json b/translations/ga.json index 2e6981a58..012cd2573 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -258,5 +258,15 @@ "Notes": "Nótaí", "Allow replies.": "Ceadaigh freagraí.", "Event": "Imeacht", - "Create an event": "Cruthaigh imeacht" + "Create an event": "Cruthaigh imeacht", + "Describe the event": "Déan cur síos ar an ócáid", + "Start Date": "Dáta tosaigh", + "End Date": "Dáta deiridh", + "Categories": "Catagóirí", + "This is a private event.": "Is ócáid phríobháideach é seo.", + "Allow anonymous participation.": "Lig rannpháirtíocht gan ainm.", + "Anyone can join": "Is féidir le duine ar bith a bheith páirteach", + "Apply to join": "Déan iarratas ar bhallraíocht", + "Invitation only": "Cuireadh amháin", + "Joining": "Ag teacht le chéile" } diff --git a/translations/hi.json b/translations/hi.json index 85f5690a4..94f61656b 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -258,5 +258,15 @@ "Notes": "टिप्पणियाँ", "Allow replies.": "जवाब दें।", "Event": "प्रतिस्पर्धा", - "Create an event": "एक घटना बनाएँ" + "Create an event": "एक घटना बनाएँ", + "Describe the event": "घटना का वर्णन करें", + "Start Date": "आरंभ करने की तिथि", + "End Date": "अंतिम तिथि", + "Categories": "श्रेणियाँ", + "This is a private event.": "यह एक निजी कार्यक्रम है।", + "Allow anonymous participation.": "अनाम भागीदारी की अनुमति दें।", + "Anyone can join": "कोई भी शामिल हो सकता है", + "Apply to join": "जुड़ने के लिए आवेदन करें", + "Invitation only": "केवल आमंत्रण", + "Joining": "में शामिल होने से" } diff --git a/translations/it.json b/translations/it.json index 24c974117..a09cee381 100644 --- a/translations/it.json +++ b/translations/it.json @@ -258,5 +258,15 @@ "Notes": "Appunti", "Allow replies.": "Consenti risposte.", "Event": "Evento", - "Create an event": "Crea un evento" + "Create an event": "Crea un evento", + "Describe the event": "Descrivi l'evento", + "Start Date": "Data d'inizio", + "End Date": "Data di fine", + "Categories": "Categorie", + "This is a private event.": "Questo è un evento privato.", + "Allow anonymous participation.": "Consenti la partecipazione anonima.", + "Anyone can join": "Chiunque può partecipare", + "Apply to join": "Richiedi di partecipare", + "Invitation only": "Solo su invito", + "Joining": "Partecipare" } diff --git a/translations/ja.json b/translations/ja.json index f565dcef0..e20f14b75 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -258,5 +258,15 @@ "Notes": "ノート", "Allow replies.": "返信を許可します。", "Event": "イベント", - "Create an event": "イベントを作成する" + "Create an event": "イベントを作成する", + "Describe the event": "イベントについて説明する", + "Start Date": "開始日", + "End Date": "終了日", + "Categories": "カテゴリー", + "This is a private event.": "これはプライベートイベントです。", + "Allow anonymous participation.": "匿名参加を許可します。", + "Anyone can join": "誰でも参加できます", + "Apply to join": "参加を申し込む", + "Invitation only": "招待のみ", + "Joining": "接合" } diff --git a/translations/oc.json b/translations/oc.json index 264644983..7c9a8d627 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -254,5 +254,15 @@ "Notes": "Notes", "Allow replies.": "Allow replies.", "Event": "Event", - "Create an event": "Create an event" + "Create an event": "Create an event", + "Describe the event": "Describe the event", + "Start Date": "Start Date", + "End Date": "End Date", + "Categories": "Categories", + "This is a private event.": "This is a private event.", + "Allow anonymous participation.": "Allow anonymous participation.", + "Anyone can join": "Anyone can join", + "Apply to join": "Apply to join", + "Invitation only": "Invitation only", + "Joining": "Joining" } diff --git a/translations/pt.json b/translations/pt.json index f7cd8b5dd..605d44783 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -258,5 +258,15 @@ "Notes": "Notas", "Allow replies.": "Permitir respostas.", "Event": "Evento", - "Create an event": "Crie um evento" + "Create an event": "Crie um evento", + "Describe the event": "Descreva o evento", + "Start Date": "Data de início", + "End Date": "Data final", + "Categories": "Categorias", + "This is a private event.": "Este é um evento privado.", + "Allow anonymous participation.": "Permita a participação anônima.", + "Anyone can join": "Qualquer um pode participar", + "Apply to join": "Aplicar para participar", + "Invitation only": "Somente para convidados", + "Joining": "Juntando" } diff --git a/translations/ru.json b/translations/ru.json index e48c01d39..f85f966f2 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -258,5 +258,15 @@ "Notes": "Ноты", "Allow replies.": "Разрешить ответы.", "Event": "Мероприятие", - "Create an event": "Создать мероприятие" + "Create an event": "Создать мероприятие", + "Describe the event": "Опишите событие", + "Start Date": "Дата начала", + "End Date": "Дата окончания", + "Categories": "Категории", + "This is a private event.": "Это частное мероприятие.", + "Allow anonymous participation.": "Разрешить анонимное участие.", + "Anyone can join": "Каждый может присоединиться", + "Apply to join": "Подать заявку на присоединение", + "Invitation only": "Только приглашение", + "Joining": "Присоединение" } diff --git a/translations/zh.json b/translations/zh.json index 18bdeba70..c3319d02c 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -258,5 +258,15 @@ "Notes": "笔记", "Allow replies.": "允许回复。", "Event": "事件", - "Create an event": "建立活动" + "Create an event": "建立活动", + "Describe the event": "描述事件", + "Start Date": "开始日期", + "End Date": "结束日期", + "Categories": "分类目录", + "This is a private event.": "这是私人活动。", + "Allow anonymous participation.": "允许匿名参与。", + "Anyone can join": "任何人都可以加入", + "Apply to join": "申请加入", + "Invitation only": "仅邀请", + "Joining": "加盟" } diff --git a/webinterface.py b/webinterface.py index 465a4abfb..abb5fee78 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1920,6 +1920,7 @@ def htmlNewPost(mediaInstance: bool, translate: {}, pathBase = path.replace('/newreport', '').replace('/newpost', '') pathBase = pathBase.replace('/newblog', '').replace('/newshare', '') pathBase = pathBase.replace('/newunlisted', '') + pathBase = pathBase.replace('/newevent', '') pathBase = pathBase.replace('/newreminder', '') pathBase = pathBase.replace('/newfollowers', '').replace('/newdm', '') @@ -1969,6 +1970,12 @@ def htmlNewPost(mediaInstance: bool, translate: {}, scopeIcon = 'scope_reminder.png' scopeDescription = translate['Reminder'] endpoint = 'newreminder' + elif path.endswith('/newevent'): + scopeIcon = 'scope_event.png' + scopeDescription = translate['Event'] + endpoint = 'newevent' + placeholderSubject = translate['Title'] + '...' + placeholderMessage = translate['Describe the event'] + '...' elif path.endswith('/newreport'): scopeIcon = 'scope_report.png' scopeDescription = translate['Report'] @@ -2029,12 +2036,25 @@ def htmlNewPost(mediaInstance: bool, translate: {}, endpoint != 'newquestion': dateAndLocation = '
    ' - dateAndLocation += \ - '

    \n' + if endpoint == 'newevent': + # Event posts don't allow replies - they're just an announcement. + # They also have a few more checkboxes + dateAndLocation += \ + '

    \n' + dateAndLocation += \ + '

    ' + \ + '

    \n' + else: + dateAndLocation += \ + '

    \n' - if not inReplyTo: + if not inReplyTo and endpoint != 'newevent': dateAndLocation += \ '

    \n' - dateAndLocation += '\n' - dateAndLocation += '\n' - dateAndLocation += '

    \n' + + if endpoint != 'newevent': + # select a date and time for this post + dateAndLocation += '\n' + dateAndLocation += '\n' + dateAndLocation += '

    \n' + else: + # select start time for the event + dateAndLocation += '\n' + dateAndLocation += '\n' + dateAndLocation += '

    \n' + # select end time for the event + dateAndLocation += \ + '

    \n' + dateAndLocation += '\n' + dateAndLocation += '\n' + dateAndLocation += '

    \n' + dateAndLocation += '
    \n' dateAndLocation += '
    \n' dateAndLocation += '
    \n' dateAndLocation += '\n' + if endpoint == 'newevent': + dateAndLocation += '
    \n' + dateAndLocation += '\n' dateAndLocation += '
    \n' newPostForm = htmlHeader(cssFilename, newPostCSS) From 23e8719445b57371d095131c0e0a1753b35e6496 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 14:56:55 +0100 Subject: [PATCH 18/84] Field name --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index abb5fee78..c8479ee62 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2092,7 +2092,7 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['End Date'] + ': \n' dateAndLocation += '\n' dateAndLocation += '

    \n' From 7a11e6ae2df6e15eb9aa3a9fde13cca50d88f589 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 14:58:25 +0100 Subject: [PATCH 19/84] Field name --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index c8479ee62..eb52ea255 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2092,7 +2092,7 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['End Date'] + ': \n' dateAndLocation += '\n' dateAndLocation += '

    \n' From 7c0dca588dd2ce9c24d2003018d6d96356097dc5 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 16:07:44 +0100 Subject: [PATCH 20/84] Alignment --- webinterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webinterface.py b/webinterface.py index eb52ea255..d0f85f87e 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2082,10 +2082,10 @@ def htmlNewPost(mediaInstance: bool, translate: {}, dateAndLocation += '

    \n' + '
    \n' # select end time for the event dateAndLocation += \ - '

    \n' dateAndLocation += '

    \n' # select end time for the event dateAndLocation += \ - '

    \n' dateAndLocation += '

    \n' # select end time for the event dateAndLocation += \ - '

    \n' dateAndLocation += '

    \n' + '\n' dateAndLocation += '
  • \n' dateAndLocation += '
    \n' From 98e8528eed9116c61bf225854daa9eeb9e66cfce Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 17:22:50 +0100 Subject: [PATCH 23/84] Select joining type --- webinterface.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/webinterface.py b/webinterface.py index 401da36e9..944964018 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2075,6 +2075,21 @@ def htmlNewPost(mediaInstance: bool, translate: {}, dateAndLocation += \ '

    \n' else: + # event joining options + dateAndLocation += '\n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '\n' + dateAndLocation += '\n' # select start time for the event dateAndLocation += '\n' From 7515606abee9a76100edba5c288dcf72f6d9b0a7 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 17:40:45 +0100 Subject: [PATCH 24/84] add divs --- webinterface.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webinterface.py b/webinterface.py index 944964018..29120306f 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2076,6 +2076,7 @@ def htmlNewPost(mediaInstance: bool, translate: {}, '

    \n' else: # event joining options + dateAndLocation += '
    \n' dateAndLocation += '\n' dateAndLocation += '\n' dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '
    \n' # select start time for the event dateAndLocation += '\n' @@ -2110,6 +2113,7 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['Time'] + ':' dateAndLocation += \ '\n' + dateAndLocation += '
    \n' dateAndLocation += '
    \n' dateAndLocation += '
    \n' From adaa354fab1d46eedc6b04e9276b7e91fc4c45fb Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 17:43:25 +0100 Subject: [PATCH 25/84] Calendar icon --- webinterface.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/webinterface.py b/webinterface.py index 29120306f..2a95f9587 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2060,12 +2060,11 @@ def htmlNewPost(mediaInstance: bool, translate: {}, 'name="schedulePost">

    \n' - dateAndLocation += \ - '

    \n' - if endpoint != 'newevent': + dateAndLocation += \ + '

    \n' # select a date and time for this post dateAndLocation += '\n' @@ -2093,6 +2092,10 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['Invitation only'] + '\n' dateAndLocation += '

    \n' dateAndLocation += '
    \n' + dateAndLocation += \ + '

    \n' # select start time for the event dateAndLocation += '\n' From 7b1f423c92a0d817967f69bb776543788a837a8a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 17:44:30 +0100 Subject: [PATCH 26/84] Newline --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 2a95f9587..60a023574 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2077,7 +2077,7 @@ def htmlNewPost(mediaInstance: bool, translate: {}, # event joining options dateAndLocation += '

    \n' dateAndLocation += '\n' + translate['Joining'] + '
    \n' dateAndLocation += '\n' dateAndLocation += '
    ' + dateAndLocation = '
    \n' if endpoint == 'newevent': + # event joining options + dateAndLocation += '
    \n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '\n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '
    \n' # Event posts don't allow replies - they're just an announcement. # They also have a few more checkboxes dateAndLocation += \ @@ -2074,23 +2091,6 @@ def htmlNewPost(mediaInstance: bool, translate: {}, dateAndLocation += \ '

    \n' else: - # event joining options - dateAndLocation += '
    \n' - dateAndLocation += '
    \n' - dateAndLocation += '\n' - dateAndLocation += '
    \n' - dateAndLocation += '\n' - dateAndLocation += '
    \n' - dateAndLocation += '\n' - dateAndLocation += '\n' - dateAndLocation += '
    \n' dateAndLocation += '
    \n' dateAndLocation += \ '

    Date: Sat, 22 Aug 2020 19:02:43 +0100 Subject: [PATCH 29/84] Event status --- daemon.py | 6 +++++- translations/ar.json | 6 +++++- translations/ca.json | 6 +++++- translations/cy.json | 6 +++++- translations/de.json | 6 +++++- translations/en.json | 6 +++++- translations/es.json | 6 +++++- translations/fr.json | 6 +++++- translations/ga.json | 6 +++++- translations/hi.json | 6 +++++- translations/it.json | 6 +++++- translations/ja.json | 6 +++++- translations/oc.json | 6 +++++- translations/pt.json | 6 +++++- translations/ru.json | 6 +++++- translations/zh.json | 6 +++++- webinterface.py | 19 ++++++++++++++++++- 17 files changed, 98 insertions(+), 17 deletions(-) diff --git a/daemon.py b/daemon.py index f6330eadf..48ac60576 100644 --- a/daemon.py +++ b/daemon.py @@ -5740,6 +5740,10 @@ class PubServer(BaseHTTPRequestHandler): anonymousParticipationEnabled = False else: anonymousParticipationEnabled = True + maximumAttendeeCapacity = 999999 + if fields.get('maximumAttendeeCapacity'): + maximumAttendeeCapacity = \ + fields['maximumAttendeeCapacity'] messageJson = \ createEventPost(self.server.baseDir, @@ -5762,7 +5766,7 @@ class PubServer(BaseHTTPRequestHandler): fields['joinMode'], fields['endDate'], fields['endTime'], - fields['maximumAttendeeCapacity'], + maximumAttendeeCapacity, fields['repliesModerationOption'], anonymousParticipationEnabled, fields['eventStatus']) diff --git a/translations/ar.json b/translations/ar.json index 0179e241b..628014ba3 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -268,5 +268,9 @@ "Anyone can join": "يمكن لأي شخص الانضمام", "Apply to join": "تقديم طلب للانضمام", "Invitation only": "المدعوون فقط", - "Joining": "انضمام" + "Joining": "انضمام", + "Status of the event": "حالة الحدث", + "Tentative": "مؤقت", + "Confirmed": "تم تأكيد", + "Cancelled": "ألغيت" } diff --git a/translations/ca.json b/translations/ca.json index 4e552fabc..4b84f77b1 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -268,5 +268,9 @@ "Anyone can join": "Qualsevol persona s’hi pot apuntar", "Apply to join": "Sol·liciteu participar", "Invitation only": "Només invitació", - "Joining": "Unir-se" + "Joining": "Unir-se", + "Status of the event": "Estat de l’esdeveniment", + "Tentative": "Temptatiu", + "Confirmed": "Confirmat", + "Cancelled": "Cancel·lat" } diff --git a/translations/cy.json b/translations/cy.json index c1a47db05..6eb4897ec 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -268,5 +268,9 @@ "Anyone can join": "Gall unrhyw un ymuno", "Apply to join": "Gwnewch gais i ymuno", "Invitation only": "Gwahoddiad yn unig", - "Joining": "Yn ymuno" + "Joining": "Yn ymuno", + "Status of the event": "Statws y digwyddiad", + "Tentative": "Cynhyrfus", + "Confirmed": "Cadarnhawyd", + "Cancelled": "Wedi'i ganslo" } diff --git a/translations/de.json b/translations/de.json index d3a67ebbb..d0e31c0cd 100644 --- a/translations/de.json +++ b/translations/de.json @@ -268,5 +268,9 @@ "Anyone can join": "Jeder kann mitmachen", "Apply to join": "Sich anmelden um teilzunehmen", "Invitation only": "Nur Einladungen", - "Joining": "Beitritt" + "Joining": "Beitritt", + "Status of the event": "Status des Ereignisses", + "Tentative": "Vorsichtig", + "Confirmed": "Bestätigt", + "Cancelled": "Abgesagt" } diff --git a/translations/en.json b/translations/en.json index f06e079aa..3221e25df 100644 --- a/translations/en.json +++ b/translations/en.json @@ -268,5 +268,9 @@ "Anyone can join": "Anyone can join", "Apply to join": "Apply to join", "Invitation only": "Invitation only", - "Joining": "Joining" + "Joining": "Joining", + "Status of the event": "Status of the event", + "Tentative": "Tentative", + "Confirmed": "Confirmed", + "Cancelled": "Cancelled" } diff --git a/translations/es.json b/translations/es.json index 992e086c4..c9b172c53 100644 --- a/translations/es.json +++ b/translations/es.json @@ -268,5 +268,9 @@ "Anyone can join": "Cualquiera puede unirse", "Apply to join": "Aplica para unirte", "Invitation only": "Sólo con Invitación", - "Joining": "Unión" + "Joining": "Unión", + "Status of the event": "Estado del evento", + "Tentative": "Tentativa", + "Confirmed": "Confirmada", + "Cancelled": "Cancelada" } diff --git a/translations/fr.json b/translations/fr.json index 2b0595501..540575307 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -268,5 +268,9 @@ "Anyone can join": "Tout le monde peut joindre", "Apply to join": "Postuler pour rejoindre", "Invitation only": "Invitation uniquement", - "Joining": "Joindre" + "Joining": "Joindre", + "Status of the event": "Statut de l'événement", + "Tentative": "Provisoire", + "Confirmed": "Confirmé", + "Cancelled": "Annulé" } diff --git a/translations/ga.json b/translations/ga.json index 012cd2573..f88afc217 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -268,5 +268,9 @@ "Anyone can join": "Is féidir le duine ar bith a bheith páirteach", "Apply to join": "Déan iarratas ar bhallraíocht", "Invitation only": "Cuireadh amháin", - "Joining": "Ag teacht le chéile" + "Joining": "Ag teacht le chéile", + "Status of the event": "Stádas na hócáide", + "Tentative": "Sealadach", + "Confirmed": "Deimhnithe", + "Cancelled": "Cealaithe" } diff --git a/translations/hi.json b/translations/hi.json index 94f61656b..63b88ed44 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -268,5 +268,9 @@ "Anyone can join": "कोई भी शामिल हो सकता है", "Apply to join": "जुड़ने के लिए आवेदन करें", "Invitation only": "केवल आमंत्रण", - "Joining": "में शामिल होने से" + "Joining": "में शामिल होने से", + "Status of the event": "घटना की स्थिति", + "Tentative": "जांच का", + "Confirmed": "की पुष्टि", + "Cancelled": "रद्द" } diff --git a/translations/it.json b/translations/it.json index a09cee381..a1588f7bb 100644 --- a/translations/it.json +++ b/translations/it.json @@ -268,5 +268,9 @@ "Anyone can join": "Chiunque può partecipare", "Apply to join": "Richiedi di partecipare", "Invitation only": "Solo su invito", - "Joining": "Partecipare" + "Joining": "Partecipare", + "Status of the event": "Stato dell'evento", + "Tentative": "Tentativa", + "Confirmed": "Confermata", + "Cancelled": "Annullata" } diff --git a/translations/ja.json b/translations/ja.json index e20f14b75..617c3cd5f 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -268,5 +268,9 @@ "Anyone can join": "誰でも参加できます", "Apply to join": "参加を申し込む", "Invitation only": "招待のみ", - "Joining": "接合" + "Joining": "接合", + "Status of the event": "イベントのステータス", + "Tentative": "暫定の", + "Confirmed": "確認済み", + "Cancelled": "キャンセル" } diff --git a/translations/oc.json b/translations/oc.json index 7c9a8d627..a7ed435c2 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -264,5 +264,9 @@ "Anyone can join": "Anyone can join", "Apply to join": "Apply to join", "Invitation only": "Invitation only", - "Joining": "Joining" + "Joining": "Joining", + "Status of the event": "Status of the event", + "Tentative": "Tentative", + "Confirmed": "Confirmed", + "Cancelled": "Cancelled" } diff --git a/translations/pt.json b/translations/pt.json index 605d44783..66d29b8d2 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -268,5 +268,9 @@ "Anyone can join": "Qualquer um pode participar", "Apply to join": "Aplicar para participar", "Invitation only": "Somente para convidados", - "Joining": "Juntando" + "Joining": "Juntando", + "Status of the event": "Status do evento", + "Tentative": "Provisório", + "Confirmed": "Confirmada", + "Cancelled": "Cancelada" } diff --git a/translations/ru.json b/translations/ru.json index f85f966f2..c5c776e7f 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -268,5 +268,9 @@ "Anyone can join": "Каждый может присоединиться", "Apply to join": "Подать заявку на присоединение", "Invitation only": "Только приглашение", - "Joining": "Присоединение" + "Joining": "Присоединение", + "Status of the event": "Статус мероприятия", + "Tentative": "Предварительно", + "Confirmed": "Подтверждено", + "Cancelled": "Отменено" } diff --git a/translations/zh.json b/translations/zh.json index c3319d02c..459ae060c 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -268,5 +268,9 @@ "Anyone can join": "任何人都可以加入", "Apply to join": "申请加入", "Invitation only": "仅邀请", - "Joining": "加盟" + "Joining": "加盟", + "Status of the event": "活动状态", + "Tentative": "暂定", + "Confirmed": "已确认", + "Cancelled": "取消" } diff --git a/webinterface.py b/webinterface.py index 75c0d7180..f5821ba3d 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2037,9 +2037,26 @@ def htmlNewPost(mediaInstance: bool, translate: {}, dateAndLocation = '

    \n' if endpoint == 'newevent': + # event status + dateAndLocation += '
    \n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '
    \n' + dateAndLocation += '
    \n' # event joining options dateAndLocation += '
    \n' + translate['Joining'] + ':
    \n' dateAndLocation += '\n' dateAndLocation += '

    \n' else: - dateAndLocation += '
    \n' + dateAndLocation += '
    \n' + dateAndLocation += '
    \n' dateAndLocation += \ '

    ' + \ translate['Status of the event'] + ':
    \n' dateAndLocation += '\n' + 'name="eventStatus" value="tentative">\n' dateAndLocation += '
    \n' dateAndLocation += '\n' + 'name="eventStatus" value="confirmed" checked>\n' dateAndLocation += '
    \n' dateAndLocation += ' Date: Sat, 22 Aug 2020 20:14:41 +0100 Subject: [PATCH 32/84] Extraneous div --- webinterface.py | 1 - 1 file changed, 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 14a864dee..a96751ea2 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2134,7 +2134,6 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['Time'] + ':' dateAndLocation += \ '\n' - dateAndLocation += '

    \n' dateAndLocation += '
    \n' dateAndLocation += '
    \n' From 1eac0134fc307fbea19bab15f6ecdeeeaa2cf2b2 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 20:23:48 +0100 Subject: [PATCH 33/84] Different image description for events --- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- webinterface.py | 11 ++++++++--- 16 files changed, 38 insertions(+), 18 deletions(-) diff --git a/translations/ar.json b/translations/ar.json index 628014ba3..07cdb36bb 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -272,5 +272,6 @@ "Status of the event": "حالة الحدث", "Tentative": "مؤقت", "Confirmed": "تم تأكيد", - "Cancelled": "ألغيت" + "Cancelled": "ألغيت", + "Event banner image description": "وصف صورة شعار الحدث" } diff --git a/translations/ca.json b/translations/ca.json index 4b84f77b1..e44b776f4 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -272,5 +272,6 @@ "Status of the event": "Estat de l’esdeveniment", "Tentative": "Temptatiu", "Confirmed": "Confirmat", - "Cancelled": "Cancel·lat" + "Cancelled": "Cancel·lat", + "Event banner image description": "Descripció de la imatge del banner de l’esdeveniment" } diff --git a/translations/cy.json b/translations/cy.json index 6eb4897ec..391b7c3d6 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -272,5 +272,6 @@ "Status of the event": "Statws y digwyddiad", "Tentative": "Cynhyrfus", "Confirmed": "Cadarnhawyd", - "Cancelled": "Wedi'i ganslo" + "Cancelled": "Wedi'i ganslo", + "Event banner image description": "Disgrifiad delwedd baner y digwyddiad" } diff --git a/translations/de.json b/translations/de.json index d0e31c0cd..d92efb6de 100644 --- a/translations/de.json +++ b/translations/de.json @@ -272,5 +272,6 @@ "Status of the event": "Status des Ereignisses", "Tentative": "Vorsichtig", "Confirmed": "Bestätigt", - "Cancelled": "Abgesagt" + "Cancelled": "Abgesagt", + "Event banner image description": "Beschreibung des Ereignisbannerbildes" } diff --git a/translations/en.json b/translations/en.json index 3221e25df..08e31dd23 100644 --- a/translations/en.json +++ b/translations/en.json @@ -272,5 +272,6 @@ "Status of the event": "Status of the event", "Tentative": "Tentative", "Confirmed": "Confirmed", - "Cancelled": "Cancelled" + "Cancelled": "Cancelled", + "Event banner image description": "Event banner image description" } diff --git a/translations/es.json b/translations/es.json index c9b172c53..1f496bce5 100644 --- a/translations/es.json +++ b/translations/es.json @@ -272,5 +272,6 @@ "Status of the event": "Estado del evento", "Tentative": "Tentativa", "Confirmed": "Confirmada", - "Cancelled": "Cancelada" + "Cancelled": "Cancelada", + "Event banner image description": "Descripción de la imagen del banner del evento" } diff --git a/translations/fr.json b/translations/fr.json index 540575307..ac340063f 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -272,5 +272,6 @@ "Status of the event": "Statut de l'événement", "Tentative": "Provisoire", "Confirmed": "Confirmé", - "Cancelled": "Annulé" + "Cancelled": "Annulé", + "Event banner image description": "Description de l'image de la bannière de l'événement" } diff --git a/translations/ga.json b/translations/ga.json index f88afc217..d20a52611 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -272,5 +272,6 @@ "Status of the event": "Stádas na hócáide", "Tentative": "Sealadach", "Confirmed": "Deimhnithe", - "Cancelled": "Cealaithe" + "Cancelled": "Cealaithe", + "Event banner image description": "Tuairisc íomhá meirge na hócáide" } diff --git a/translations/hi.json b/translations/hi.json index 63b88ed44..13278acf7 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -272,5 +272,6 @@ "Status of the event": "घटना की स्थिति", "Tentative": "जांच का", "Confirmed": "की पुष्टि", - "Cancelled": "रद्द" + "Cancelled": "रद्द", + "Event banner image description": "घटना बैनर छवि विवरण" } diff --git a/translations/it.json b/translations/it.json index a1588f7bb..f54599f1e 100644 --- a/translations/it.json +++ b/translations/it.json @@ -272,5 +272,6 @@ "Status of the event": "Stato dell'evento", "Tentative": "Tentativa", "Confirmed": "Confermata", - "Cancelled": "Annullata" + "Cancelled": "Annullata", + "Event banner image description": "Descrizione dell'immagine del banner dell'evento" } diff --git a/translations/ja.json b/translations/ja.json index 617c3cd5f..f398be598 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -272,5 +272,6 @@ "Status of the event": "イベントのステータス", "Tentative": "暫定の", "Confirmed": "確認済み", - "Cancelled": "キャンセル" + "Cancelled": "キャンセル", + "Event banner image description": "イベントバナー画像の説明" } diff --git a/translations/oc.json b/translations/oc.json index a7ed435c2..d12a7b0df 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -268,5 +268,6 @@ "Status of the event": "Status of the event", "Tentative": "Tentative", "Confirmed": "Confirmed", - "Cancelled": "Cancelled" + "Cancelled": "Cancelled", + "Event banner image description": "Event banner image description" } diff --git a/translations/pt.json b/translations/pt.json index 66d29b8d2..9fa829244 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -272,5 +272,6 @@ "Status of the event": "Status do evento", "Tentative": "Provisório", "Confirmed": "Confirmada", - "Cancelled": "Cancelada" + "Cancelled": "Cancelada", + "Event banner image description": "Descrição da imagem do banner do evento" } diff --git a/translations/ru.json b/translations/ru.json index c5c776e7f..6143f4540 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -272,5 +272,6 @@ "Status of the event": "Статус мероприятия", "Tentative": "Предварительно", "Confirmed": "Подтверждено", - "Cancelled": "Отменено" + "Cancelled": "Отменено", + "Event banner image description": "Описание изображения баннера мероприятия" } diff --git a/translations/zh.json b/translations/zh.json index 459ae060c..83ed3bdc3 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -272,5 +272,6 @@ "Status of the event": "活动状态", "Tentative": "暂定", "Confirmed": "已确认", - "Cancelled": "取消" + "Cancelled": "取消", + "Event banner image description": "活动横幅图片说明" } diff --git a/webinterface.py b/webinterface.py index a96751ea2..a00de8a1e 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1925,9 +1925,14 @@ def htmlNewPost(mediaInstance: bool, translate: {}, pathBase = pathBase.replace('/newfollowers', '').replace('/newdm', '') newPostImageSection = '
    ' - newPostImageSection += \ - ' \n' + if endpoint != 'newevent': + newPostImageSection += \ + ' \n' + else: + newPostImageSection += \ + ' \n' newPostImageSection += \ ' \n' newPostImageSection += \ From 69531c89d9936a17f645303ffd4656d514cd3cd4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 20:26:55 +0100 Subject: [PATCH 34/84] Check path --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index a00de8a1e..3b795b6f3 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1925,7 +1925,7 @@ def htmlNewPost(mediaInstance: bool, translate: {}, pathBase = pathBase.replace('/newfollowers', '').replace('/newdm', '') newPostImageSection = '
    ' - if endpoint != 'newevent': + if not path.endswith('/newevent'): newPostImageSection += \ ' \n' From 0a96dfe03c3a9608a6cc7a3398f89cac02507dfd Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 20:40:21 +0100 Subject: [PATCH 35/84] Extra label --- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- webinterface.py | 20 +++++++++++++++----- 16 files changed, 45 insertions(+), 20 deletions(-) diff --git a/translations/ar.json b/translations/ar.json index 07cdb36bb..e694f1950 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -273,5 +273,6 @@ "Tentative": "مؤقت", "Confirmed": "تم تأكيد", "Cancelled": "ألغيت", - "Event banner image description": "وصف صورة شعار الحدث" + "Event banner image description": "وصف صورة شعار الحدث", + "Banner image": "صورة بانر" } diff --git a/translations/ca.json b/translations/ca.json index e44b776f4..9012777de 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -273,5 +273,6 @@ "Tentative": "Temptatiu", "Confirmed": "Confirmat", "Cancelled": "Cancel·lat", - "Event banner image description": "Descripció de la imatge del banner de l’esdeveniment" + "Event banner image description": "Descripció de la imatge del banner de l’esdeveniment", + "Banner image": "Imatge de pancarta" } diff --git a/translations/cy.json b/translations/cy.json index 391b7c3d6..e80b5f898 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -273,5 +273,6 @@ "Tentative": "Cynhyrfus", "Confirmed": "Cadarnhawyd", "Cancelled": "Wedi'i ganslo", - "Event banner image description": "Disgrifiad delwedd baner y digwyddiad" + "Event banner image description": "Disgrifiad delwedd baner y digwyddiad", + "Banner image": "Delwedd baner" } diff --git a/translations/de.json b/translations/de.json index d92efb6de..f11b3d48b 100644 --- a/translations/de.json +++ b/translations/de.json @@ -273,5 +273,6 @@ "Tentative": "Vorsichtig", "Confirmed": "Bestätigt", "Cancelled": "Abgesagt", - "Event banner image description": "Beschreibung des Ereignisbannerbildes" + "Event banner image description": "Beschreibung des Ereignisbannerbildes", + "Banner image": "Bannerbild" } diff --git a/translations/en.json b/translations/en.json index 08e31dd23..4b4c6e70a 100644 --- a/translations/en.json +++ b/translations/en.json @@ -273,5 +273,6 @@ "Tentative": "Tentative", "Confirmed": "Confirmed", "Cancelled": "Cancelled", - "Event banner image description": "Event banner image description" + "Event banner image description": "Event banner image description", + "Banner image": "Banner image" } diff --git a/translations/es.json b/translations/es.json index 1f496bce5..23565aa64 100644 --- a/translations/es.json +++ b/translations/es.json @@ -273,5 +273,6 @@ "Tentative": "Tentativa", "Confirmed": "Confirmada", "Cancelled": "Cancelada", - "Event banner image description": "Descripción de la imagen del banner del evento" + "Event banner image description": "Descripción de la imagen del banner del evento", + "Banner image": "Imagen de banner" } diff --git a/translations/fr.json b/translations/fr.json index ac340063f..7627a4d7c 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -273,5 +273,6 @@ "Tentative": "Provisoire", "Confirmed": "Confirmé", "Cancelled": "Annulé", - "Event banner image description": "Description de l'image de la bannière de l'événement" + "Event banner image description": "Description de l'image de la bannière de l'événement", + "Banner image": "Image de bannière" } diff --git a/translations/ga.json b/translations/ga.json index d20a52611..b13b1a323 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -273,5 +273,6 @@ "Tentative": "Sealadach", "Confirmed": "Deimhnithe", "Cancelled": "Cealaithe", - "Event banner image description": "Tuairisc íomhá meirge na hócáide" + "Event banner image description": "Tuairisc íomhá meirge na hócáide", + "Banner image": "Íomhá meirge" } diff --git a/translations/hi.json b/translations/hi.json index 13278acf7..1a96b7d8d 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -273,5 +273,6 @@ "Tentative": "जांच का", "Confirmed": "की पुष्टि", "Cancelled": "रद्द", - "Event banner image description": "घटना बैनर छवि विवरण" + "Event banner image description": "घटना बैनर छवि विवरण", + "Banner image": "बैनर की छवि" } diff --git a/translations/it.json b/translations/it.json index f54599f1e..86193b7e8 100644 --- a/translations/it.json +++ b/translations/it.json @@ -273,5 +273,6 @@ "Tentative": "Tentativa", "Confirmed": "Confermata", "Cancelled": "Annullata", - "Event banner image description": "Descrizione dell'immagine del banner dell'evento" + "Event banner image description": "Descrizione dell'immagine del banner dell'evento", + "Banner image": "Immagine banner" } diff --git a/translations/ja.json b/translations/ja.json index f398be598..084d91a0d 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -273,5 +273,6 @@ "Tentative": "暫定の", "Confirmed": "確認済み", "Cancelled": "キャンセル", - "Event banner image description": "イベントバナー画像の説明" + "Event banner image description": "イベントバナー画像の説明", + "Banner image": "バナー画像" } diff --git a/translations/oc.json b/translations/oc.json index d12a7b0df..73237fed1 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -269,5 +269,6 @@ "Tentative": "Tentative", "Confirmed": "Confirmed", "Cancelled": "Cancelled", - "Event banner image description": "Event banner image description" + "Event banner image description": "Event banner image description", + "Banner image": "Banner image" } diff --git a/translations/pt.json b/translations/pt.json index 9fa829244..44018ee11 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -273,5 +273,6 @@ "Tentative": "Provisório", "Confirmed": "Confirmada", "Cancelled": "Cancelada", - "Event banner image description": "Descrição da imagem do banner do evento" + "Event banner image description": "Descrição da imagem do banner do evento", + "Banner image": "Imagem de banner" } diff --git a/translations/ru.json b/translations/ru.json index 6143f4540..bfbb8f6cf 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -273,5 +273,6 @@ "Tentative": "Предварительно", "Confirmed": "Подтверждено", "Cancelled": "Отменено", - "Event banner image description": "Описание изображения баннера мероприятия" + "Event banner image description": "Описание изображения баннера мероприятия", + "Banner image": "Изображение баннера" } diff --git a/translations/zh.json b/translations/zh.json index 83ed3bdc3..bfeeb057f 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -273,5 +273,6 @@ "Tentative": "暂定", "Confirmed": "已确认", "Cancelled": "取消", - "Event banner image description": "活动横幅图片说明" + "Event banner image description": "活动横幅图片说明", + "Banner image": "横幅图片" } diff --git a/webinterface.py b/webinterface.py index 3b795b6f3..a891ab36a 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1935,11 +1935,21 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['Event banner image description'] + '\n' newPostImageSection += \ ' \n' - newPostImageSection += \ - ' \n' + + if path.endswith('/newevent'): + newPostImageSection += \ + ' \n' + newPostImageSection += \ + ' \n' + else: + newPostImageSection += \ + ' \n' newPostImageSection += '
    \n' scopeIcon = 'scope_public.png' From f1546a245827efb2dc4886a96929d37cd3bd8fe5 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 22:04:50 +0100 Subject: [PATCH 36/84] Maximum attendees --- daemon.py | 2 +- epicyon-profile.css | 20 ++++++++++++++++++++ translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- webinterface.py | 8 ++++++++ 18 files changed, 59 insertions(+), 16 deletions(-) diff --git a/daemon.py b/daemon.py index 48ac60576..02d455d77 100644 --- a/daemon.py +++ b/daemon.py @@ -5743,7 +5743,7 @@ class PubServer(BaseHTTPRequestHandler): maximumAttendeeCapacity = 999999 if fields.get('maximumAttendeeCapacity'): maximumAttendeeCapacity = \ - fields['maximumAttendeeCapacity'] + int(fields['maximumAttendeeCapacity']) messageJson = \ createEventPost(self.server.baseDir, diff --git a/epicyon-profile.css b/epicyon-profile.css index 2a36b38dc..d849aaea2 100644 --- a/epicyon-profile.css +++ b/epicyon-profile.css @@ -1279,6 +1279,16 @@ aside .toggle-inside li { padding: 10px; margin: 20px 30px; } + input[type=number] + { + -ms-transform: scale(2); + -moz-transform: scale(2); + -webkit-transform: scale(2); + -o-transform: scale(2); + transform: scale(2); + padding: 10px; + margin: 20px 30px; + } } @media screen and (min-width: 2200px) { @@ -1709,4 +1719,14 @@ aside .toggle-inside li { padding: 20px; margin: 30px 40px; } + input[type=number] + { + -ms-transform: scale(4); + -moz-transform: scale(4); + -webkit-transform: scale(4); + -o-transform: scale(4); + transform: scale(4); + padding: 20px; + margin: 30px 40px; + } } diff --git a/translations/ar.json b/translations/ar.json index e694f1950..38afd2b62 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -274,5 +274,6 @@ "Confirmed": "تم تأكيد", "Cancelled": "ألغيت", "Event banner image description": "وصف صورة شعار الحدث", - "Banner image": "صورة بانر" + "Banner image": "صورة بانر", + "Maximum attendees": "الحد الأقصى للحضور" } diff --git a/translations/ca.json b/translations/ca.json index 9012777de..aea20a991 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -274,5 +274,6 @@ "Confirmed": "Confirmat", "Cancelled": "Cancel·lat", "Event banner image description": "Descripció de la imatge del banner de l’esdeveniment", - "Banner image": "Imatge de pancarta" + "Banner image": "Imatge de pancarta", + "Maximum attendees": "Màxim d’assistents" } diff --git a/translations/cy.json b/translations/cy.json index e80b5f898..e84bd97ed 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -274,5 +274,6 @@ "Confirmed": "Cadarnhawyd", "Cancelled": "Wedi'i ganslo", "Event banner image description": "Disgrifiad delwedd baner y digwyddiad", - "Banner image": "Delwedd baner" + "Banner image": "Delwedd baner", + "Maximum attendees": "Uchafswm mynychwyr" } diff --git a/translations/de.json b/translations/de.json index f11b3d48b..a3e57336b 100644 --- a/translations/de.json +++ b/translations/de.json @@ -274,5 +274,6 @@ "Confirmed": "Bestätigt", "Cancelled": "Abgesagt", "Event banner image description": "Beschreibung des Ereignisbannerbildes", - "Banner image": "Bannerbild" + "Banner image": "Bannerbild", + "Maximum attendees": "Maximale Teilnehmerzahl" } diff --git a/translations/en.json b/translations/en.json index 4b4c6e70a..e920d2d31 100644 --- a/translations/en.json +++ b/translations/en.json @@ -274,5 +274,6 @@ "Confirmed": "Confirmed", "Cancelled": "Cancelled", "Event banner image description": "Event banner image description", - "Banner image": "Banner image" + "Banner image": "Banner image", + "Maximum attendees": "Maximum attendees" } diff --git a/translations/es.json b/translations/es.json index 23565aa64..9e3096dc0 100644 --- a/translations/es.json +++ b/translations/es.json @@ -274,5 +274,6 @@ "Confirmed": "Confirmada", "Cancelled": "Cancelada", "Event banner image description": "Descripción de la imagen del banner del evento", - "Banner image": "Imagen de banner" + "Banner image": "Imagen de banner", + "Maximum attendees": "Asistentes máximos" } diff --git a/translations/fr.json b/translations/fr.json index 7627a4d7c..4bccf4389 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -274,5 +274,6 @@ "Confirmed": "Confirmé", "Cancelled": "Annulé", "Event banner image description": "Description de l'image de la bannière de l'événement", - "Banner image": "Image de bannière" + "Banner image": "Image de bannière", + "Maximum attendees": "Nombre maximum de participants" } diff --git a/translations/ga.json b/translations/ga.json index b13b1a323..35d6463fb 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -274,5 +274,6 @@ "Confirmed": "Deimhnithe", "Cancelled": "Cealaithe", "Event banner image description": "Tuairisc íomhá meirge na hócáide", - "Banner image": "Íomhá meirge" + "Banner image": "Íomhá meirge", + "Maximum attendees": "Uasmhéid freastail" } diff --git a/translations/hi.json b/translations/hi.json index 1a96b7d8d..4e1be87b8 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -274,5 +274,6 @@ "Confirmed": "की पुष्टि", "Cancelled": "रद्द", "Event banner image description": "घटना बैनर छवि विवरण", - "Banner image": "बैनर की छवि" + "Banner image": "बैनर की छवि", + "Maximum attendees": "अधिकतम उपस्थित" } diff --git a/translations/it.json b/translations/it.json index 86193b7e8..b73d94cc5 100644 --- a/translations/it.json +++ b/translations/it.json @@ -274,5 +274,6 @@ "Confirmed": "Confermata", "Cancelled": "Annullata", "Event banner image description": "Descrizione dell'immagine del banner dell'evento", - "Banner image": "Immagine banner" + "Banner image": "Immagine banner", + "Maximum attendees": "Numero massimo di partecipanti" } diff --git a/translations/ja.json b/translations/ja.json index 084d91a0d..01c8dc3b3 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -274,5 +274,6 @@ "Confirmed": "確認済み", "Cancelled": "キャンセル", "Event banner image description": "イベントバナー画像の説明", - "Banner image": "バナー画像" + "Banner image": "バナー画像", + "Maximum attendees": "最大参加者" } diff --git a/translations/oc.json b/translations/oc.json index 73237fed1..96371beca 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -270,5 +270,6 @@ "Confirmed": "Confirmed", "Cancelled": "Cancelled", "Event banner image description": "Event banner image description", - "Banner image": "Banner image" + "Banner image": "Banner image", + "Maximum attendees": "Maximum attendees" } diff --git a/translations/pt.json b/translations/pt.json index 44018ee11..cac28c6cc 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -274,5 +274,6 @@ "Confirmed": "Confirmada", "Cancelled": "Cancelada", "Event banner image description": "Descrição da imagem do banner do evento", - "Banner image": "Imagem de banner" + "Banner image": "Imagem de banner", + "Maximum attendees": "Máximo de participantes" } diff --git a/translations/ru.json b/translations/ru.json index bfbb8f6cf..2be8bfc5f 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -274,5 +274,6 @@ "Confirmed": "Подтверждено", "Cancelled": "Отменено", "Event banner image description": "Описание изображения баннера мероприятия", - "Banner image": "Изображение баннера" + "Banner image": "Изображение баннера", + "Maximum attendees": "Максимальное количество участников" } diff --git a/translations/zh.json b/translations/zh.json index bfeeb057f..0fb87c977 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -274,5 +274,6 @@ "Confirmed": "已确认", "Cancelled": "取消", "Event banner image description": "活动横幅图片说明", - "Banner image": "横幅图片" + "Banner image": "横幅图片", + "Maximum attendees": "参加人数上限" } diff --git a/webinterface.py b/webinterface.py index a891ab36a..906b36e4e 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2069,6 +2069,14 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['Cancelled'] + '
    \n' dateAndLocation += '
    \n' dateAndLocation += '
    \n' + # maximum attendees + dateAndLocation += '\n' + dateAndLocation += '\n' + dateAndLocation += '
    \n' + dateAndLocation += '
    \n' # event joining options dateAndLocation += '
    \n' From ab97b36e26b98c27f7062e87e5b4f00d64171e04 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 22:09:06 +0100 Subject: [PATCH 37/84] Default maximum attendees --- webinterface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 906b36e4e..fb51ad245 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2074,7 +2074,8 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['Maximum attendees'] + ':\n' dateAndLocation += '\n' + 'name="maximumAttendeeCapacity" min="1" max="999999" ' + \ + 'value="100">\n' dateAndLocation += '
    \n' dateAndLocation += '
    \n' # event joining options From 3ee733e5cc086f66b9288aabf92d4a37a7c09550 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 22 Aug 2020 22:12:46 +0100 Subject: [PATCH 38/84] Number style --- epicyon-profile.css | 4 ++-- webinterface.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/epicyon-profile.css b/epicyon-profile.css index d849aaea2..9b368d22c 100644 --- a/epicyon-profile.css +++ b/epicyon-profile.css @@ -1287,7 +1287,7 @@ aside .toggle-inside li { -o-transform: scale(2); transform: scale(2); padding: 10px; - margin: 20px 30px; + margin: 20px 60px; } } @@ -1727,6 +1727,6 @@ aside .toggle-inside li { -o-transform: scale(4); transform: scale(4); padding: 20px; - margin: 30px 40px; + margin: 30px 80px; } } diff --git a/webinterface.py b/webinterface.py index fb51ad245..4f6c8ce74 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2070,7 +2070,8 @@ def htmlNewPost(mediaInstance: bool, translate: {}, dateAndLocation += '
    \n' dateAndLocation += '
    \n' # maximum attendees - dateAndLocation += '\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 += \ ' Date: Sun, 23 Aug 2020 15:45:58 +0100 Subject: [PATCH 41/84] Unbreak the unit tests --- daemon.py | 22 ++++++++++++---------- inbox.py | 38 ++++++++++++++++++++++++-------------- posts.py | 26 ++++++++++++++------------ utils.py | 2 ++ 4 files changed, 52 insertions(+), 36 deletions(-) diff --git a/daemon.py b/daemon.py index dd45e6e21..1f94d4c30 100644 --- a/daemon.py +++ b/daemon.py @@ -8822,16 +8822,17 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 22) - if not inboxPermittedMessage(self.server.domain, - messageJson, - self.server.federationList): - if self.server.debug: - # https://www.youtube.com/watch?v=K3PrSj9XEu4 - print('DEBUG: Ah Ah Ah') - self.send_response(403) - self.end_headers() - self.server.POSTbusy = False - return + if not self.server.unitTest: + if not inboxPermittedMessage(self.server.domain, + messageJson, + self.server.federationList): + if self.server.debug: + # https://www.youtube.com/watch?v=K3PrSj9XEu4 + print('DEBUG: Ah Ah Ah') + self.send_response(403) + self.end_headers() + self.server.POSTbusy = False + return self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 23) @@ -8986,6 +8987,7 @@ def runDaemon(blogsInstance: bool, mediaInstance: bool, print('ERROR: HTTP server failed to start. ' + str(e)) return False + httpd.unitTest = unitTest httpd.YTReplacementDomain = YTReplacementDomain # This counter is used to update the list of blocked domains in memory. diff --git a/inbox.py b/inbox.py index 25113f727..56258023f 100644 --- a/inbox.py +++ b/inbox.py @@ -231,9 +231,11 @@ def getPersonPubKey(baseDir: str, session, personUrl: str, def inboxMessageHasParams(messageJson: {}) -> bool: """Checks whether an incoming message contains expected parameters """ - expectedParams = ['type', 'actor', 'object'] + expectedParams = ['actor', 'type', 'object'] for param in expectedParams: if not messageJson.get(param): + # print('inboxMessageHasParams: ' + + # param + ' ' + str(messageJson)) return False if not messageJson.get('to'): allowedWithoutToParam = ['Like', 'Follow', 'Request', @@ -249,6 +251,7 @@ def inboxPermittedMessage(domain: str, messageJson: {}, """ if not messageJson.get('actor'): return False + actor = messageJson['actor'] # always allow the local domain if domain in actor: @@ -2254,12 +2257,18 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, if validPostContent(baseDir, nickname, domain, postJsonObject, maxMentions, maxEmoji): + if postJsonObject.get('object'): + jsonObj = postJsonObject['object'] + if not isinstance(jsonObj, dict): + jsonObj = None + else: + jsonObj = postJsonObject # check for incoming git patches - if isinstance(postJsonObject['object'], dict): - if postJsonObject['object'].get('content') and \ - postJsonObject['object'].get('summary') and \ - postJsonObject['object'].get('attributedTo'): - attributedTo = postJsonObject['object']['attributedTo'] + if jsonObj: + if jsonObj.get('content') and \ + jsonObj.get('summary') and \ + jsonObj.get('attributedTo'): + attributedTo = jsonObj['attributedTo'] if isinstance(attributedTo, str): fromNickname = getNicknameFromActor(attributedTo) fromDomain, fromPort = getDomainFromActor(attributedTo) @@ -2267,17 +2276,17 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, if fromPort != 80 and fromPort != 443: fromDomain += ':' + str(fromPort) if receiveGitPatch(baseDir, nickname, domain, - postJsonObject['object']['type'], - postJsonObject['object']['summary'], - postJsonObject['object']['content'], + jsonObj['type'], + jsonObj['summary'], + jsonObj['content'], fromNickname, fromDomain): gitPatchNotify(baseDir, handle, - postJsonObject['object']['summary'], - postJsonObject['object']['content'], + jsonObj['summary'], + jsonObj['content'], fromNickname, fromDomain) - elif '[PATCH]' in postJsonObject['object']['content']: + elif '[PATCH]' in jsonObj['content']: print('WARN: git patch not accepted - ' + - postJsonObject['object']['summary']) + jsonObj['summary']) return False # replace YouTube links, so they get less tracking data @@ -2680,7 +2689,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, if accountMaxPostsPerDay > 0 or domainMaxPostsPerDay > 0: pprint(quotasDaily) - print('Obtaining public key for actor ' + queueJson['actor']) + if queueJson.get('actor'): + print('Obtaining public key for actor ' + queueJson['actor']) # Try a few times to obtain the public key pubKey = None diff --git a/posts.py b/posts.py index fbd956f91..44b3e90f7 100644 --- a/posts.py +++ b/posts.py @@ -622,6 +622,8 @@ def appendEventFields(newPost: {}, maximumAttendeeCapacity: int) -> {}: """Appends Mobilizon-type event fields to a post """ + return newPost + if not eventUUID: return newPost @@ -892,12 +894,12 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, attachMedia(baseDir, httpPrefix, domain, port, newPost['object'], attachImageFilename, mediaType, imageDescription, useBlurhash) - newPost = appendEventFields(newPost['object'], eventUUID, eventStatus, - anonymousParticipationEnabled, - repliesModerationOption, - category, joinMode, - eventDateStr, endDateStr, - location, maximumAttendeeCapacity) + #newPost = appendEventFields(newPost['object'], eventUUID, eventStatus, + # anonymousParticipationEnabled, + # repliesModerationOption, + # category, joinMode, + # eventDateStr, endDateStr, + # location, maximumAttendeeCapacity) else: idStr = \ httpPrefix + '://' + domain + '/users/' + nickname + \ @@ -939,12 +941,12 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, attachMedia(baseDir, httpPrefix, domain, port, newPost, attachImageFilename, mediaType, imageDescription, useBlurhash) - newPost = appendEventFields(newPost, eventUUID, eventStatus, - anonymousParticipationEnabled, - repliesModerationOption, - category, joinMode, - eventDateStr, endDateStr, - location, maximumAttendeeCapacity) + #newPost = appendEventFields(newPost, eventUUID, eventStatus, + # anonymousParticipationEnabled, + # repliesModerationOption, + # category, joinMode, + # eventDateStr, endDateStr, + # location, maximumAttendeeCapacity) if ccUrl: if len(ccUrl) > 0: newPost['cc'] = [ccUrl] diff --git a/utils.py b/utils.py index 3f36d25bd..3855083ac 100644 --- a/utils.py +++ b/utils.py @@ -28,6 +28,8 @@ def removeIdEnding(idStr: str) -> str: idStr = idStr[:-len('/undo')] elif idStr.endswith('/event'): idStr = idStr[:-len('/event')] + elif idStr.endswith('/replies'): + idStr = idStr[:-len('/replies')] return idStr From e90f1d4f2c318bf85531d46f9b87e5f301feadc0 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 23 Aug 2020 16:11:42 +0100 Subject: [PATCH 42/84] No return object --- posts.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/posts.py b/posts.py index 44b3e90f7..e2dfd5363 100644 --- a/posts.py +++ b/posts.py @@ -619,13 +619,11 @@ def appendEventFields(newPost: {}, eventDateStr: str, endDateStr: str, location: str, - maximumAttendeeCapacity: int) -> {}: + maximumAttendeeCapacity: int) -> None: """Appends Mobilizon-type event fields to a post """ - return newPost - if not eventUUID: - return newPost + return # add attributes for Mobilizon-type events newPost['uuid'] = eventUUID @@ -646,7 +644,6 @@ def appendEventFields(newPost: {}, newPost['location'] = location if maximumAttendeeCapacity: newPost['maximumAttendeeCapacity'] = maximumAttendeeCapacity - return newPost def createPostBase(baseDir: str, nickname: str, domain: str, port: int, @@ -894,12 +891,12 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, attachMedia(baseDir, httpPrefix, domain, port, newPost['object'], attachImageFilename, mediaType, imageDescription, useBlurhash) - #newPost = appendEventFields(newPost['object'], eventUUID, eventStatus, - # anonymousParticipationEnabled, - # repliesModerationOption, - # category, joinMode, - # eventDateStr, endDateStr, - # location, maximumAttendeeCapacity) + appendEventFields(newPost['object'], eventUUID, eventStatus, + anonymousParticipationEnabled, + repliesModerationOption, + category, joinMode, + eventDateStr, endDateStr, + location, maximumAttendeeCapacity) else: idStr = \ httpPrefix + '://' + domain + '/users/' + nickname + \ @@ -941,12 +938,12 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, attachMedia(baseDir, httpPrefix, domain, port, newPost, attachImageFilename, mediaType, imageDescription, useBlurhash) - #newPost = appendEventFields(newPost, eventUUID, eventStatus, - # anonymousParticipationEnabled, - # repliesModerationOption, - # category, joinMode, - # eventDateStr, endDateStr, - # location, maximumAttendeeCapacity) + appendEventFields(newPost, eventUUID, eventStatus, + anonymousParticipationEnabled, + repliesModerationOption, + category, joinMode, + eventDateStr, endDateStr, + location, maximumAttendeeCapacity) if ccUrl: if len(ccUrl) > 0: newPost['cc'] = [ccUrl] From 1ff70dd8a4571b90ed33bddd5e82d67a89ca5273 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 23 Aug 2020 17:53:22 +0100 Subject: [PATCH 43/84] Terminology consistent with other event systems --- translations/ar.json | 1 + translations/ca.json | 1 + translations/cy.json | 1 + translations/de.json | 1 + translations/en.json | 1 + translations/es.json | 1 + translations/fr.json | 1 + translations/ga.json | 1 + translations/hi.json | 1 + translations/it.json | 1 + translations/ja.json | 1 + translations/oc.json | 1 + translations/pt.json | 1 + translations/ru.json | 1 + translations/zh.json | 1 + webinterface.py | 2 +- 16 files changed, 16 insertions(+), 1 deletion(-) diff --git a/translations/ar.json b/translations/ar.json index 56c889fa1..dbfdab999 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -258,6 +258,7 @@ "Notes": "ملاحظات", "Allow replies.": "السماح بالردود.", "Event": "حدث", + "Event name": "اسم الحدث", "Events": "الأحداث", "Create an event": "أنشئ حدثًا", "Describe the event": "صف الحدث", diff --git a/translations/ca.json b/translations/ca.json index 3e420c602..dfb820e6b 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -258,6 +258,7 @@ "Notes": "Notes", "Allow replies.": "Permetre respostes.", "Event": "Esdeveniment", + "Event name": "Nom de l’esdeveniment", "Events": "Esdeveniments", "Create an event": "Crea un esdeveniment", "Describe the event": "Descriviu l’esdeveniment", diff --git a/translations/cy.json b/translations/cy.json index e7bd2c8bb..626f61f2f 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -258,6 +258,7 @@ "Notes": "Nodiadau", "Allow replies.": "Caniatáu atebion.", "Event": "Digwyddiad", + "Event name": "Enw'r digwyddiad", "Events": "Digwyddiadau", "Create an event": "Creu digwyddiad", "Describe the event": "Disgrifiwch y digwyddiad", diff --git a/translations/de.json b/translations/de.json index a1029eb32..c5744c5eb 100644 --- a/translations/de.json +++ b/translations/de.json @@ -258,6 +258,7 @@ "Notes": "Anmerkungen", "Allow replies.": "Antworten zulassen.", "Event": "Veranstaltung", + "Event name": "Veranstaltungsname", "Events": "Veranstaltungen", "Create an event": "Erstellen Sie ein Ereignis", "Describe the event": "Beschreiben Sie das Ereignis", diff --git a/translations/en.json b/translations/en.json index d5f957fdb..9fe0967fd 100644 --- a/translations/en.json +++ b/translations/en.json @@ -258,6 +258,7 @@ "Notes": "Notes", "Allow replies.": "Allow replies.", "Event": "Event", + "Event name": "Event name", "Events": "Events", "Create an event": "Create an event", "Describe the event": "Describe the event", diff --git a/translations/es.json b/translations/es.json index 907814a64..5971ee9f7 100644 --- a/translations/es.json +++ b/translations/es.json @@ -258,6 +258,7 @@ "Notes": "Notas", "Allow replies.": "Permitir respuestas.", "Event": "Evento", + "Event name": "Nombre del evento", "Events": "Eventos", "Create an event": "Crea un evento", "Describe the event": "Describe el evento", diff --git a/translations/fr.json b/translations/fr.json index 3cd15ff37..4bb72abaf 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", + "Event name": "Nom de l'événement", "Events": "Événements", "Create an event": "Créer un événement", "Describe the event": "Décrivez l'événement", diff --git a/translations/ga.json b/translations/ga.json index bc7237e4e..c0bd69ecd 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -258,6 +258,7 @@ "Notes": "Nótaí", "Allow replies.": "Ceadaigh freagraí.", "Event": "Imeacht", + "Event name": "Ainm na hócáide", "Events": "Imeachtaí", "Create an event": "Cruthaigh imeacht", "Describe the event": "Déan cur síos ar an ócáid", diff --git a/translations/hi.json b/translations/hi.json index fea37de5b..eece055d7 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -258,6 +258,7 @@ "Notes": "टिप्पणियाँ", "Allow replies.": "जवाब दें।", "Event": "प्रतिस्पर्धा", + "Event name": "कार्यक्रम नाम", "Events": "आयोजन", "Create an event": "एक घटना बनाएँ", "Describe the event": "घटना का वर्णन करें", diff --git a/translations/it.json b/translations/it.json index cf22239ec..5e5113d1d 100644 --- a/translations/it.json +++ b/translations/it.json @@ -258,6 +258,7 @@ "Notes": "Appunti", "Allow replies.": "Consenti risposte.", "Event": "Evento", + "Event name": "Nome dell'evento", "Events": "Eventi", "Create an event": "Crea un evento", "Describe the event": "Descrivi l'evento", diff --git a/translations/ja.json b/translations/ja.json index 6a024bcf1..27710682f 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -258,6 +258,7 @@ "Notes": "ノート", "Allow replies.": "返信を許可します。", "Event": "イベント", + "Event name": "イベント名", "Events": "イベント", "Create an event": "イベントを作成する", "Describe the event": "イベントについて説明する", diff --git a/translations/oc.json b/translations/oc.json index b38fdf8dc..d7c0d467e 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -254,6 +254,7 @@ "Notes": "Notes", "Allow replies.": "Allow replies.", "Event": "Event", + "Event name": "Event name", "Events": "Events", "Create an event": "Create an event", "Describe the event": "Describe the event", diff --git a/translations/pt.json b/translations/pt.json index 86cef14ab..66c9735b0 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -258,6 +258,7 @@ "Notes": "Notas", "Allow replies.": "Permitir respostas.", "Event": "Evento", + "Event name": "Nome do evento", "Events": "Eventos", "Create an event": "Crie um evento", "Describe the event": "Descreva o evento", diff --git a/translations/ru.json b/translations/ru.json index 0bdedc6aa..4d7b8d24e 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -258,6 +258,7 @@ "Notes": "Ноты", "Allow replies.": "Разрешить ответы.", "Event": "Мероприятие", + "Event name": "Название события", "Events": "События", "Create an event": "Создать мероприятие", "Describe the event": "Опишите событие", diff --git a/translations/zh.json b/translations/zh.json index d01c60b9b..b49b24fe8 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -258,6 +258,7 @@ "Notes": "笔记", "Allow replies.": "允许回复。", "Event": "事件", + "Event name": "活动名称", "Events": "大事记", "Create an event": "建立活动", "Describe the event": "描述事件", diff --git a/webinterface.py b/webinterface.py index 1eebed49e..2e497b67c 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1990,7 +1990,7 @@ def htmlNewPost(mediaInstance: bool, translate: {}, scopeIcon = 'scope_event.png' scopeDescription = translate['Event'] endpoint = 'newevent' - placeholderSubject = translate['Title'] + '...' + placeholderSubject = translate['Event name'] placeholderMessage = translate['Describe the event'] + '...' elif path.endswith('/newreport'): scopeIcon = 'scope_report.png' From 0294f1c722e88627af2a5ceb0eed8c1d52bb1b9c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 23 Aug 2020 18:50:49 +0100 Subject: [PATCH 44/84] Ticket url in event --- daemon.py | 3 ++- posts.py | 33 +++++++++++++++++++-------------- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- webinterface.py | 3 +++ 18 files changed, 54 insertions(+), 30 deletions(-) diff --git a/daemon.py b/daemon.py index 1f94d4c30..d6e15ba41 100644 --- a/daemon.py +++ b/daemon.py @@ -5864,7 +5864,8 @@ class PubServer(BaseHTTPRequestHandler): maximumAttendeeCapacity, fields['repliesModerationOption'], anonymousParticipationEnabled, - fields['eventStatus']) + fields['eventStatus'], + fields['ticketUrl']) if messageJson: if fields['schedulePost']: return 1 diff --git a/posts.py b/posts.py index e2dfd5363..ec49a6359 100644 --- a/posts.py +++ b/posts.py @@ -619,7 +619,8 @@ def appendEventFields(newPost: {}, eventDateStr: str, endDateStr: str, location: str, - maximumAttendeeCapacity: int) -> None: + maximumAttendeeCapacity: int, + ticketUrl: str) -> None: """Appends Mobilizon-type event fields to a post """ if not eventUUID: @@ -644,6 +645,8 @@ def appendEventFields(newPost: {}, newPost['location'] = location if maximumAttendeeCapacity: newPost['maximumAttendeeCapacity'] = maximumAttendeeCapacity + if ticketUrl: + newPost['ticketUrl'] = ticketUrl def createPostBase(baseDir: str, nickname: str, domain: str, port: int, @@ -662,7 +665,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, maximumAttendeeCapacity=None, repliesModerationOption=None, anonymousParticipationEnabled=None, - eventStatus=None) -> {}: + eventStatus=None, ticketUrl=None) -> {}: """Creates a message """ mentionedRecipients = \ @@ -896,7 +899,8 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, repliesModerationOption, category, joinMode, eventDateStr, endDateStr, - location, maximumAttendeeCapacity) + location, maximumAttendeeCapacity, + ticketUrl) else: idStr = \ httpPrefix + '://' + domain + '/users/' + nickname + \ @@ -943,7 +947,8 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, repliesModerationOption, category, joinMode, eventDateStr, endDateStr, - location, maximumAttendeeCapacity) + location, maximumAttendeeCapacity, + ticketUrl) if ccUrl: if len(ccUrl) > 0: newPost['cc'] = [ccUrl] @@ -1120,7 +1125,7 @@ def createPublicPost(baseDir: str, False, False, inReplyTo, inReplyToAtomUri, subject, schedulePost, eventDate, eventTime, location, None, None, None, None, None, - None, None, None, None) + None, None, None, None, None) def createBlogPost(baseDir: str, @@ -1173,7 +1178,7 @@ def createQuestionPost(baseDir: str, False, False, None, None, subject, False, None, None, None, None, None, None, None, None, - None, None, None, None) + None, None, None, None, None) messageJson['object']['type'] = 'Question' messageJson['object']['oneOf'] = [] messageJson['object']['votersCount'] = 0 @@ -1222,7 +1227,7 @@ def createUnlistedPost(baseDir: str, False, False, inReplyTo, inReplyToAtomUri, subject, schedulePost, eventDate, eventTime, location, None, None, None, None, None, - None, None, None, None) + None, None, None, None, None) def createFollowersOnlyPost(baseDir: str, @@ -1255,7 +1260,7 @@ def createFollowersOnlyPost(baseDir: str, False, False, inReplyTo, inReplyToAtomUri, subject, schedulePost, eventDate, eventTime, location, None, None, None, None, None, - None, None, None, None) + None, None, None, None, None) def createEventPost(baseDir: str, @@ -1273,7 +1278,7 @@ def createEventPost(baseDir: str, maximumAttendeeCapacity=None, repliesModerationOption=None, anonymousParticipationEnabled=None, - eventStatus=None) -> {}: + eventStatus=None, ticketUrl=None) -> {}: """Mobilizon-type Event post """ if not attachImageFilename: @@ -1307,7 +1312,7 @@ def createEventPost(baseDir: str, endDate, endTime, maximumAttendeeCapacity, repliesModerationOption, anonymousParticipationEnabled, - eventStatus) + eventStatus, ticketUrl) def getMentionedPeople(baseDir: str, httpPrefix: str, @@ -1379,7 +1384,7 @@ def createDirectMessagePost(baseDir: str, False, False, inReplyTo, inReplyToAtomUri, subject, schedulePost, eventDate, eventTime, location, None, None, None, None, None, - None, None, None, None) + None, None, None, None, None) # mentioned recipients go into To rather than Cc messageJson['to'] = messageJson['object']['cc'] messageJson['object']['to'] = messageJson['to'] @@ -1473,7 +1478,7 @@ def createReportPost(baseDir: str, True, False, None, None, subject, False, None, None, None, None, None, None, None, None, - None, None, None, None) + None, None, None, None, None) if not postJsonObject: continue @@ -1634,7 +1639,7 @@ def sendPost(projectVersion: str, inReplyToAtomUri, subject, False, None, None, None, None, None, None, None, None, - None, None, None, None) + None, None, None, None, None) # get the senders private key privateKeyPem = getPersonKey(nickname, domain, baseDir, 'private') @@ -1782,7 +1787,7 @@ def sendPostViaServer(projectVersion: str, inReplyToAtomUri, subject, False, None, None, None, None, None, None, None, None, - None, None, None, None) + None, None, None, None, None) authHeader = createBasicAuthHeader(fromNickname, password) diff --git a/translations/ar.json b/translations/ar.json index dbfdab999..cbf796db8 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -277,5 +277,6 @@ "Cancelled": "ألغيت", "Event banner image description": "وصف صورة شعار الحدث", "Banner image": "صورة بانر", - "Maximum attendees": "الحد الأقصى للحضور" + "Maximum attendees": "الحد الأقصى للحضور", + "Ticket URL": "عنوان URL للتذكرة" } diff --git a/translations/ca.json b/translations/ca.json index dfb820e6b..310a661c3 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -277,5 +277,6 @@ "Cancelled": "Cancel·lat", "Event banner image description": "Descripció de la imatge del banner de l’esdeveniment", "Banner image": "Imatge de pancarta", - "Maximum attendees": "Màxim d’assistents" + "Maximum attendees": "Màxim d’assistents", + "Ticket URL": "URL de l'entrada" } diff --git a/translations/cy.json b/translations/cy.json index 626f61f2f..d2cece7ae 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -277,5 +277,6 @@ "Cancelled": "Wedi'i ganslo", "Event banner image description": "Disgrifiad delwedd baner y digwyddiad", "Banner image": "Delwedd baner", - "Maximum attendees": "Uchafswm mynychwyr" + "Maximum attendees": "Uchafswm mynychwyr", + "Ticket URL": "URL y tocyn" } diff --git a/translations/de.json b/translations/de.json index c5744c5eb..5e5b00215 100644 --- a/translations/de.json +++ b/translations/de.json @@ -277,5 +277,6 @@ "Cancelled": "Abgesagt", "Event banner image description": "Beschreibung des Ereignisbannerbildes", "Banner image": "Bannerbild", - "Maximum attendees": "Maximale Teilnehmerzahl" + "Maximum attendees": "Maximale Teilnehmerzahl", + "Ticket URL": "Ticket URL" } diff --git a/translations/en.json b/translations/en.json index 9fe0967fd..32950fcc6 100644 --- a/translations/en.json +++ b/translations/en.json @@ -277,5 +277,6 @@ "Cancelled": "Cancelled", "Event banner image description": "Event banner image description", "Banner image": "Banner image", - "Maximum attendees": "Maximum attendees" + "Maximum attendees": "Maximum attendees", + "Ticket URL": "Ticket URL" } diff --git a/translations/es.json b/translations/es.json index 5971ee9f7..3023aba06 100644 --- a/translations/es.json +++ b/translations/es.json @@ -277,5 +277,6 @@ "Cancelled": "Cancelada", "Event banner image description": "Descripción de la imagen del banner del evento", "Banner image": "Imagen de banner", - "Maximum attendees": "Asistentes máximos" + "Maximum attendees": "Asistentes máximos", + "Ticket URL": "URL del ticket" } diff --git a/translations/fr.json b/translations/fr.json index 4bb72abaf..ba35142d3 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -277,5 +277,6 @@ "Cancelled": "Annulé", "Event banner image description": "Description de l'image de la bannière de l'événement", "Banner image": "Image de bannière", - "Maximum attendees": "Nombre maximum de participants" + "Maximum attendees": "Nombre maximum de participants", + "Ticket URL": "URL du ticket" } diff --git a/translations/ga.json b/translations/ga.json index c0bd69ecd..f2d7dfd0f 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -277,5 +277,6 @@ "Cancelled": "Cealaithe", "Event banner image description": "Tuairisc íomhá meirge na hócáide", "Banner image": "Íomhá meirge", - "Maximum attendees": "Uasmhéid freastail" + "Maximum attendees": "Uasmhéid freastail", + "Ticket URL": "URL na dticéad" } diff --git a/translations/hi.json b/translations/hi.json index eece055d7..f1408a8b8 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -277,5 +277,6 @@ "Cancelled": "रद्द", "Event banner image description": "घटना बैनर छवि विवरण", "Banner image": "बैनर की छवि", - "Maximum attendees": "अधिकतम उपस्थित" + "Maximum attendees": "अधिकतम उपस्थित", + "Ticket URL": "टिकट URL" } diff --git a/translations/it.json b/translations/it.json index 5e5113d1d..26129d99f 100644 --- a/translations/it.json +++ b/translations/it.json @@ -277,5 +277,6 @@ "Cancelled": "Annullata", "Event banner image description": "Descrizione dell'immagine del banner dell'evento", "Banner image": "Immagine banner", - "Maximum attendees": "Numero massimo di partecipanti" + "Maximum attendees": "Numero massimo di partecipanti", + "Ticket URL": "URL del biglietto" } diff --git a/translations/ja.json b/translations/ja.json index 27710682f..7168441e0 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -277,5 +277,6 @@ "Cancelled": "キャンセル", "Event banner image description": "イベントバナー画像の説明", "Banner image": "バナー画像", - "Maximum attendees": "最大参加者" + "Maximum attendees": "最大参加者", + "Ticket URL": "チケットURL" } diff --git a/translations/oc.json b/translations/oc.json index d7c0d467e..d4de3fa48 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -273,5 +273,6 @@ "Cancelled": "Cancelled", "Event banner image description": "Event banner image description", "Banner image": "Banner image", - "Maximum attendees": "Maximum attendees" + "Maximum attendees": "Maximum attendees", + "Ticket URL": "Ticket URL" } diff --git a/translations/pt.json b/translations/pt.json index 66c9735b0..82f591749 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -277,5 +277,6 @@ "Cancelled": "Cancelada", "Event banner image description": "Descrição da imagem do banner do evento", "Banner image": "Imagem de banner", - "Maximum attendees": "Máximo de participantes" + "Maximum attendees": "Máximo de participantes", + "Ticket URL": "URL do bilhete" } diff --git a/translations/ru.json b/translations/ru.json index 4d7b8d24e..5f00471d4 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -277,5 +277,6 @@ "Cancelled": "Отменено", "Event banner image description": "Описание изображения баннера мероприятия", "Banner image": "Изображение баннера", - "Maximum attendees": "Максимальное количество участников" + "Maximum attendees": "Максимальное количество участников", + "Ticket URL": "URL билета" } diff --git a/translations/zh.json b/translations/zh.json index b49b24fe8..eab8eece1 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -277,5 +277,6 @@ "Cancelled": "取消", "Event banner image description": "活动横幅图片说明", "Banner image": "横幅图片", - "Maximum attendees": "参加人数上限" + "Maximum attendees": "参加人数上限", + "Ticket URL": "工单URL" } diff --git a/webinterface.py b/webinterface.py index 2e497b67c..29707c2a4 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2167,6 +2167,9 @@ def htmlNewPost(mediaInstance: bool, translate: {}, translate['Location'] + ': \n' dateAndLocation += '\n' if endpoint == 'newevent': + dateAndLocation += '
    \n' + dateAndLocation += '\n' dateAndLocation += '
    \n' dateAndLocation += '\n' From 4d4a19f9a23308bdcc2f6c3606a5d8ea21dab66c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 23 Aug 2020 20:04:11 +0100 Subject: [PATCH 45/84] Event name --- posts.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/posts.py b/posts.py index ec49a6359..80ad3f22a 100644 --- a/posts.py +++ b/posts.py @@ -620,7 +620,8 @@ def appendEventFields(newPost: {}, endDateStr: str, location: str, maximumAttendeeCapacity: int, - ticketUrl: str) -> None: + ticketUrl: str, + subject: str) -> None: """Appends Mobilizon-type event fields to a post """ if not eventUUID: @@ -647,6 +648,8 @@ def appendEventFields(newPost: {}, newPost['maximumAttendeeCapacity'] = maximumAttendeeCapacity if ticketUrl: newPost['ticketUrl'] = ticketUrl + if subject: + newPost['name'] = subject def createPostBase(baseDir: str, nickname: str, domain: str, port: int, @@ -900,7 +903,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, category, joinMode, eventDateStr, endDateStr, location, maximumAttendeeCapacity, - ticketUrl) + ticketUrl, subject) else: idStr = \ httpPrefix + '://' + domain + '/users/' + nickname + \ @@ -948,7 +951,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, category, joinMode, eventDateStr, endDateStr, location, maximumAttendeeCapacity, - ticketUrl) + ticketUrl, subject) if ccUrl: if len(ccUrl) > 0: newPost['cc'] = [ccUrl] From ffb827fdbb91238070cb7196c58e0afd5fe6d943 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 23 Aug 2020 20:06:49 +0100 Subject: [PATCH 46/84] Set event name --- posts.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/posts.py b/posts.py index 80ad3f22a..33bc00f6e 100644 --- a/posts.py +++ b/posts.py @@ -650,6 +650,8 @@ def appendEventFields(newPost: {}, newPost['ticketUrl'] = ticketUrl if subject: newPost['name'] = subject + newPost['summary'] = None + newPost['sensitive'] = False def createPostBase(baseDir: str, nickname: str, domain: str, port: int, From a691110dedd38ab20ee7d562c7ef383958b394f6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 24 Aug 2020 09:53:42 +0100 Subject: [PATCH 47/84] Radio button scale on mobile --- epicyon-profile.css | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/epicyon-profile.css b/epicyon-profile.css index 9b368d22c..62ee6412e 100644 --- a/epicyon-profile.css +++ b/epicyon-profile.css @@ -1711,22 +1711,22 @@ aside .toggle-inside li { } input[type=radio] { - -ms-transform: scale(4); - -moz-transform: scale(4); - -webkit-transform: scale(4); - -o-transform: scale(4); - transform: scale(4); + -ms-transform: scale(2); + -moz-transform: scale(2); + -webkit-transform: scale(2); + -o-transform: scale(2); + transform: scale(2); padding: 20px; margin: 30px 40px; } input[type=number] { - -ms-transform: scale(4); - -moz-transform: scale(4); - -webkit-transform: scale(4); - -o-transform: scale(4); - transform: scale(4); - padding: 20px; - margin: 30px 80px; + -ms-transform: scale(2); + -moz-transform: scale(2); + -webkit-transform: scale(2); + -o-transform: scale(2); + transform: scale(2); + padding: 10px; + margin: 40px 80px; } } From df29c0e0240851022f717d338c6f43477f15d780 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 24 Aug 2020 10:05:46 +0100 Subject: [PATCH 48/84] Clearer new post logic --- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- webinterface.py | 62 +++++++++++++++++++++++++------------------- 16 files changed, 65 insertions(+), 42 deletions(-) diff --git a/translations/ar.json b/translations/ar.json index cbf796db8..6c65f21b9 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -278,5 +278,6 @@ "Event banner image description": "وصف صورة شعار الحدث", "Banner image": "صورة بانر", "Maximum attendees": "الحد الأقصى للحضور", - "Ticket URL": "عنوان URL للتذكرة" + "Ticket URL": "عنوان URL للتذكرة", + "Create a new event": "أنشئ حدثًا جديدًا" } diff --git a/translations/ca.json b/translations/ca.json index 310a661c3..c462ce6d3 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -278,5 +278,6 @@ "Event banner image description": "Descripció de la imatge del banner de l’esdeveniment", "Banner image": "Imatge de pancarta", "Maximum attendees": "Màxim d’assistents", - "Ticket URL": "URL de l'entrada" + "Ticket URL": "URL de l'entrada", + "Create a new event": "Creeu un esdeveniment nou" } diff --git a/translations/cy.json b/translations/cy.json index d2cece7ae..9affa0a71 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -278,5 +278,6 @@ "Event banner image description": "Disgrifiad delwedd baner y digwyddiad", "Banner image": "Delwedd baner", "Maximum attendees": "Uchafswm mynychwyr", - "Ticket URL": "URL y tocyn" + "Ticket URL": "URL y tocyn", + "Create a new event": "Creu digwyddiad newydd" } diff --git a/translations/de.json b/translations/de.json index 5e5b00215..930ef8304 100644 --- a/translations/de.json +++ b/translations/de.json @@ -278,5 +278,6 @@ "Event banner image description": "Beschreibung des Ereignisbannerbildes", "Banner image": "Bannerbild", "Maximum attendees": "Maximale Teilnehmerzahl", - "Ticket URL": "Ticket URL" + "Ticket URL": "Ticket URL", + "Create a new event": "Erstellen Sie ein neues Ereignis" } diff --git a/translations/en.json b/translations/en.json index 32950fcc6..00626db29 100644 --- a/translations/en.json +++ b/translations/en.json @@ -278,5 +278,6 @@ "Event banner image description": "Event banner image description", "Banner image": "Banner image", "Maximum attendees": "Maximum attendees", - "Ticket URL": "Ticket URL" + "Ticket URL": "Ticket URL", + "Create a new event": "Create a new event" } diff --git a/translations/es.json b/translations/es.json index 3023aba06..c605d8172 100644 --- a/translations/es.json +++ b/translations/es.json @@ -278,5 +278,6 @@ "Event banner image description": "Descripción de la imagen del banner del evento", "Banner image": "Imagen de banner", "Maximum attendees": "Asistentes máximos", - "Ticket URL": "URL del ticket" + "Ticket URL": "URL del ticket", + "Create a new event": "Crea un nuevo evento" } diff --git a/translations/fr.json b/translations/fr.json index ba35142d3..19dd7bf1d 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -278,5 +278,6 @@ "Event banner image description": "Description de l'image de la bannière de l'événement", "Banner image": "Image de bannière", "Maximum attendees": "Nombre maximum de participants", - "Ticket URL": "URL du ticket" + "Ticket URL": "URL du ticket", + "Create a new event": "Créer un nouvel événement" } diff --git a/translations/ga.json b/translations/ga.json index f2d7dfd0f..33184c028 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -278,5 +278,6 @@ "Event banner image description": "Tuairisc íomhá meirge na hócáide", "Banner image": "Íomhá meirge", "Maximum attendees": "Uasmhéid freastail", - "Ticket URL": "URL na dticéad" + "Ticket URL": "URL na dticéad", + "Create a new event": "Cruthaigh imeacht nua" } diff --git a/translations/hi.json b/translations/hi.json index f1408a8b8..b7ca0a68a 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -278,5 +278,6 @@ "Event banner image description": "घटना बैनर छवि विवरण", "Banner image": "बैनर की छवि", "Maximum attendees": "अधिकतम उपस्थित", - "Ticket URL": "टिकट URL" + "Ticket URL": "टिकट URL", + "Create a new event": "एक नई घटना बनाएँ" } diff --git a/translations/it.json b/translations/it.json index 26129d99f..f5db48988 100644 --- a/translations/it.json +++ b/translations/it.json @@ -278,5 +278,6 @@ "Event banner image description": "Descrizione dell'immagine del banner dell'evento", "Banner image": "Immagine banner", "Maximum attendees": "Numero massimo di partecipanti", - "Ticket URL": "URL del biglietto" + "Ticket URL": "URL del biglietto", + "Create a new event": "Crea un nuovo evento" } diff --git a/translations/ja.json b/translations/ja.json index 7168441e0..0e18d19b8 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -278,5 +278,6 @@ "Event banner image description": "イベントバナー画像の説明", "Banner image": "バナー画像", "Maximum attendees": "最大参加者", - "Ticket URL": "チケットURL" + "Ticket URL": "チケットURL", + "Create a new event": "新しいイベントを作成する" } diff --git a/translations/oc.json b/translations/oc.json index d4de3fa48..d2c8307b2 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -274,5 +274,6 @@ "Event banner image description": "Event banner image description", "Banner image": "Banner image", "Maximum attendees": "Maximum attendees", - "Ticket URL": "Ticket URL" + "Ticket URL": "Ticket URL", + "Create a new event": "Create a new event" } diff --git a/translations/pt.json b/translations/pt.json index 82f591749..aaf2f4ff7 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -278,5 +278,6 @@ "Event banner image description": "Descrição da imagem do banner do evento", "Banner image": "Imagem de banner", "Maximum attendees": "Máximo de participantes", - "Ticket URL": "URL do bilhete" + "Ticket URL": "URL do bilhete", + "Create a new event": "Crie um novo evento" } diff --git a/translations/ru.json b/translations/ru.json index 5f00471d4..9f55c9816 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -278,5 +278,6 @@ "Event banner image description": "Описание изображения баннера мероприятия", "Banner image": "Изображение баннера", "Maximum attendees": "Максимальное количество участников", - "Ticket URL": "URL билета" + "Ticket URL": "URL билета", + "Create a new event": "Создать новое мероприятие" } diff --git a/translations/zh.json b/translations/zh.json index eab8eece1..0a0f18988 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -278,5 +278,6 @@ "Event banner image description": "活动横幅图片说明", "Banner image": "横幅图片", "Maximum attendees": "参加人数上限", - "Ticket URL": "工单URL" + "Ticket URL": "工单URL", + "Create a new event": "建立新活动" } diff --git a/webinterface.py b/webinterface.py index 29707c2a4..cabbd6ace 100644 --- a/webinterface.py +++ b/webinterface.py @@ -4729,33 +4729,8 @@ def htmlTimeline(defaultTimeline: str, tlStr = htmlHeader(cssFilename, profileStyle) - if boxName != 'dm': - if boxName != 'tlblogs': - if not manuallyApproveFollowers: - newPostButtonStr = \ - '
    | ' + \
-                    translate['Create a new post'] + \
-                    '\n' - else: - newPostButtonStr = \ - '| ' + translate['Create a new post'] + \
-                    '\n' - else: - newPostButtonStr = \ - '| ' + \
-                translate['Create a new post'] + \
-                '\n' - else: + # what screen to go to when a new post is created + if boxName == 'dm': newPostButtonStr = \ '| ' + translate['Create a new DM'] + \
             '\n' + elif boxName == 'tlblogs': + newPostButtonStr = \ + '| ' + \
+            translate['Create a new post'] + \
+            '\n' + elif boxName == 'tlevents': + newPostButtonStr = \ + '| ' + \
+            translate['Create a new event'] + \
+            '\n' + else: + if not manuallyApproveFollowers: + newPostButtonStr = \ + '| ' + \
+                translate['Create a new post'] + \
+                '\n' + else: + newPostButtonStr = \ + '| ' + translate['Create a new post'] + \
+                '\n' # This creates a link to the profile page when viewed # in lynx, but should be invisible in a graphical web browser From a4db99566bb11bfca21eb9acb869b7ca8f00d12d Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 24 Aug 2020 10:31:31 +0100 Subject: [PATCH 49/84] Comments --- webinterface.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index cabbd6ace..41b278c5f 100644 --- a/webinterface.py +++ b/webinterface.py @@ -4585,7 +4585,7 @@ def htmlTimeline(defaultTimeline: str, if boxName == 'tlshares': os.remove(newShareFile) - # should the Moderation button be highlighted? + # should the Moderation/reports button be highlighted? newReport = False newReportFile = accountDir + '/.newReport' if os.path.isfile(newReportFile): @@ -4593,11 +4593,16 @@ def htmlTimeline(defaultTimeline: str, if boxName == 'moderation': os.remove(newReportFile) + # directory where icons are found + # This changes depending upon theme iconsDir = getIconsDir(baseDir) + + # the css filename cssFilename = baseDir + '/epicyon-profile.css' if os.path.isfile(baseDir + '/epicyon.css'): cssFilename = baseDir + '/epicyon.css' + # filename of the banner shown at the top bannerFile = 'banner.png' bannerFilename = baseDir + '/accounts/' + \ nickname + '@' + domain + '/' + bannerFile @@ -4613,16 +4618,20 @@ def htmlTimeline(defaultTimeline: str, bannerFile = 'banner.webp' with open(cssFilename, 'r') as cssFile: + # load css profileStyle = \ cssFile.read().replace('banner.png', '/users/' + nickname + '/' + bannerFile) + # replace any https within the css with whatever prefix is needed if httpPrefix != 'https': profileStyle = \ profileStyle.replace('https://', httpPrefix + '://') + # is the user a moderator? moderator = isModerator(baseDir, nickname) + # the appearance of buttons - highlighted or not inboxButton = 'button' blogsButton = 'button' dmButton = 'button' @@ -4670,15 +4679,18 @@ def htmlTimeline(defaultTimeline: str, elif boxName == 'tlevents' or boxName == 'events': eventsButton = 'buttonselected' + # get the full domain, including any port number fullDomain = domain if port != 80 and port != 443: if ':' not in domain: fullDomain = domain + ':' + str(port) + usersPath = '/users/' + nickname actor = httpPrefix + '://' + fullDomain + usersPath showIndividualPostIcons = True + # show an icon for new follow approvals followApprovals = '' followRequestsFilename = \ baseDir + '/accounts/' + \ @@ -4698,6 +4710,7 @@ def htmlTimeline(defaultTimeline: str, '" src="/' + iconsDir + '/person.png"/>\n' break + # moderation / reports button moderationButtonStr = '' if moderator and not minimal: moderationButtonStr = \ @@ -4707,6 +4720,7 @@ def htmlTimeline(defaultTimeline: str, htmlHighlightLabel(translate['Mod'], newReport) + \ ' \n' + # shares, bookmarks and events buttons sharesButtonStr = '' bookmarksButtonStr = '' eventsButtonStr = '' @@ -4835,6 +4849,7 @@ def htmlTimeline(defaultTimeline: str, '\n' # typically the blogs button + # but may change if this is a blogging oriented instance if defaultTimeline != 'tlblogs': if not minimal: tlStr += \ @@ -4850,14 +4865,19 @@ def htmlTimeline(defaultTimeline: str, inboxButton + '">' + translate['Inbox'] + \ '\n' + # button for the outbox tlStr += \ ' \n' + + # add other buttons tlStr += \ sharesButtonStr + bookmarksButtonStr + eventsButtonStr + \ moderationButtonStr + newPostButtonStr + + # the search button tlStr += \ ' | ' + \
         translate['Search and follow'] + '\n' + # the calendar button calendarAltText = translate['Calendar'] if newCalendarEvent: # indicate that the calendar icon is highlighted @@ -4875,6 +4896,7 @@ def htmlTimeline(defaultTimeline: str, calendarImage + '" title="' + translate['Calendar'] + \ '" alt="| ' + calendarAltText + '" class="timelineicon"/>\n' + # the show/hide button, for a simpler header appearance tlStr += \ ' \n' + if endpoint == 'newevent': + dateAndLocation += '
    \n' + dateAndLocation += '
    \n' + dateAndLocation += '
    \n' + dateAndLocation += \ + ' \n' dateAndLocation += '
    \n' dateAndLocation += '
    \n' dateAndLocation += '
    content warning') + assert resultStr == 'Invalid content warning' + + def runAllTests(): print('Running tests...') + testValidContentWarning() testRemoveIdEnding() testJsonPostAllowsComments() runHtmlReplaceQuoteMarks() From a4849367930f13614499025faeec7705a65efa62 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 25 Aug 2020 20:45:15 +0100 Subject: [PATCH 62/84] More content warning validation --- inbox.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/inbox.py b/inbox.py index 8fdc61378..6e7838f74 100644 --- a/inbox.py +++ b/inbox.py @@ -50,6 +50,7 @@ from filters import isFiltered from announce import updateAnnounceCollection from announce import undoAnnounceCollectionEntry from httpsig import messageContentDigest +from posts import validContentWarning from posts import downloadAnnounce from posts import isDM from posts import isReply @@ -1708,6 +1709,15 @@ def validPostContent(baseDir: str, nickname: str, domain: str, if 'Z' not in messageJson['object']['published']: return False + if messageJson['object'].get('summary'): + summary = messageJson['object']['summary'] + if not isinstance(summary, str): + print('WARN: content warning is not a string') + return False + if summary != validContentWarning(summary): + print('WARN: invalid content warning ' + summary) + return False + if isGitPatch(baseDir, nickname, domain, messageJson['object']['type'], messageJson['object']['summary'], From 38f4c6e66a5c52d06970030d885e108183975826 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 25 Aug 2020 21:09:14 +0100 Subject: [PATCH 63/84] Save to event timeline --- outbox.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/outbox.py b/outbox.py index 0c4b7fafb..8fcf5865b 100644 --- a/outbox.py +++ b/outbox.py @@ -198,8 +198,7 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str, messageJson['type'] == 'Announce': indexes = [outboxName, "inbox"] selfActor = \ - httpPrefix + '://' + domainFull + \ - '/users/' + postToNickname + httpPrefix + '://' + domainFull + '/users/' + postToNickname for boxNameIndex in indexes: if boxNameIndex == 'inbox' and outboxName == 'tlblogs': continue From 0bc77af81de63c98cb4aa4aad59e3d5f541dee7c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 25 Aug 2020 21:20:56 +0100 Subject: [PATCH 64/84] Check that post was saved to outbox --- outbox.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/outbox.py b/outbox.py index 8fcf5865b..c64405159 100644 --- a/outbox.py +++ b/outbox.py @@ -188,6 +188,9 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str, postId, postToNickname, domainFull, messageJson, outboxName) + if not savedFilename: + print('WARN: post not saved to outbox ' + outboxName) + return False if messageJson['type'] == 'Create' or \ messageJson['type'] == 'Question' or \ messageJson['type'] == 'Note' or \ @@ -200,6 +203,8 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str, selfActor = \ httpPrefix + '://' + domainFull + '/users/' + postToNickname for boxNameIndex in indexes: + if not boxNameIndex: + continue if boxNameIndex == 'inbox' and outboxName == 'tlblogs': continue # avoid duplicates of the message if already going From 8d6ece6649e64540c45c10d8727b23e308524c6f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 25 Aug 2020 21:29:09 +0100 Subject: [PATCH 65/84] Events --- outbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/outbox.py b/outbox.py index c64405159..e532393f7 100644 --- a/outbox.py +++ b/outbox.py @@ -180,7 +180,7 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str, if messageJson['object']['type'] == 'Article': outboxName = 'tlblogs' elif messageJson['object']['type'] == 'Event': - outboxName = 'tlevent' + outboxName = 'tlevents' savedFilename = \ savePostToBox(baseDir, From ca5bac38165b8f8ee735c6ce70eaa202b6e50ce4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 25 Aug 2020 22:51:30 +0100 Subject: [PATCH 66/84] Events feed --- daemon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index fc1ba1e51..c2e274218 100644 --- a/daemon.py +++ b/daemon.py @@ -4895,7 +4895,7 @@ class PubServer(BaseHTTPRequestHandler): else: # don't need authenticated fetch here because # there is already the authorization check - msg = json.dumps(inboxFeed, + msg = json.dumps(eventsFeed, ensure_ascii=False) msg = msg.encode('utf-8') self._set_headers('application/json', @@ -6347,6 +6347,7 @@ class PubServer(BaseHTTPRequestHandler): if not self.path.endswith('confirm'): self.path = self.path.replace('/outbox/', '/outbox') self.path = self.path.replace('/tlblogs/', '/tlblogs') + self.path = self.path.replace('/tlevents/', '/tlevents') self.path = self.path.replace('/inbox/', '/inbox') self.path = self.path.replace('/shares/', '/shares') self.path = self.path.replace('/sharedInbox/', '/sharedInbox') From 309cc6111c814139f08be328905db85124cd430a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 25 Aug 2020 22:55:38 +0100 Subject: [PATCH 67/84] Bookmarks feed --- daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index c2e274218..8af19676d 100644 --- a/daemon.py +++ b/daemon.py @@ -4801,7 +4801,7 @@ class PubServer(BaseHTTPRequestHandler): else: # don't need authenticated fetch here because # there is already the authorization check - msg = json.dumps(inboxFeed, + msg = json.dumps(bookmarksFeed, ensure_ascii=False) msg = msg.encode('utf-8') self._set_headers('application/json', From 08ff5b420c5afbd4afe0fea86dc56ca7218abda8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 10:29:51 +0100 Subject: [PATCH 68/84] Single events timeline name --- daemon.py | 5 +++-- person.py | 4 ++-- posts.py | 5 +---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/daemon.py b/daemon.py index 8af19676d..98b1623fe 100644 --- a/daemon.py +++ b/daemon.py @@ -4831,6 +4831,9 @@ class PubServer(BaseHTTPRequestHandler): '/events?page=' in self.path: if '/users/' in self.path: if authorized: + if self.path.endswith('/events') or \ + '/events?page=' in self.path: + self.path = self.path.replace('/events', '/tlevents') eventsFeed = \ personBoxJson(self.server.recentPostsCache, self.server.session, @@ -4845,7 +4848,6 @@ class PubServer(BaseHTTPRequestHandler): 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] @@ -4908,7 +4910,6 @@ class PubServer(BaseHTTPRequestHandler): 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: diff --git a/person.py b/person.py index 4f0477bcd..184ea1f80 100644 --- a/person.py +++ b/person.py @@ -600,7 +600,7 @@ def personBoxJson(recentPostsCache: {}, boxname != 'tlblogs' and \ boxname != 'outbox' and boxname != 'moderation' and \ boxname != 'tlbookmarks' and boxname != 'bookmarks' and \ - boxname != 'tlevents' and boxname != 'events': + boxname != 'tlevents': return None if not '/' + boxname in path: @@ -648,7 +648,7 @@ def personBoxJson(recentPostsCache: {}, port, httpPrefix, noOfItems, headerOnly, ocapAlways, pageNumber) - elif boxname == 'tlevents' or boxname == 'events': + elif boxname == 'tlevents': return createEventsTimeline(session, baseDir, nickname, domain, port, httpPrefix, noOfItems, headerOnly, ocapAlways, diff --git a/posts.py b/posts.py index 8ac971683..90e36754d 100644 --- a/posts.py +++ b/posts.py @@ -2832,7 +2832,7 @@ def createBoxIndexed(recentPostsCache: {}, boxname != 'tlblogs' and \ boxname != 'outbox' and boxname != 'tlbookmarks' and \ boxname != 'bookmarks' and \ - boxname != 'tlevents' and boxname != 'events': + boxname != 'tlevents': return None # bookmarks and events timelines are like the inbox @@ -2841,9 +2841,6 @@ def createBoxIndexed(recentPostsCache: {}, if boxname == "tlbookmarks": boxname = "bookmarks" indexBoxName = boxname - elif boxname == "tlevents": - boxname = "events" - indexBoxName = boxname if port: if port != 80 and port != 443: From c4c0cf1679c5cc588e8f8cb23f9ca286820b77b1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 11:03:44 +0100 Subject: [PATCH 69/84] Comments --- webinterface.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/webinterface.py b/webinterface.py index df038b7ee..63d748f73 100644 --- a/webinterface.py +++ b/webinterface.py @@ -4967,11 +4967,15 @@ def htmlTimeline(defaultTimeline: str, if boxName == 'inbox' and pageNumber == 1: if todaysEventsCheck(baseDir, nickname, domain): now = datetime.now() + + # happening today button tlStr += \ '
    \n\n' + + # happening this week button if thisWeeksEventsCheck(baseDir, nickname, domain): tlStr += \ ' 1: tlStr += '
    ' tlStr += '
    \n' + + # show each post in the timeline for item in timelineJson['orderedItems']: if item['type'] == 'Create' or \ item['type'] == 'Announce' or \ From 18f20152d0bce96627c2b37c0266dbd9d74f14cc Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 11:06:16 +0100 Subject: [PATCH 70/84] Single events timeline name --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 63d748f73..f79e0a53b 100644 --- a/webinterface.py +++ b/webinterface.py @@ -4686,7 +4686,7 @@ def htmlTimeline(defaultTimeline: str, sharesButton = 'buttonselectedhighlighted' elif boxName == 'tlbookmarks' or boxName == 'bookmarks': bookmarksButton = 'buttonselected' - elif boxName == 'tlevents' or boxName == 'events': + elif boxName == 'tlevents': eventsButton = 'buttonselected' # get the full domain, including any port number From 0dc6d39de56beb37018cf4cad5683e0877ad3fce Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 11:09:03 +0100 Subject: [PATCH 71/84] Debug --- daemon.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/daemon.py b/daemon.py index 98b1623fe..ba3bc2e99 100644 --- a/daemon.py +++ b/daemon.py @@ -4831,6 +4831,7 @@ class PubServer(BaseHTTPRequestHandler): '/events?page=' in self.path: if '/users/' in self.path: if authorized: + # convert /events to /tlevents if self.path.endswith('/events') or \ '/events?page=' in self.path: self.path = self.path.replace('/events', '/tlevents') @@ -4844,6 +4845,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix, maxPostsInFeed, 'tlevents', authorized, self.server.ocapAlways) + print('eventsFeed: ' + str(eventsFeed)) if eventsFeed: if self._requestHTTP(): nickname = self.path.replace('/users/', '') From 0ac5feb992174fdf76bfd4bb4da23315450cf737 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 11:21:24 +0100 Subject: [PATCH 72/84] Use recent posts cache on other timelines --- person.py | 9 ++++++--- posts.py | 16 ++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/person.py b/person.py index 184ea1f80..f339cd875 100644 --- a/person.py +++ b/person.py @@ -640,7 +640,8 @@ def personBoxJson(recentPostsCache: {}, httpPrefix, noOfItems, headerOnly, ocapAlways, pageNumber) elif boxname == 'dm': - return createDMTimeline(session, baseDir, nickname, domain, port, + return createDMTimeline(recentPostsCache, + session, baseDir, nickname, domain, port, httpPrefix, noOfItems, headerOnly, ocapAlways, pageNumber) elif boxname == 'tlbookmarks' or boxname == 'bookmarks': @@ -649,12 +650,14 @@ def personBoxJson(recentPostsCache: {}, noOfItems, headerOnly, ocapAlways, pageNumber) elif boxname == 'tlevents': - return createEventsTimeline(session, baseDir, nickname, domain, + return createEventsTimeline(recentPostsCache, + session, baseDir, nickname, domain, port, httpPrefix, noOfItems, headerOnly, ocapAlways, pageNumber) elif boxname == 'tlreplies': - return createRepliesTimeline(session, baseDir, nickname, domain, + return createRepliesTimeline(recentPostsCache, + session, baseDir, nickname, domain, port, httpPrefix, noOfItems, headerOnly, ocapAlways, pageNumber) diff --git a/posts.py b/posts.py index 90e36754d..a7f22cba7 100644 --- a/posts.py +++ b/posts.py @@ -2449,30 +2449,34 @@ def createBookmarksTimeline(session, baseDir: str, nickname: str, domain: str, True, ocapAlways, pageNumber) -def createEventsTimeline(session, baseDir: str, nickname: str, domain: str, +def createEventsTimeline(recentPostsCache: {}, + session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, headerOnly: bool, ocapAlways: bool, pageNumber=None) -> {}: - return createBoxIndexed({}, session, baseDir, 'tlevents', + return createBoxIndexed(recentPostsCache, session, baseDir, 'tlevents', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, True, ocapAlways, pageNumber) -def createDMTimeline(session, baseDir: str, nickname: str, domain: str, +def createDMTimeline(recentPostsCache: {}, + session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, headerOnly: bool, ocapAlways: bool, pageNumber=None) -> {}: - return createBoxIndexed({}, session, baseDir, 'dm', nickname, + return createBoxIndexed(recentPostsCache, + session, baseDir, 'dm', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, True, ocapAlways, pageNumber) -def createRepliesTimeline(session, baseDir: str, nickname: str, domain: str, +def createRepliesTimeline(recentPostsCache: {}, + session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, headerOnly: bool, ocapAlways: bool, pageNumber=None) -> {}: - return createBoxIndexed({}, session, baseDir, 'tlreplies', + return createBoxIndexed(recentPostsCache, session, baseDir, 'tlreplies', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, True, ocapAlways, pageNumber) From 8976c99310a7ba4a69eb3b2a2a932aac0ee19210 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 11:25:08 +0100 Subject: [PATCH 73/84] Increase maximum recent posts --- epicyon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epicyon.py b/epicyon.py index e4c7f00b5..276dd32bf 100644 --- a/epicyon.py +++ b/epicyon.py @@ -108,7 +108,7 @@ parser.add_argument('-p', '--port', dest='port', type=int, default=None, help='Port number to run on') parser.add_argument('--postcache', dest='maxRecentPosts', type=int, - default=100, + default=512, help='The maximum number of recent posts to store in RAM') parser.add_argument('--proxy', dest='proxyPort', type=int, default=None, help='Proxy port number to run on') From a9db0b03abfa1f226591e69045c0dc67167a6603 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 12:10:21 +0100 Subject: [PATCH 74/84] Updating of recent posts cache for events timeline --- inbox.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/inbox.py b/inbox.py index 6e7838f74..3945c2167 100644 --- a/inbox.py +++ b/inbox.py @@ -124,13 +124,14 @@ def inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int, session, cachedWebfingers: {}, personCache: {}, nickname: str, domain: str, port: int, postJsonObject: {}, - allowDeletion: bool) -> None: + allowDeletion: bool, boxname: str) -> None: """Converts the json post into html and stores it in a cache This enables the post to be quickly displayed later """ pageNumber = -999 avatarUrl = None - boxName = 'inbox' + if boxname != 'tlevents' and boxname != 'outbox': + boxName = 'inbox' individualPostAsHtml(recentPostsCache, maxRecentPosts, getIconsDir(baseDir), translate, pageNumber, baseDir, session, cachedWebfingers, personCache, @@ -2411,27 +2412,30 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, if not inboxUpdateIndex(boxname, baseDir, handle, destinationFilename, debug): print('ERROR: unable to update ' + boxname + ' index') + else: + if not unitTest: + print('Saving inbox post as html to cache') + htmlCacheStartTime = time.time() + inboxStorePostToHtmlCache(recentPostsCache, + maxRecentPosts, + translate, baseDir, + httpPrefix, + session, cachedWebfingers, + personCache, + handle.split('@')[0], + domain, port, + postJsonObject, + allowDeletion, + boxname) + timeDiff = \ + str(int((time.time() - htmlCacheStartTime) * 1000)) + print('Saved inbox post as html to cache in ' + + timeDiff + ' mS') inboxUpdateCalendar(baseDir, handle, postJsonObject) storeHashTags(baseDir, handle.split('@')[0], postJsonObject) - if not unitTest: - if debug: - print('DEBUG: saving inbox post as html to cache') - htmlCacheStartTime = time.time() - inboxStorePostToHtmlCache(recentPostsCache, maxRecentPosts, - translate, baseDir, httpPrefix, - session, cachedWebfingers, - personCache, - handle.split('@')[0], domain, port, - postJsonObject, allowDeletion) - if debug: - timeDiff = \ - str(int((time.time() - htmlCacheStartTime) * 1000)) - print('DEBUG: saved inbox post as html to cache in ' + - timeDiff + ' mS') - # send the post out to group members if isGroup: sendToGroupMembers(session, baseDir, handle, port, From 673247abf01d06eec038f58c547093995857a598 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 12:19:32 +0100 Subject: [PATCH 75/84] debug flag --- inbox.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/inbox.py b/inbox.py index 3945c2167..3d9ae6e53 100644 --- a/inbox.py +++ b/inbox.py @@ -2414,7 +2414,9 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, print('ERROR: unable to update ' + boxname + ' index') else: if not unitTest: - print('Saving inbox post as html to cache') + if debug: + print('Saving inbox post as html to cache') + htmlCacheStartTime = time.time() inboxStorePostToHtmlCache(recentPostsCache, maxRecentPosts, @@ -2427,10 +2429,13 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, postJsonObject, allowDeletion, boxname) - timeDiff = \ - str(int((time.time() - htmlCacheStartTime) * 1000)) - print('Saved inbox post as html to cache in ' + - timeDiff + ' mS') + if debug: + timeDiff = \ + str(int((time.time() - htmlCacheStartTime) * + 1000)) + print('Saved ' + boxname + + ' post as html to cache in ' + + timeDiff + ' mS') inboxUpdateCalendar(baseDir, handle, postJsonObject) From 593893206cf372c997b7495c6d4e5ca4ccd300a1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 13:12:43 +0100 Subject: [PATCH 76/84] Allow Event activity in timeline --- posts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/posts.py b/posts.py index a7f22cba7..3a07d8679 100644 --- a/posts.py +++ b/posts.py @@ -2784,6 +2784,7 @@ def addPostStringToTimeline(postStr: str, boxname: str, # must be a recognized ActivityPub type if ('"Note"' in postStr or '"EncryptedMessage"' in postStr or + '"Event"' in postStr or '"Article"' in postStr or '"Patch"' in postStr or '"Announce"' in postStr or From 3b7bc19dba28544fe6020af2f814b01c5f00ccc4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 13:42:17 +0100 Subject: [PATCH 77/84] Removing old posts from cache --- utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/utils.py b/utils.py index 4387f5dcb..c4dc8f286 100644 --- a/utils.py +++ b/utils.py @@ -771,6 +771,7 @@ def updateRecentPostsCache(recentPostsCache: {}, maxRecentPosts: int, recentPostsCache['html'][postId] = htmlStr while len(recentPostsCache['html'].items()) > maxRecentPosts: + postId = recentPostsCache['index'][0] recentPostsCache['index'].pop(0) del recentPostsCache['json'][postId] del recentPostsCache['html'][postId] From dcefa585bbb44ba7dcb7b71269b8dfd3f0e85e92 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 18:41:38 +0100 Subject: [PATCH 78/84] Event edit button --- inbox.py | 38 +------------------------------------- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- utils.py | 37 +++++++++++++++++++++++++++++++++++++ webinterface.py | 18 +++++++++++++++++- 18 files changed, 85 insertions(+), 53 deletions(-) diff --git a/inbox.py b/inbox.py index 3d9ae6e53..b11c62cc8 100644 --- a/inbox.py +++ b/inbox.py @@ -10,6 +10,7 @@ import json import os import datetime import time +from utils import isEventPost from utils import removeIdEnding from utils import getProtocolPrefixes from utils import isBlogPost @@ -774,43 +775,6 @@ def receiveUndo(session, baseDir: str, httpPrefix: str, return False -def isEventPost(messageJson: {}) -> bool: - """Is the given post a mobilizon-type event activity? - See https://framagit.org/framasoft/mobilizon/-/blob/ - master/lib/federation/activity_stream/converter/event.ex - """ - if not messageJson.get('id'): - return False - if not messageJson.get('actor'): - return False - if not messageJson.get('object'): - return False - if not isinstance(messageJson['object'], dict): - return False - if not messageJson['object'].get('type'): - return False - if messageJson['object']['type'] != 'Event': - return False - print('Event arriving') - if not messageJson['object'].get('startTime'): - print('No event start time') - return False - if not messageJson['object'].get('actor'): - print('No event actor') - return False - if not messageJson['object'].get('content'): - print('No event content') - return False - if not messageJson['object'].get('name'): - print('No event name') - return False - if not messageJson['object'].get('uuid'): - print('No event UUID') - return False - print('Event detected') - return True - - def receiveEventPost(recentPostsCache: {}, session, baseDir: str, httpPrefix: str, domain: str, port: int, sendThreads: [], postLog: [], cachedWebfingers: {}, diff --git a/translations/ar.json b/translations/ar.json index 2e5b5b3ed..14f9ced66 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -280,5 +280,6 @@ "Maximum attendees": "الحد الأقصى للحضور", "Ticket URL": "عنوان URL للتذكرة", "Create a new event": "أنشئ حدثًا جديدًا", - "Moderation policy or code of conduct": "سياسة الوسطية أو قواعد السلوك" + "Moderation policy or code of conduct": "سياسة الوسطية أو قواعد السلوك", + "Edit event": "تحرير الحدث" } diff --git a/translations/ca.json b/translations/ca.json index 819367002..e55e2c0e5 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -280,5 +280,6 @@ "Maximum attendees": "Màxim d’assistents", "Ticket URL": "URL de l'entrada", "Create a new event": "Creeu un esdeveniment nou", - "Moderation policy or code of conduct": "Política de moderació o codi de conducta" + "Moderation policy or code of conduct": "Política de moderació o codi de conducta", + "Edit event": "Edita l’esdeveniment" } diff --git a/translations/cy.json b/translations/cy.json index f02707e16..7b93d64bf 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -280,5 +280,6 @@ "Maximum attendees": "Uchafswm mynychwyr", "Ticket URL": "URL y tocyn", "Create a new event": "Creu digwyddiad newydd", - "Moderation policy or code of conduct": "Polisi cymedroli neu god ymddygiad" + "Moderation policy or code of conduct": "Polisi cymedroli neu god ymddygiad", + "Edit event": "Golygu digwyddiad" } diff --git a/translations/de.json b/translations/de.json index 3c28b13b8..930c17b6a 100644 --- a/translations/de.json +++ b/translations/de.json @@ -280,5 +280,6 @@ "Maximum attendees": "Maximale Teilnehmerzahl", "Ticket URL": "Ticket URL", "Create a new event": "Erstellen Sie ein neues Ereignis", - "Moderation policy or code of conduct": "Moderationsrichtlinie oder Verhaltenskodex" + "Moderation policy or code of conduct": "Moderationsrichtlinie oder Verhaltenskodex", + "Edit event": "Ereignis bearbeiten" } diff --git a/translations/en.json b/translations/en.json index 243a9c840..8c0e8b6dc 100644 --- a/translations/en.json +++ b/translations/en.json @@ -280,5 +280,6 @@ "Maximum attendees": "Maximum attendees", "Ticket URL": "Ticket URL", "Create a new event": "Create a new event", - "Moderation policy or code of conduct": "Moderation policy or code of conduct" + "Moderation policy or code of conduct": "Moderation policy or code of conduct", + "Edit event": "Edit event" } diff --git a/translations/es.json b/translations/es.json index c1049c4e2..5edcc8859 100644 --- a/translations/es.json +++ b/translations/es.json @@ -280,5 +280,6 @@ "Maximum attendees": "Asistentes máximos", "Ticket URL": "URL del ticket", "Create a new event": "Crea un nuevo evento", - "Moderation policy or code of conduct": "Política de moderación o código de conducta" + "Moderation policy or code of conduct": "Política de moderación o código de conducta", + "Edit event": "Editar evento" } diff --git a/translations/fr.json b/translations/fr.json index d162a533f..ca67d4197 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -280,5 +280,6 @@ "Maximum attendees": "Nombre maximum de participants", "Ticket URL": "URL du ticket", "Create a new event": "Créer un nouvel événement", - "Moderation policy or code of conduct": "Politique de modération ou code de conduite" + "Moderation policy or code of conduct": "Politique de modération ou code de conduite", + "Edit event": "Modifier l'événement" } diff --git a/translations/ga.json b/translations/ga.json index 02b510ae8..a746ae4d2 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -280,5 +280,6 @@ "Maximum attendees": "Uasmhéid freastail", "Ticket URL": "URL na dticéad", "Create a new event": "Cruthaigh imeacht nua", - "Moderation policy or code of conduct": "Beartas modhnóireachta nó cód iompair" + "Moderation policy or code of conduct": "Beartas modhnóireachta nó cód iompair", + "Edit event": "Cuir imeacht in eagar" } diff --git a/translations/hi.json b/translations/hi.json index 21e9ef0e3..ac11254a7 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -280,5 +280,6 @@ "Maximum attendees": "अधिकतम उपस्थित", "Ticket URL": "टिकट URL", "Create a new event": "एक नई घटना बनाएँ", - "Moderation policy or code of conduct": "मॉडरेशन पॉलिसी या आचार संहिता" + "Moderation policy or code of conduct": "मॉडरेशन पॉलिसी या आचार संहिता", + "Edit event": "घटना संपादित करें" } diff --git a/translations/it.json b/translations/it.json index 18a8f1254..72e8efe3a 100644 --- a/translations/it.json +++ b/translations/it.json @@ -280,5 +280,6 @@ "Maximum attendees": "Numero massimo di partecipanti", "Ticket URL": "URL del biglietto", "Create a new event": "Crea un nuovo evento", - "Moderation policy or code of conduct": "Politica di moderazione o codice di condotta" + "Moderation policy or code of conduct": "Politica di moderazione o codice di condotta", + "Edit event": "Modifica evento" } diff --git a/translations/ja.json b/translations/ja.json index 01a57ec6f..13fa27be8 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -280,5 +280,6 @@ "Maximum attendees": "最大参加者", "Ticket URL": "チケットURL", "Create a new event": "新しいイベントを作成する", - "Moderation policy or code of conduct": "モデレートポリシーまたは行動規範" + "Moderation policy or code of conduct": "モデレートポリシーまたは行動規範", + "Edit event": "イベントを編集" } diff --git a/translations/oc.json b/translations/oc.json index d8610fa94..3642f1171 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -276,5 +276,6 @@ "Maximum attendees": "Maximum attendees", "Ticket URL": "Ticket URL", "Create a new event": "Create a new event", - "Moderation policy or code of conduct": "Moderation policy or code of conduct" + "Moderation policy or code of conduct": "Moderation policy or code of conduct", + "Edit event": "Edit event" } diff --git a/translations/pt.json b/translations/pt.json index 82de1c0cc..08fd34d55 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -280,5 +280,6 @@ "Maximum attendees": "Máximo de participantes", "Ticket URL": "URL do bilhete", "Create a new event": "Crie um novo evento", - "Moderation policy or code of conduct": "Política de moderação ou código de conduta" + "Moderation policy or code of conduct": "Política de moderação ou código de conduta", + "Edit event": "Editar evento" } diff --git a/translations/ru.json b/translations/ru.json index 42aed9c04..74456e435 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -280,5 +280,6 @@ "Maximum attendees": "Максимальное количество участников", "Ticket URL": "URL билета", "Create a new event": "Создать новое мероприятие", - "Moderation policy or code of conduct": "Политика модерации или кодекс поведения" + "Moderation policy or code of conduct": "Политика модерации или кодекс поведения", + "Edit event": "Изменить мероприятие" } diff --git a/translations/zh.json b/translations/zh.json index 6665093c7..52e93e6ef 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -280,5 +280,6 @@ "Maximum attendees": "参加人数上限", "Ticket URL": "工单URL", "Create a new event": "建立新活动", - "Moderation policy or code of conduct": "审核政策或行为准则" + "Moderation policy or code of conduct": "审核政策或行为准则", + "Edit event": "编辑活动" } diff --git a/utils.py b/utils.py index c4dc8f286..073e820a8 100644 --- a/utils.py +++ b/utils.py @@ -807,6 +807,43 @@ def mergeDicts(dict1: {}, dict2: {}) -> {}: return res +def isEventPost(messageJson: {}) -> bool: + """Is the given post a mobilizon-type event activity? + See https://framagit.org/framasoft/mobilizon/-/blob/ + master/lib/federation/activity_stream/converter/event.ex + """ + if not messageJson.get('id'): + return False + if not messageJson.get('actor'): + return False + if not messageJson.get('object'): + return False + if not isinstance(messageJson['object'], dict): + return False + if not messageJson['object'].get('type'): + return False + if messageJson['object']['type'] != 'Event': + return False + print('Event arriving') + if not messageJson['object'].get('startTime'): + print('No event start time') + return False + if not messageJson['object'].get('actor'): + print('No event actor') + return False + if not messageJson['object'].get('content'): + print('No event content') + return False + if not messageJson['object'].get('name'): + print('No event name') + return False + if not messageJson['object'].get('uuid'): + print('No event UUID') + return False + print('Event detected') + return True + + def isBlogPost(postJsonObject: {}) -> bool: """Is the given post a blog post? """ diff --git a/webinterface.py b/webinterface.py index f79e0a53b..e71fd8ac4 100644 --- a/webinterface.py +++ b/webinterface.py @@ -29,6 +29,7 @@ from utils import removeIdEnding from utils import getProtocolPrefixes from utils import getFileCaseInsensitive from utils import searchBoxPosts +from utils import isEventPost from utils import isBlogPost from utils import updateRecentPostsCache from utils import getNicknameFromActor @@ -4019,20 +4020,35 @@ def individualPostAsHtml(recentPostsCache: {}, maxRecentPosts: int, translate['Reply to this post'] + \ ' |" src="/' + iconsDir + '/reply.png"/>\n' + isEvent = isEventPost(postJsonObject) + editStr = '' if fullDomain + '/users/' + nickname in postJsonObject['actor']: if '/statuses/' in postJsonObject['object']['id']: if isBlogPost(postJsonObject): + blogPostId = postJsonObject['object']['id'] editStr += \ '' + \ '' + \
                     translate['Edit blog post'] + \
                     ' |\n' + elif isEvent: + eventPostId = postJsonObject['object']['id'] + editStr += \ + '' + \ + '' + \
+                    translate['Edit event'] + \
+                    ' |\n' announceStr = '' if not isModerationPost and showRepeatIcon: From d826f69a2e1ab68de915d9cf82d7a1b7f560876a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 19:06:02 +0100 Subject: [PATCH 79/84] Placeholder for editing event --- daemon.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/daemon.py b/daemon.py index ba3bc2e99..9e0c73a05 100644 --- a/daemon.py +++ b/daemon.py @@ -3599,6 +3599,40 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return + # Edit an event + if authorized and \ + '/tlevents' in self.path and \ + '?editeventpost=' in self.path and \ + '?actor=' in self.path: + messageId = self.path.split('?editeventpost=')[1] + if '?' in messageId: + messageId = messageId.split('?')[0] + actor = self.path.split('?actor=')[1] + if '?' in actor: + actor = actor.split('?')[0] + nickname = getNicknameFromActor(self.path) + if nickname == actor: + postUrl = \ + self.server.httpPrefix + '://' + \ + self.server.domainFull + '/users/' + nickname + \ + '/statuses/' + messageId + msg = None + # TODO + # htmlEditEvent(self.server.mediaInstance, + # self.server.translate, + # self.server.baseDir, + # self.server.httpPrefix, + # self.path, + # nickname, self.server.domain, + # postUrl) + if msg: + msg = msg.encode('utf-8') + self._set_headers('text/html', len(msg), + cookie, callingDomain) + self._write(msg) + self.server.GETbusy = False + return + # edit profile in web interface if '/users/' in self.path and self.path.endswith('/editprofile'): msg = htmlEditProfile(self.server.translate, From 0fce25806861bd86465fb9138df5a3cfc36f51d4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 26 Aug 2020 19:21:57 +0100 Subject: [PATCH 80/84] Test for translations files --- tests.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests.py b/tests.py index a514ad8e8..59b97c462 100644 --- a/tests.py +++ b/tests.py @@ -2055,8 +2055,32 @@ def testValidContentWarning(): assert resultStr == 'Invalid content warning' +def testTranslations(): + print('testTranslations') + languagesStr = ('ar', 'ca', 'cy', 'de', 'es', 'fr', 'ga', + 'hi', 'it', 'ja', 'oc', 'pt', 'ru', 'zh') + + # load all translations into a dict + langDict = {} + for lang in languagesStr: + langJson = loadJson('translations/' + lang + '.json') + assert langJson + langDict[lang] = langJson + + # load english translations + translationsJson = loadJson('translations/en.json') + # test each english string exists in the other language files + for englishStr, translatedStr in translationsJson.items(): + for lang in languagesStr: + langJson = langDict[lang] + if not langJson.get(englishStr): + print(englishStr + ' is missing from ' + lang + '.json') + assert langJson.get(englishStr) + + def runAllTests(): print('Running tests...') + testTranslations() testValidContentWarning() testRemoveIdEnding() testJsonPostAllowsComments() From 8ff510e79104b72acb1e3acc95992311919f8d68 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 27 Aug 2020 10:19:32 +0100 Subject: [PATCH 81/84] Optionally notify when posts are liked --- daemon.py | 14 ++++++++++++++ inbox.py | 4 +++- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- webinterface.py | 8 ++++++++ 18 files changed, 55 insertions(+), 16 deletions(-) diff --git a/daemon.py b/daemon.py index 9e0c73a05..4300d4d47 100644 --- a/daemon.py +++ b/daemon.py @@ -7116,6 +7116,20 @@ class PubServer(BaseHTTPRequestHandler): if not removeTwitterActive: if os.path.isfile(removeTwitterFilename): os.remove(removeTwitterFilename) + # notify about new Likes + notifyLikesFilename = \ + self.server.baseDir + '/accounts/' + \ + nickname + '@' + self.server.domain + \ + '/.notifyLikes' + notifyLikesActive = False + if fields.get('notifyLikes'): + if fields['notifyLikes'] == 'on': + notifyLikesActive = True + with open(notifyLikesFilename, "w") as rFile: + rFile.write('\n') + if not notifyLikesActive: + if os.path.isfile(notifyLikesFilename): + os.remove(notifyLikesFilename) # this account is a bot if fields.get('isBot'): if fields['isBot'] == 'on': diff --git a/inbox.py b/inbox.py index b11c62cc8..f5c2138b8 100644 --- a/inbox.py +++ b/inbox.py @@ -1847,8 +1847,10 @@ def likeNotify(baseDir: str, domain: str, onionDomain: str, return accountDir = baseDir + '/accounts/' + handle - if not os.path.isdir(accountDir): + notifyLikesEnabledFilename = accountDir + '/.notifyLikes' + if not os.path.isfile(notifyLikesEnabledFilename): return + likeFile = accountDir + '/.newLike' if os.path.isfile(likeFile): if '##sent##' not in open(likeFile).read(): diff --git a/translations/ar.json b/translations/ar.json index 14f9ced66..b6223d1d1 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -281,5 +281,6 @@ "Ticket URL": "عنوان URL للتذكرة", "Create a new event": "أنشئ حدثًا جديدًا", "Moderation policy or code of conduct": "سياسة الوسطية أو قواعد السلوك", - "Edit event": "تحرير الحدث" + "Edit event": "تحرير الحدث", + "Notify when posts are liked": "يخطر عندما يتم اعجاب المشاركات" } diff --git a/translations/ca.json b/translations/ca.json index e55e2c0e5..343576942 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -281,5 +281,6 @@ "Ticket URL": "URL de l'entrada", "Create a new event": "Creeu un esdeveniment nou", "Moderation policy or code of conduct": "Política de moderació o codi de conducta", - "Edit event": "Edita l’esdeveniment" + "Edit event": "Edita l’esdeveniment", + "Notify when posts are liked": "Notifiqueu-ho quan us agradin les publicacions" } diff --git a/translations/cy.json b/translations/cy.json index 7b93d64bf..631892be2 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -281,5 +281,6 @@ "Ticket URL": "URL y tocyn", "Create a new event": "Creu digwyddiad newydd", "Moderation policy or code of conduct": "Polisi cymedroli neu god ymddygiad", - "Edit event": "Golygu digwyddiad" + "Edit event": "Golygu digwyddiad", + "Notify when posts are liked": "Hysbysu pryd mae swyddi'n cael eu hoffi" } diff --git a/translations/de.json b/translations/de.json index 930c17b6a..99794eb22 100644 --- a/translations/de.json +++ b/translations/de.json @@ -281,5 +281,6 @@ "Ticket URL": "Ticket URL", "Create a new event": "Erstellen Sie ein neues Ereignis", "Moderation policy or code of conduct": "Moderationsrichtlinie oder Verhaltenskodex", - "Edit event": "Ereignis bearbeiten" + "Edit event": "Ereignis bearbeiten", + "Notify when posts are liked": "Benachrichtigen, wenn Beiträge gefallen" } diff --git a/translations/en.json b/translations/en.json index 8c0e8b6dc..2476be31e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -281,5 +281,6 @@ "Ticket URL": "Ticket URL", "Create a new event": "Create a new event", "Moderation policy or code of conduct": "Moderation policy or code of conduct", - "Edit event": "Edit event" + "Edit event": "Edit event", + "Notify when posts are liked": "Notify when posts are liked" } diff --git a/translations/es.json b/translations/es.json index 5edcc8859..a6969342a 100644 --- a/translations/es.json +++ b/translations/es.json @@ -281,5 +281,6 @@ "Ticket URL": "URL del ticket", "Create a new event": "Crea un nuevo evento", "Moderation policy or code of conduct": "Política de moderación o código de conducta", - "Edit event": "Editar evento" + "Edit event": "Editar evento", + "Notify when posts are liked": "Notificar cuando les gusten las publicaciones" } diff --git a/translations/fr.json b/translations/fr.json index ca67d4197..b113e888b 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -281,5 +281,6 @@ "Ticket URL": "URL du ticket", "Create a new event": "Créer un nouvel événement", "Moderation policy or code of conduct": "Politique de modération ou code de conduite", - "Edit event": "Modifier l'événement" + "Edit event": "Modifier l'événement", + "Notify when posts are liked": "Notifier lorsque les messages sont aimés" } diff --git a/translations/ga.json b/translations/ga.json index a746ae4d2..04ad6f2f3 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -281,5 +281,6 @@ "Ticket URL": "URL na dticéad", "Create a new event": "Cruthaigh imeacht nua", "Moderation policy or code of conduct": "Beartas modhnóireachta nó cód iompair", - "Edit event": "Cuir imeacht in eagar" + "Edit event": "Cuir imeacht in eagar", + "Notify when posts are liked": "Cuir in iúl cathain is maith poist" } diff --git a/translations/hi.json b/translations/hi.json index ac11254a7..a8f984adb 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -281,5 +281,6 @@ "Ticket URL": "टिकट URL", "Create a new event": "एक नई घटना बनाएँ", "Moderation policy or code of conduct": "मॉडरेशन पॉलिसी या आचार संहिता", - "Edit event": "घटना संपादित करें" + "Edit event": "घटना संपादित करें", + "Notify when posts are liked": "पोस्ट पसंद आने पर सूचित करें" } diff --git a/translations/it.json b/translations/it.json index 72e8efe3a..35443ffa4 100644 --- a/translations/it.json +++ b/translations/it.json @@ -281,5 +281,6 @@ "Ticket URL": "URL del biglietto", "Create a new event": "Crea un nuovo evento", "Moderation policy or code of conduct": "Politica di moderazione o codice di condotta", - "Edit event": "Modifica evento" + "Edit event": "Modifica evento", + "Notify when posts are liked": "Avvisa quando i post sono piaciuti" } diff --git a/translations/ja.json b/translations/ja.json index 13fa27be8..a11bf5ea1 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -281,5 +281,6 @@ "Ticket URL": "チケットURL", "Create a new event": "新しいイベントを作成する", "Moderation policy or code of conduct": "モデレートポリシーまたは行動規範", - "Edit event": "イベントを編集" + "Edit event": "イベントを編集", + "Notify when posts are liked": "投稿が高く評価されたときに通知する" } diff --git a/translations/oc.json b/translations/oc.json index 3642f1171..2a020527c 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -277,5 +277,6 @@ "Ticket URL": "Ticket URL", "Create a new event": "Create a new event", "Moderation policy or code of conduct": "Moderation policy or code of conduct", - "Edit event": "Edit event" + "Edit event": "Edit event", + "Notify when posts are liked": "Notify when posts are liked" } diff --git a/translations/pt.json b/translations/pt.json index 08fd34d55..27a0466bb 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -281,5 +281,6 @@ "Ticket URL": "URL do bilhete", "Create a new event": "Crie um novo evento", "Moderation policy or code of conduct": "Política de moderação ou código de conduta", - "Edit event": "Editar evento" + "Edit event": "Editar evento", + "Notify when posts are liked": "Notificar quando as postagens forem curtidas" } diff --git a/translations/ru.json b/translations/ru.json index 74456e435..2b5d61d44 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -281,5 +281,6 @@ "Ticket URL": "URL билета", "Create a new event": "Создать новое мероприятие", "Moderation policy or code of conduct": "Политика модерации или кодекс поведения", - "Edit event": "Изменить мероприятие" + "Edit event": "Изменить мероприятие", + "Notify when posts are liked": "Уведомлять, когда публикации нравятся" } diff --git a/translations/zh.json b/translations/zh.json index 52e93e6ef..3ce3f2d7d 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -281,5 +281,6 @@ "Ticket URL": "工单URL", "Create a new event": "建立新活动", "Moderation policy or code of conduct": "审核政策或行为准则", - "Edit event": "编辑活动" + "Edit event": "编辑活动", + "Notify when posts are liked": "通知喜欢的帖子" } diff --git a/webinterface.py b/webinterface.py index e71fd8ac4..a2e451b4d 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1083,6 +1083,7 @@ def htmlEditProfile(translate: {}, baseDir: str, path: str, isGroup = '' followDMs = '' removeTwitter = '' + notifyLikes = '' mediaInstanceStr = '' displayNickname = nickname bioStr = '' @@ -1130,6 +1131,9 @@ def htmlEditProfile(translate: {}, baseDir: str, path: str, if os.path.isfile(baseDir + '/accounts/' + nickname + '@' + domain + '/.removeTwitter'): removeTwitter = 'checked' + if os.path.isfile(baseDir + '/accounts/' + + nickname + '@' + domain + '/.notifyLikes'): + notifyLikes = 'checked' mediaInstance = getConfigParam(baseDir, "mediaInstance") if mediaInstance: @@ -1465,6 +1469,10 @@ def htmlEditProfile(translate: {}, baseDir: str, path: str, ' ' + \ translate['This is a media instance'] + '
    \n' + editProfileForm += \ + ' ' + \ + translate['Notify when posts are liked'] + '
    \n' editProfileForm += \ '