diff --git a/blog.py b/blog.py index 0666e8677..a136d36d0 100644 --- a/blog.py +++ b/blog.py @@ -420,7 +420,7 @@ def htmlBlogPost(session, authorized: bool, postJsonObject: {}, peertubeInstances: [], systemLanguage: str, personCache: {}, - debug: bool) -> str: + debug: bool, contentLicenseUrl: str) -> str: """Returns a html blog post """ blogStr = '' @@ -431,12 +431,19 @@ def htmlBlogPost(session, authorized: bool, instanceTitle = \ getConfigParam(baseDir, 'instanceTitle') published = postJsonObject['object']['published'] + modified = published + if postJsonObject['object'].get('updated'): + modified = postJsonObject['object']['updated'] title = postJsonObject['object']['summary'] + url = '' + if postJsonObject['object'].get('url'): + url = postJsonObject['object']['url'] snippet = _getSnippetFromBlogContent(postJsonObject, systemLanguage) blogStr = htmlHeaderWithBlogMarkup(cssFilename, instanceTitle, httpPrefix, domainFull, nickname, - systemLanguage, published, - title, snippet) + systemLanguage, published, modified, + title, snippet, translate, url, + contentLicenseUrl) _htmlBlogRemoveCwButton(blogStr, translate) blogStr += _htmlBlogPostContent(debug, session, authorized, baseDir, diff --git a/daemon.py b/daemon.py index 4a5f14d94..05795bdcf 100644 --- a/daemon.py +++ b/daemon.py @@ -508,7 +508,8 @@ class PubServer(BaseHTTPRequestHandler): location, False, self.server.systemLanguage, conversationId, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: # name field contains the answer messageJson['object']['name'] = answer @@ -1281,7 +1282,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.maxLikeCount, self.server.maxRecentPosts, self.server.CWlists, - self.server.listsEnabled) + self.server.listsEnabled, + self.server.contentLicenseUrl) def _getOutboxThreadIndex(self, nickname: str, maxOutboxThreadsPerAccount: int) -> int: @@ -4357,7 +4359,7 @@ class PubServer(BaseHTTPRequestHandler): domain: str, domainFull: str, onionDomain: str, i2pDomain: str, debug: bool, allowLocalNetworkAccess: bool, - systemLanguage: str) -> None: + systemLanguage: str, contentLicenseUrl: str) -> None: """Updates your user profile after editing via the Edit button on the profile screen """ @@ -4504,7 +4506,8 @@ class PubServer(BaseHTTPRequestHandler): if self.server.lowBandwidth: convertImageToLowBandwidth(filename) processMetaData(baseDir, nickname, domain, - filename, postImageFilename, city) + filename, postImageFilename, city, + contentLicenseUrl) if os.path.isfile(postImageFilename): print('profile update POST ' + mType + ' image, zip or font saved to ' + @@ -4900,6 +4903,24 @@ class PubServer(BaseHTTPRequestHandler): setConfigParam(baseDir, 'libretranslateApiKey', '') + # change instance short description + if fields.get('contentLicenseUrl'): + if fields['contentLicenseUrl'] != \ + self.server.contentLicenseUrl: + licenseStr = fields['contentLicenseUrl'] + setConfigParam(baseDir, + 'contentLicenseUrl', + licenseStr) + self.server.contentLicenseUrl = \ + licenseStr + else: + licenseStr = \ + 'https://creativecommons.org/licenses/by/4.0' + setConfigParam(baseDir, + 'contentLicenseUrl', + licenseStr) + self.server.contentLicenseUrl = licenseStr + # change instance short description currInstanceDescriptionShort = \ getConfigParam(baseDir, @@ -6049,9 +6070,14 @@ class PubServer(BaseHTTPRequestHandler): "sizes": "144x144" }, { - "src": "/logo152.png", + "src": "/logo150.png", "type": "image/png", - "sizes": "152x152" + "sizes": "150x150" + }, + { + "src": "/apple-touch-icon.png", + "type": "image/png", + "sizes": "180x180" }, { "src": "/logo192.png", @@ -6092,6 +6118,33 @@ class PubServer(BaseHTTPRequestHandler): '_GET', '_progressiveWebAppManifest', self.server.debug) + def _browserConfig(self, callingDomain: str, GETstartTime) -> None: + """Used by MS Windows to put an icon on the desktop if you + link to a website + """ + xmlStr = \ + '\n' + \ + '\n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' #eeeeee\n' + \ + ' \n' + \ + ' \n' + \ + '' + + msg = json.dumps(xmlStr, + ensure_ascii=False).encode('utf-8') + msglen = len(msg) + self._set_headers('application/xrd+xml', msglen, + None, callingDomain, False) + self._write(msg) + if self.server.debug: + print('Sent browserconfig: ' + callingDomain) + fitnessPerformance(GETstartTime, self.server.fitness, + '_GET', '_browserConfig', + self.server.debug) + def _getFavicon(self, callingDomain: str, baseDir: str, debug: bool, favFilename: str) -> None: @@ -8726,7 +8779,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.sharedItemsFederatedDomains, rolesList, None, None, self.server.CWlists, - self.server.listsEnabled) + self.server.listsEnabled, + self.server.contentLicenseUrl) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8841,7 +8895,8 @@ class PubServer(BaseHTTPRequestHandler): skills, None, None, self.server.CWlists, - self.server.listsEnabled) + self.server.listsEnabled, + self.server.contentLicenseUrl) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -10834,7 +10889,8 @@ class PubServer(BaseHTTPRequestHandler): shares, pageNumber, sharesPerPage, self.server.CWlists, - self.server.listsEnabled) + self.server.listsEnabled, + self.server.contentLicenseUrl) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -10926,6 +10982,8 @@ class PubServer(BaseHTTPRequestHandler): city = getSpoofedCity(self.server.city, baseDir, nickname, domain) + contentLicenseUrl = \ + self.server.contentLicenseUrl msg = \ htmlProfile(self.server.signingPrivateKeyPem, self.server.rssIconAtTop, @@ -10960,7 +11018,8 @@ class PubServer(BaseHTTPRequestHandler): pageNumber, followsPerPage, self.server.CWlists, - self.server.listsEnabled).encode('utf-8') + self.server.listsEnabled, + contentLicenseUrl).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, callingDomain, False) @@ -11050,6 +11109,8 @@ class PubServer(BaseHTTPRequestHandler): city = getSpoofedCity(self.server.city, baseDir, nickname, domain) + contentLicenseUrl = \ + self.server.contentLicenseUrl msg = \ htmlProfile(self.server.signingPrivateKeyPem, self.server.rssIconAtTop, @@ -11085,7 +11146,8 @@ class PubServer(BaseHTTPRequestHandler): pageNumber, followsPerPage, self.server.CWlists, - self.server.listsEnabled).encode('utf-8') + self.server.listsEnabled, + contentLicenseUrl).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, callingDomain, False) @@ -11226,7 +11288,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.sharedItemsFederatedDomains, None, None, None, self.server.CWlists, - self.server.listsEnabled).encode('utf-8') + self.server.listsEnabled, + self.server.contentLicenseUrl).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, callingDomain, False) @@ -12239,6 +12302,17 @@ class PubServer(BaseHTTPRequestHandler): uaStr = self.headers['User-agent'] return uaStr + def _permittedCrawlerPath(self, path: str) -> bool: + """Is the given path permitted to be crawled by a search engine? + this should only allow through basic information, such as nodeinfo + """ + if path == '/' or path == '/about' or path == '/login' or \ + path.startswith('/api/') or \ + path.startswith('/nodeinfo/') or \ + path.startswith('/blog/'): + return True + return False + def do_GET(self): callingDomain = self.server.domainFull @@ -12267,9 +12341,10 @@ class PubServer(BaseHTTPRequestHandler): uaStr = self._getUserAgent() - if self._blockedUserAgent(callingDomain, uaStr): - self._400() - return + if not self._permittedCrawlerPath(self.path): + if self._blockedUserAgent(callingDomain, uaStr): + self._400() + return refererDomain = self._getRefererDomain(uaStr) @@ -12401,6 +12476,11 @@ class PubServer(BaseHTTPRequestHandler): else: self.path = '/' + if '/browserconfig.xml' in self.path: + if self._hasAccept(callingDomain): + self._browserConfig(callingDomain, GETstartTime) + return + # default newswire favicon, for links to sites which # have no favicon if 'newswire_favicon.ico' in self.path: @@ -13089,7 +13169,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.systemLanguage, self.server.personCache, - self.server.debug) + self.server.debug, + self.server.contentLicenseUrl) if msg is not None: msg = msg.encode('utf-8') msglen = len(msg) @@ -13482,10 +13563,11 @@ class PubServer(BaseHTTPRequestHandler): self.path == '/logo96.png' or \ self.path == '/logo128.png' or \ self.path == '/logo144.png' or \ - self.path == '/logo152.png' or \ + self.path == '/logo150.png' or \ self.path == '/logo192.png' or \ self.path == '/logo256.png' or \ - self.path == '/logo512.png': + self.path == '/logo512.png' or \ + self.path == '/apple-touch-icon.png': mediaFilename = \ self.server.baseDir + '/img' + self.path if os.path.isfile(mediaFilename): @@ -13660,6 +13742,7 @@ class PubServer(BaseHTTPRequestHandler): GETstartTime) return + # show a background image on the login or person options page if '-background.' in self.path: if self._showBackgroundImage(callingDomain, self.path, self.server.baseDir, @@ -13849,6 +13932,7 @@ class PubServer(BaseHTTPRequestHandler): '_GET', 'login shown done', self.server.debug) + # the newswire screen on mobile if htmlGET and self.path.startswith('/users/') and \ self.path.endswith('/newswiremobile'): if (authorized or @@ -14618,6 +14702,7 @@ class PubServer(BaseHTTPRequestHandler): '_GET', 'post replies done', self.server.debug) + # roles on profile screen if self.path.endswith('/roles') and usersInPath: if self._showRoles(authorized, callingDomain, self.path, @@ -15195,7 +15280,8 @@ class PubServer(BaseHTTPRequestHandler): def _receiveNewPostProcess(self, postType: str, path: str, headers: {}, length: int, postBytes, boundary: str, callingDomain: str, cookie: str, - authorized: bool) -> int: + authorized: bool, + contentLicenseUrl: str) -> int: # Note: this needs to happen synchronously # 0=this is not a new post # 1=new post success @@ -15272,7 +15358,8 @@ class PubServer(BaseHTTPRequestHandler): convertImageToLowBandwidth(filename) processMetaData(self.server.baseDir, nickname, self.server.domain, - filename, postImageFilename, city) + filename, postImageFilename, city, + contentLicenseUrl) if os.path.isfile(postImageFilename): print('POST media saved to ' + postImageFilename) else: @@ -15399,7 +15486,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location'], False, self.server.systemLanguage, conversationId, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: if fields['schedulePost']: return 1 @@ -15481,7 +15569,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location'], self.server.systemLanguage, conversationId, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: if fields['schedulePost']: return 1 @@ -15573,7 +15662,8 @@ class PubServer(BaseHTTPRequestHandler): attachmentMediaType, imgDescription, city, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) replaceYouTube(postJsonObject, self.server.YTReplacementDomain, @@ -15631,7 +15721,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location'], self.server.systemLanguage, conversationId, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: if fields['schedulePost']: return 1 @@ -15682,7 +15773,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location'], self.server.systemLanguage, conversationId, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: if fields['schedulePost']: return 1 @@ -15737,7 +15829,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location'], self.server.systemLanguage, conversationId, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: if fields['schedulePost']: return 1 @@ -15790,7 +15883,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location'], self.server.systemLanguage, conversationId, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: if fields['schedulePost']: return 1 @@ -15826,7 +15920,8 @@ class PubServer(BaseHTTPRequestHandler): city, self.server.debug, fields['subject'], self.server.systemLanguage, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: if self._postToOutbox(messageJson, self.server.projectVersion, @@ -15867,7 +15962,8 @@ class PubServer(BaseHTTPRequestHandler): fields['subject'], intDuration, self.server.systemLanguage, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if messageJson: if self.server.debug: print('DEBUG: new Question') @@ -15939,7 +16035,8 @@ class PubServer(BaseHTTPRequestHandler): city, itemPrice, itemCurrency, self.server.systemLanguage, self.server.translate, sharesFileType, - self.server.lowBandwidth) + self.server.lowBandwidth, + self.server.contentLicenseUrl) if filename: if os.path.isfile(filename): try: @@ -15954,7 +16051,8 @@ class PubServer(BaseHTTPRequestHandler): def _receiveNewPost(self, postType: str, path: str, callingDomain: str, cookie: str, - authorized: bool) -> int: + authorized: bool, + contentLicenseUrl: str) -> int: """A new post has been created This creates a thread to send the new post """ @@ -16056,7 +16154,8 @@ class PubServer(BaseHTTPRequestHandler): path, headers, length, postBytes, boundary, callingDomain, cookie, - authorized) + authorized, + contentLicenseUrl) return pageNumber def _cryptoAPIreadHandle(self): @@ -16353,7 +16452,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.onionDomain, self.server.i2pDomain, self.server.debug, self.server.allowLocalNetworkAccess, - self.server.systemLanguage) + self.server.systemLanguage, + self.server.contentLicenseUrl) return if authorized and self.path.endswith('/linksdata'): @@ -16713,7 +16813,8 @@ class PubServer(BaseHTTPRequestHandler): pageNumber = \ self._receiveNewPost(currPostType, self.path, callingDomain, cookie, - authorized) + authorized, + self.server.contentLicenseUrl) if pageNumber: print(currPostType + ' post received') nickname = self.path.split('/users/')[1] @@ -17126,7 +17227,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None: break -def runDaemon(listsEnabled: str, +def runDaemon(contentLicenseUrl: str, + listsEnabled: str, defaultReplyIntervalHours: int, lowBandwidth: bool, maxLikeCount: int, @@ -17214,6 +17316,11 @@ def runDaemon(listsEnabled: str, # scan the theme directory for any svg files containing scripts assert not scanThemesForScripts(baseDir) + # license for content of the instance + if not contentLicenseUrl: + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' + httpd.contentLicenseUrl = contentLicenseUrl + # fitness metrics fitnessFilename = baseDir + '/accounts/fitness.json' httpd.fitness = {} diff --git a/desktop_client.py b/desktop_client.py index 93603f5ca..054f8b835 100644 --- a/desktop_client.py +++ b/desktop_client.py @@ -419,6 +419,7 @@ def _desktopReplyToPost(session, postId: str, screenreader: str, systemLanguage: str, espeak, conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str, signingPrivateKeyPem: str) -> None: """Use the desktop client to send a reply to the most recent post """ @@ -473,6 +474,7 @@ def _desktopReplyToPost(session, postId: str, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, systemLanguage, lowBandwidth, + contentLicenseUrl, debug, postId, postId, conversationId, subject) == 0: sayStr = 'Reply sent' @@ -488,6 +490,7 @@ def _desktopNewPost(session, debug: bool, screenreader: str, systemLanguage: str, espeak, lowBandwidth: bool, + contentLicenseUrl: str, signingPrivateKeyPem: str) -> None: """Use the desktop client to create a new post """ @@ -538,6 +541,7 @@ def _desktopNewPost(session, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, systemLanguage, lowBandwidth, + contentLicenseUrl, debug, None, None, conversationId, subject) == 0: sayStr = 'Post sent' @@ -1124,6 +1128,7 @@ def _desktopNewDM(session, toHandle: str, debug: bool, screenreader: str, systemLanguage: str, espeak, lowBandwidth: bool, + contentLicenseUrl: str, signingPrivateKeyPem: str) -> None: """Use the desktop client to create a new direct message which can include multiple destination handles @@ -1146,6 +1151,7 @@ def _desktopNewDM(session, toHandle: str, debug, screenreader, systemLanguage, espeak, lowBandwidth, + contentLicenseUrl, signingPrivateKeyPem) @@ -1156,6 +1162,7 @@ def _desktopNewDMbase(session, toHandle: str, debug: bool, screenreader: str, systemLanguage: str, espeak, lowBandwidth: bool, + contentLicenseUrl: str, signingPrivateKeyPem: str) -> None: """Use the desktop client to create a new direct message """ @@ -1246,6 +1253,7 @@ def _desktopNewDMbase(session, toHandle: str, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, systemLanguage, lowBandwidth, + contentLicenseUrl, debug, None, None, conversationId, subject) == 0: sayStr = 'Direct message sent' @@ -1319,6 +1327,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, # TODO: this should probably be retrieved somehow from the server signingPrivateKeyPem = None + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' + blockedCache = {} indent = ' ' @@ -1716,6 +1726,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, screenreader, systemLanguage, espeak, conversationId, lowBandwidth, + contentLicenseUrl, signingPrivateKeyPem) refreshTimeline = True print('') @@ -1751,6 +1762,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, debug, screenreader, systemLanguage, espeak, lowBandwidth, + contentLicenseUrl, signingPrivateKeyPem) refreshTimeline = True else: @@ -1762,6 +1774,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, debug, screenreader, systemLanguage, espeak, lowBandwidth, + contentLicenseUrl, signingPrivateKeyPem) refreshTimeline = True print('') diff --git a/epicyon.py b/epicyon.py index a0c9e1f64..9cf52179e 100644 --- a/epicyon.py +++ b/epicyon.py @@ -112,6 +112,9 @@ def str2bool(v) -> bool: parser = argparse.ArgumentParser(description='ActivityPub Server') +parser.add_argument('--contentLicenseUrl', type=str, + default='https://creativecommons.org/licenses/by/4.0', + help='Url of the license used for the instance content') parser.add_argument('--listsEnabled', type=str, default=None, help='Names of content warning lists enabled. ' + @@ -1281,7 +1284,8 @@ if args.message: args.commentsEnabled, attach, mediaType, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, - args.language, args.lowBandwidth, args.debug, + args.language, args.lowBandwidth, + args.contentLicenseUrl, args.debug, replyTo, replyTo, args.conversationId, subject) for i in range(10): # TODO detect send success/fail @@ -2330,7 +2334,8 @@ if args.avatar: sys.exit() city = 'London, England' if setProfileImage(baseDir, httpPrefix, args.nickname, domain, - port, args.avatar, 'avatar', '128x128', city): + port, args.avatar, 'avatar', '128x128', city, + args.contentLicenseUrl): print('Avatar added for ' + args.nickname) else: print('Avatar was not added for ' + args.nickname) @@ -2346,7 +2351,7 @@ if args.backgroundImage: city = 'London, England' if setProfileImage(baseDir, httpPrefix, args.nickname, domain, port, args.backgroundImage, 'background', - '256x256', city): + '256x256', city, args.contentLicenseUrl): print('Background image added for ' + args.nickname) else: print('Background image was not added for ' + args.nickname) @@ -2716,7 +2721,8 @@ if args.testdata: "mechanical", "City", "0", "GBP", "2 months", - debug, city, args.language, {}, 'shares', args.lowBandwidth) + debug, city, args.language, {}, 'shares', args.lowBandwidth, + args.contentLicenseUrl) addShare(baseDir, httpPrefix, nickname, domain, port, "witch hat", @@ -2726,7 +2732,8 @@ if args.testdata: "clothing", "City", "0", "GBP", "3 months", - debug, city, args.language, {}, 'shares', args.lowBandwidth) + debug, city, args.language, {}, 'shares', args.lowBandwidth, + args.contentLicenseUrl) deleteAllPosts(baseDir, nickname, domain, 'inbox') deleteAllPosts(baseDir, nickname, domain, 'outbox') @@ -2762,7 +2769,7 @@ if args.testdata: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, args.language, conversationId, - lowBandwidth) + lowBandwidth, args.contentLicenseUrl) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Zoiks!!!", testFollowersOnly, @@ -2775,7 +2782,7 @@ if args.testdata: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, args.language, conversationId, - lowBandwidth) + lowBandwidth, args.contentLicenseUrl) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Hey scoob we need like a hundred more #milkshakes", testFollowersOnly, @@ -2788,7 +2795,7 @@ if args.testdata: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, args.language, conversationId, - lowBandwidth) + lowBandwidth, args.contentLicenseUrl) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Getting kinda spooky around here", testFollowersOnly, @@ -2801,7 +2808,7 @@ if args.testdata: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, args.language, conversationId, - lowBandwidth) + lowBandwidth, args.contentLicenseUrl) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "And they would have gotten away with it too" + "if it wasn't for those pesky hackers", @@ -2815,7 +2822,7 @@ if args.testdata: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, args.language, conversationId, - lowBandwidth) + lowBandwidth, args.contentLicenseUrl) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "man these centralized sites are like the worst!", testFollowersOnly, @@ -2828,7 +2835,7 @@ if args.testdata: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, args.language, conversationId, - lowBandwidth) + lowBandwidth, args.contentLicenseUrl) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "another mystery solved #test", testFollowersOnly, @@ -2841,7 +2848,7 @@ if args.testdata: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, args.language, conversationId, - lowBandwidth) + lowBandwidth, args.contentLicenseUrl) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "let's go bowling", testFollowersOnly, @@ -2854,7 +2861,7 @@ if args.testdata: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, args.language, conversationId, - lowBandwidth) + lowBandwidth, args.contentLicenseUrl) domainFull = domain + ':' + str(port) clearFollows(baseDir, nickname, domain) followPerson(baseDir, nickname, domain, 'maxboardroom', domainFull, @@ -2884,6 +2891,13 @@ minimumvotes = getConfigParam(baseDir, 'minvotes') if minimumvotes: args.minimumvotes = int(minimumvotes) +contentLicenseUrl = '' +if args.contentLicenseUrl: + contentLicenseUrl = args.contentLicenseUrl + setConfigParam(baseDir, 'contentLicenseUrl', contentLicenseUrl) +else: + contentLicenseUrl = getConfigParam(baseDir, 'contentLicenseUrl') + votingtime = getConfigParam(baseDir, 'votingtime') if votingtime: args.votingtime = votingtime @@ -3078,7 +3092,8 @@ if args.defaultCurrency: print('Default currency set to ' + args.defaultCurrency) if __name__ == "__main__": - runDaemon(listsEnabled, + runDaemon(contentLicenseUrl, + listsEnabled, args.defaultReplyIntervalHours, args.lowBandwidth, args.maxLikeCount, sharedItemsFederatedDomains, diff --git a/follow.py b/follow.py index 4343d60b7..b8953131f 100644 --- a/follow.py +++ b/follow.py @@ -209,6 +209,29 @@ def followerOfPerson(baseDir: str, nickname: str, domain: str, federationList, debug, groupAccount, 'followers.txt') +def getFollowerDomains(baseDir: str, nickname: str, domain: str) -> []: + """Returns a list of domains for followers + """ + domain = removeDomainPort(domain) + followersFile = acctDir(baseDir, nickname, domain) + '/followers.txt' + if not os.path.isfile(followersFile): + return [] + + lines = [] + with open(followersFile, 'r') as fpFollowers: + lines = fpFollowers.readlines() + + domainsList = [] + for handle in lines: + handle = handle.replace('\n', '') + followerDomain, _ = getDomainFromActor(handle) + if not followerDomain: + continue + if followerDomain not in domainsList: + domainsList.append(followerDomain) + return domainsList + + def isFollowerOfPerson(baseDir: str, nickname: str, domain: str, followerNickname: str, followerDomain: str) -> bool: """is the given nickname a follower of followerNickname? diff --git a/img/apple-touch-icon.png b/img/apple-touch-icon.png new file mode 100644 index 000000000..996deaf00 Binary files /dev/null and b/img/apple-touch-icon.png differ diff --git a/img/logo150.png b/img/logo150.png new file mode 100644 index 000000000..5b933f90f Binary files /dev/null and b/img/logo150.png differ diff --git a/img/logo152.png b/img/logo152.png deleted file mode 100644 index 011235ee4..000000000 Binary files a/img/logo152.png and /dev/null differ diff --git a/inbox.py b/inbox.py index 0d336a0c0..fab4fca6e 100644 --- a/inbox.py +++ b/inbox.py @@ -2374,7 +2374,8 @@ def _bounceDM(senderPostId: str, session, httpPrefix: str, cachedWebfingers: {}, personCache: {}, translate: {}, debug: bool, lastBounceMessage: [], systemLanguage: str, - signingPrivateKeyPem: str) -> bool: + signingPrivateKeyPem: str, + contentLicenseUrl: str) -> bool: """Sends a bounce message back to the sending handle if a DM has been rejected """ @@ -2433,7 +2434,8 @@ def _bounceDM(senderPostId: str, session, httpPrefix: str, inReplyTo, inReplyToAtomUri, subject, debug, schedulePost, eventDate, eventTime, location, - systemLanguage, conversationId, lowBandwidth) + systemLanguage, conversationId, lowBandwidth, + contentLicenseUrl) if not postJsonObject: print('WARN: unable to create bounce message to ' + sendingHandle) return False @@ -2459,7 +2461,8 @@ def _isValidDM(baseDir: str, nickname: str, domain: str, port: int, translate: {}, debug: bool, lastBounceMessage: [], handle: str, systemLanguage: str, - signingPrivateKeyPem: str) -> bool: + signingPrivateKeyPem: str, + contentLicenseUrl: str) -> bool: """Is the given message a valid DM? """ if nickname == 'inbox': @@ -2537,7 +2540,8 @@ def _isValidDM(baseDir: str, nickname: str, domain: str, port: int, translate, debug, lastBounceMessage, systemLanguage, - signingPrivateKeyPem) + signingPrivateKeyPem, + contentLicenseUrl) return False # dm index will be updated @@ -2772,7 +2776,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, maxLikeCount: int, signingPrivateKeyPem: str, defaultReplyIntervalHours: int, - CWlists: {}, listsEnabled: str) -> bool: + CWlists: {}, listsEnabled: str, + contentLicenseUrl: str) -> bool: """ Anything which needs to be done after initial checks have passed """ actor = keyId @@ -3004,7 +3009,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, translate, debug, lastBounceMessage, handle, systemLanguage, - signingPrivateKeyPem): + signingPrivateKeyPem, + contentLicenseUrl): return False # get the actor being replied to @@ -3786,6 +3792,7 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, saveJson(queueJson['post'], sharedInboxPostFilename) listsEnabled = getConfigParam(baseDir, "listsEnabled") + contentLicenseUrl = getConfigParam(baseDir, "contentLicenseUrl") # for posts addressed to specific accounts for handle, capsId in recipientsDict.items(): @@ -3818,7 +3825,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, maxLikeCount, signingPrivateKeyPem, defaultReplyIntervalHours, - CWlists, listsEnabled) + CWlists, listsEnabled, + contentLicenseUrl) if debug: pprint(queueJson['post']) print('Queue: Queue post accepted') diff --git a/media.py b/media.py index c05faca93..184c8d2af 100644 --- a/media.py +++ b/media.py @@ -107,7 +107,8 @@ def _removeMetaData(imageFilename: str, outputFilename: str) -> None: def _spoofMetaData(baseDir: str, nickname: str, domain: str, - outputFilename: str, spoofCity: str) -> None: + outputFilename: str, spoofCity: str, + contentLicenseUrl: str) -> None: """Spoof image metadata using a decoy model for a given city """ if not os.path.isfile(outputFilename): @@ -151,6 +152,7 @@ def _spoofMetaData(baseDir: str, nickname: str, domain: str, '-GPSLongitude=' + str(longitude) + ' ' + '-GPSLatitudeRef=' + latitudeRef + ' ' + '-GPSLatitude=' + str(latitude) + ' ' + + '-copyright="' + contentLicenseUrl + '" ' + '-Comment="" ' + outputFilename) != 0: # nosec print('ERROR: exiftool failed to run') @@ -203,7 +205,7 @@ def convertImageToLowBandwidth(imageFilename: str) -> None: def processMetaData(baseDir: str, nickname: str, domain: str, imageFilename: str, outputFilename: str, - city: str) -> None: + city: str, contentLicenseUrl: str) -> None: """Handles image metadata. This tries to spoof the metadata if possible, but otherwise just removes it """ @@ -211,7 +213,8 @@ def processMetaData(baseDir: str, nickname: str, domain: str, _removeMetaData(imageFilename, outputFilename) # now add some spoofed data to misdirect surveillance capitalists - _spoofMetaData(baseDir, nickname, domain, outputFilename, city) + _spoofMetaData(baseDir, nickname, domain, outputFilename, city, + contentLicenseUrl) def _isMedia(imageFilename: str) -> bool: @@ -299,7 +302,8 @@ def attachMedia(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, postJson: {}, imageFilename: str, mediaType: str, description: str, - city: str, lowBandwidth: bool) -> {}: + city: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Attaches media to a json object post The description can be None """ @@ -357,7 +361,8 @@ def attachMedia(baseDir: str, httpPrefix: str, if lowBandwidth: convertImageToLowBandwidth(imageFilename) processMetaData(baseDir, nickname, domain, - imageFilename, mediaFilename, city) + imageFilename, mediaFilename, city, + contentLicenseUrl) else: copyfile(imageFilename, mediaFilename) _updateEtag(mediaFilename) diff --git a/newsdaemon.py b/newsdaemon.py index a83616cb8..80c69bbb8 100644 --- a/newsdaemon.py +++ b/newsdaemon.py @@ -530,7 +530,8 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str, maxMirroredArticles: int, allowLocalNetworkAccess: bool, systemLanguage: str, - lowBandwidth: bool) -> None: + lowBandwidth: bool, + contentLicenseUrl: str) -> None: """Converts rss items in a newswire into posts """ if not newswire: @@ -622,7 +623,8 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str, attachImageFilename, mediaType, imageDescription, city, rssTitle, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) if not blog: continue @@ -818,7 +820,8 @@ def runNewswireDaemon(baseDir: str, httpd, httpd.maxMirroredArticles, httpd.allowLocalNetworkAccess, httpd.systemLanguage, - httpd.lowBandwidth) + httpd.lowBandwidth, + httpd.contentLicenseUrl) print('Newswire feed converted to ActivityPub') if httpd.maxNewsPosts > 0: diff --git a/outbox.py b/outbox.py index 78c9f1cc9..6b0bc0583 100644 --- a/outbox.py +++ b/outbox.py @@ -197,7 +197,8 @@ def postMessageToOutbox(session, translate: {}, peertubeInstances: str, theme: str, maxLikeCount: int, maxRecentPosts: int, CWlists: {}, - listsEnabled: str) -> bool: + listsEnabled: str, + contentLicenseUrl: str) -> bool: """post is received by the outbox Client to server message post https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery @@ -607,7 +608,8 @@ def postMessageToOutbox(session, translate: {}, print('DEBUG: handle share uploads') outboxShareUpload(baseDir, httpPrefix, postToNickname, domain, port, messageJson, debug, city, - systemLanguage, translate, lowBandwidth) + systemLanguage, translate, lowBandwidth, + contentLicenseUrl) if debug: print('DEBUG: handle undo share uploads') diff --git a/person.py b/person.py index 79daa3158..3594dd849 100644 --- a/person.py +++ b/person.py @@ -88,7 +88,8 @@ def generateRSAKey() -> (str, str): def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, imageFilename: str, imageType: str, - resolution: str, city: str) -> bool: + resolution: str, city: str, + contentLicenseUrl: str) -> bool: """Saves the given image file as an avatar or background image for the given person """ @@ -151,7 +152,8 @@ def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str, resolution + ' -quality 50 ' + profileFilename subprocess.call(cmd, shell=True) processMetaData(baseDir, nickname, domain, - profileFilename, profileFilename, city) + profileFilename, profileFilename, city, + contentLicenseUrl) return True return False diff --git a/posts.py b/posts.py index 01eca5505..38be89bd0 100644 --- a/posts.py +++ b/posts.py @@ -1064,7 +1064,8 @@ def _createPostS2S(baseDir: str, nickname: str, domain: str, port: int, mediaType: str, imageDescription: str, city: str, postObjectType: str, summary: str, inReplyToAtomUri: str, systemLanguage: str, - conversationId: str, lowBandwidth: bool) -> {}: + conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Creates a new server-to-server post """ actorUrl = localActorUrl(httpPrefix, nickname, domain) @@ -1124,7 +1125,8 @@ def _createPostS2S(baseDir: str, nickname: str, domain: str, port: int, newPost['object'] = \ attachMedia(baseDir, httpPrefix, nickname, domain, port, newPost['object'], attachImageFilename, - mediaType, imageDescription, city, lowBandwidth) + mediaType, imageDescription, city, lowBandwidth, + contentLicenseUrl) return newPost @@ -1137,7 +1139,8 @@ def _createPostC2S(baseDir: str, nickname: str, domain: str, port: int, mediaType: str, imageDescription: str, city: str, postObjectType: str, summary: str, inReplyToAtomUri: str, systemLanguage: str, - conversationId: str, lowBandwidth: str) -> {}: + conversationId: str, lowBandwidth: str, + contentLicenseUrl: str) -> {}: """Creates a new client-to-server post """ domainFull = getFullDomain(domain, port) @@ -1187,7 +1190,8 @@ def _createPostC2S(baseDir: str, nickname: str, domain: str, port: int, newPost = \ attachMedia(baseDir, httpPrefix, nickname, domain, port, newPost, attachImageFilename, - mediaType, imageDescription, city, lowBandwidth) + mediaType, imageDescription, city, lowBandwidth, + contentLicenseUrl) return newPost @@ -1314,7 +1318,8 @@ def _createPostBase(baseDir: str, anonymousParticipationEnabled: bool, eventStatus: str, ticketUrl: str, systemLanguage: str, - conversationId: str, lowBandwidth: bool) -> {}: + conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Creates a message """ content = removeInvalidChars(content) @@ -1439,7 +1444,8 @@ def _createPostBase(baseDir: str, mediaType, imageDescription, city, postObjectType, summary, inReplyToAtomUri, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) else: newPost = \ _createPostC2S(baseDir, nickname, domain, port, @@ -1451,7 +1457,8 @@ def _createPostBase(baseDir: str, mediaType, imageDescription, city, postObjectType, summary, inReplyToAtomUri, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) _createPostMentions(ccUrl, newPost, toRecipients, tags) @@ -1685,7 +1692,8 @@ def createPublicPost(baseDir: str, location: str, isArticle: bool, systemLanguage: str, - conversationId: str, lowBandwidth: bool) -> {}: + conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Public post """ domainFull = getFullDomain(domain, port) @@ -1716,7 +1724,8 @@ def createPublicPost(baseDir: str, repliesModerationOption, anonymousParticipationEnabled, eventStatus, ticketUrl, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) def _appendCitationsToBlogPost(baseDir: str, @@ -1759,7 +1768,8 @@ def createBlogPost(baseDir: str, subject: str, schedulePost: bool, eventDate: str, eventTime: str, location: str, systemLanguage: str, - conversationId: str, lowBandwidth: bool) -> {}: + conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: blogJson = \ createPublicPost(baseDir, nickname, domain, port, httpPrefix, @@ -1771,7 +1781,7 @@ def createBlogPost(baseDir: str, schedulePost, eventDate, eventTime, location, True, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) blogJson['object']['url'] = \ blogJson['object']['url'].replace('/@', '/users/') _appendCitationsToBlogPost(baseDir, nickname, domain, blogJson) @@ -1785,7 +1795,8 @@ def createNewsPost(baseDir: str, attachImageFilename: str, mediaType: str, imageDescription: str, city: str, subject: str, systemLanguage: str, - conversationId: str, lowBandwidth: bool) -> {}: + conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: clientToServer = False inReplyTo = None inReplyToAtomUri = None @@ -1804,7 +1815,7 @@ def createNewsPost(baseDir: str, schedulePost, eventDate, eventTime, location, True, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) blog['object']['type'] = 'Article' return blog @@ -1817,7 +1828,8 @@ def createQuestionPost(baseDir: str, attachImageFilename: str, mediaType: str, imageDescription: str, city: str, subject: str, durationDays: int, - systemLanguage: str, lowBandwidth: bool) -> {}: + systemLanguage: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Question post with multiple choice options """ domainFull = getFullDomain(domain, port) @@ -1834,7 +1846,7 @@ def createQuestionPost(baseDir: str, False, None, None, None, None, None, None, None, None, None, None, None, None, None, systemLanguage, - None, lowBandwidth) + None, lowBandwidth, contentLicenseUrl) messageJson['object']['type'] = 'Question' messageJson['object']['oneOf'] = [] messageJson['object']['votersCount'] = 0 @@ -1866,7 +1878,8 @@ def createUnlistedPost(baseDir: str, subject: str, schedulePost: bool, eventDate: str, eventTime: str, location: str, systemLanguage: str, - conversationId: str, lowBandwidth: bool) -> {}: + conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Unlisted post. This has the #Public and followers links inverted. """ domainFull = getFullDomain(domain, port) @@ -1883,7 +1896,8 @@ def createUnlistedPost(baseDir: str, schedulePost, eventDate, eventTime, location, None, None, None, None, None, None, None, None, None, None, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) def createFollowersOnlyPost(baseDir: str, @@ -1899,7 +1913,8 @@ def createFollowersOnlyPost(baseDir: str, subject: str, schedulePost: bool, eventDate: str, eventTime: str, location: str, systemLanguage: str, - conversationId: str, lowBandwidth: bool) -> {}: + conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Followers only post """ domainFull = getFullDomain(domain, port) @@ -1916,7 +1931,8 @@ def createFollowersOnlyPost(baseDir: str, schedulePost, eventDate, eventTime, location, None, None, None, None, None, None, None, None, None, None, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) def getMentionedPeople(baseDir: str, httpPrefix: str, @@ -1968,7 +1984,8 @@ def createDirectMessagePost(baseDir: str, schedulePost: bool, eventDate: str, eventTime: str, location: str, systemLanguage: str, - conversationId: str, lowBandwidth: bool) -> {}: + conversationId: str, lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Direct Message post """ content = resolvePetnames(baseDir, nickname, domain, content) @@ -1992,7 +2009,8 @@ def createDirectMessagePost(baseDir: str, schedulePost, eventDate, eventTime, location, None, None, None, None, None, None, None, None, None, None, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) # mentioned recipients go into To rather than Cc messageJson['to'] = messageJson['object']['cc'] messageJson['object']['to'] = messageJson['to'] @@ -2012,7 +2030,8 @@ def createReportPost(baseDir: str, attachImageFilename: str, mediaType: str, imageDescription: str, city: str, debug: bool, subject: str, systemLanguage: str, - lowBandwidth: bool) -> {}: + lowBandwidth: bool, + contentLicenseUrl: str) -> {}: """Send a report to moderators """ domainFull = getFullDomain(domain, port) @@ -2086,7 +2105,7 @@ def createReportPost(baseDir: str, False, None, None, None, None, None, None, None, None, None, None, None, None, None, systemLanguage, - None, lowBandwidth) + None, lowBandwidth, contentLicenseUrl) if not postJsonObject: continue @@ -2183,7 +2202,7 @@ def sendPost(signingPrivateKeyPem: str, projectVersion: str, isArticle: bool, systemLanguage: str, sharedItemsFederatedDomains: [], sharedItemFederationTokens: {}, - lowBandwidth: bool, + lowBandwidth: bool, contentLicenseUrl: str, debug: bool = False, inReplyTo: str = None, inReplyToAtomUri: str = None, subject: str = None) -> int: """Post to another inbox. Used by unit tests. @@ -2249,7 +2268,8 @@ def sendPost(signingPrivateKeyPem: str, projectVersion: str, False, None, None, None, None, None, None, None, None, None, None, None, None, None, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) # get the senders private key privateKeyPem = _getPersonKey(nickname, domain, baseDir, 'private') @@ -2331,6 +2351,7 @@ def sendPostViaServer(signingPrivateKeyPem: str, projectVersion: str, cachedWebfingers: {}, personCache: {}, isArticle: bool, systemLanguage: str, lowBandwidth: bool, + contentLicenseUrl: str, debug: bool = False, inReplyTo: str = None, inReplyToAtomUri: str = None, @@ -2416,7 +2437,8 @@ def sendPostViaServer(signingPrivateKeyPem: str, projectVersion: str, False, None, None, None, None, None, None, None, None, None, None, None, None, None, systemLanguage, - conversationId, lowBandwidth) + conversationId, lowBandwidth, + contentLicenseUrl) authHeader = createBasicAuthHeader(fromNickname, password) diff --git a/schedule.py b/schedule.py index b35d11f7a..804bc16b2 100644 --- a/schedule.py +++ b/schedule.py @@ -128,7 +128,8 @@ def _updatePostSchedule(baseDir: str, handle: str, httpd, httpd.maxLikeCount, httpd.maxRecentPosts, httpd.CWlists, - httpd.listsEnabled): + httpd.listsEnabled, + httpd.contentLicenseUrl): indexLines.remove(line) try: os.remove(postFilename) diff --git a/shares.py b/shares.py index a00e47458..9df356597 100644 --- a/shares.py +++ b/shares.py @@ -308,7 +308,8 @@ def addShare(baseDir: str, duration: str, debug: bool, city: str, price: str, currency: str, systemLanguage: str, translate: {}, - sharesFileType: str, lowBandwidth: bool) -> None: + sharesFileType: str, lowBandwidth: bool, + contentLicenseUrl: str) -> None: """Adds a new share """ if isFilteredGlobally(baseDir, @@ -363,7 +364,7 @@ def addShare(baseDir: str, convertImageToLowBandwidth(imageFilename) processMetaData(baseDir, nickname, domain, imageFilename, itemIDfile + '.' + ext, - city) + city, contentLicenseUrl) if moveImage: try: os.remove(imageFilename) @@ -1033,7 +1034,8 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, messageJson: {}, debug: bool, city: str, systemLanguage: str, translate: {}, - lowBandwidth: bool) -> None: + lowBandwidth: bool, + contentLicenseUrl: str) -> None: """ When a shared item is received by the outbox from c2s """ if not messageJson.get('type'): @@ -1095,7 +1097,7 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, messageJson['object']['itemPrice'], messageJson['object']['itemCurrency'], systemLanguage, translate, 'shares', - lowBandwidth) + lowBandwidth, contentLicenseUrl) if debug: print('DEBUG: shared item received via c2s') diff --git a/tests.py b/tests.py index 876594409..616389903 100644 --- a/tests.py +++ b/tests.py @@ -753,6 +753,7 @@ def createServerAlice(path: str, domain: str, port: int, testLocation = None testIsArticle = False conversationId = None + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' createPublicPost(path, nickname, domain, port, httpPrefix, "No wise fish would go anywhere without a porpoise", testFollowersOnly, @@ -766,7 +767,7 @@ def createServerAlice(path: str, domain: str, port: int, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) createPublicPost(path, nickname, domain, port, httpPrefix, "Curiouser and curiouser!", testFollowersOnly, @@ -780,7 +781,7 @@ def createServerAlice(path: str, domain: str, port: int, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) createPublicPost(path, nickname, domain, port, httpPrefix, "In the gardens of memory, in the palace " + "of dreams, that is where you and I shall meet", @@ -795,7 +796,7 @@ def createServerAlice(path: str, domain: str, port: int, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) regenerateIndexForBox(path, nickname, domain, 'outbox') global testServerAliceRunning testServerAliceRunning = True @@ -818,8 +819,10 @@ def createServerAlice(path: str, domain: str, port: int, maxLikeCount = 10 defaultReplyIntervalHours = 9999999999 listsEnabled = '' + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' print('Server running: Alice') - runDaemon(listsEnabled, defaultReplyIntervalHours, + runDaemon(contentLicenseUrl, + listsEnabled, defaultReplyIntervalHours, lowBandwidth, maxLikeCount, sharedItemsFederatedDomains, userAgentsBlocked, @@ -892,6 +895,7 @@ def createServerBob(path: str, domain: str, port: int, testLocation = None testIsArticle = False conversationId = None + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' createPublicPost(path, nickname, domain, port, httpPrefix, "It's your life, live it your way.", testFollowersOnly, @@ -905,7 +909,7 @@ def createServerBob(path: str, domain: str, port: int, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) createPublicPost(path, nickname, domain, port, httpPrefix, "One of the things I've realised is that " + "I am very simple", @@ -920,7 +924,7 @@ def createServerBob(path: str, domain: str, port: int, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) createPublicPost(path, nickname, domain, port, httpPrefix, "Quantum physics is a bit of a passion of mine", testFollowersOnly, @@ -934,7 +938,7 @@ def createServerBob(path: str, domain: str, port: int, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) regenerateIndexForBox(path, nickname, domain, 'outbox') global testServerBobRunning testServerBobRunning = True @@ -957,8 +961,10 @@ def createServerBob(path: str, domain: str, port: int, maxLikeCount = 10 defaultReplyIntervalHours = 9999999999 listsEnabled = '' + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' print('Server running: Bob') - runDaemon(listsEnabled, defaultReplyIntervalHours, + runDaemon(contentLicenseUrl, + listsEnabled, defaultReplyIntervalHours, lowBandwidth, maxLikeCount, sharedItemsFederatedDomains, userAgentsBlocked, @@ -1025,8 +1031,10 @@ def createServerEve(path: str, domain: str, port: int, federationList: [], lowBandwidth = True defaultReplyIntervalHours = 9999999999 listsEnabled = '' + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' print('Server running: Eve') - runDaemon(listsEnabled, defaultReplyIntervalHours, + runDaemon(contentLicenseUrl, + listsEnabled, defaultReplyIntervalHours, lowBandwidth, maxLikeCount, sharedItemsFederatedDomains, userAgentsBlocked, @@ -1095,8 +1103,10 @@ def createServerGroup(path: str, domain: str, port: int, lowBandwidth = True defaultReplyIntervalHours = 9999999999 listsEnabled = '' + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' print('Server running: Group') - runDaemon(listsEnabled, defaultReplyIntervalHours, + runDaemon(contentLicenseUrl, + listsEnabled, defaultReplyIntervalHours, lowBandwidth, maxLikeCount, sharedItemsFederatedDomains, userAgentsBlocked, @@ -1132,6 +1142,7 @@ def testPostMessageBetweenServers(baseDir: str) -> None: systemLanguage = 'en' httpPrefix = 'http' proxyType = None + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' if os.path.isdir(baseDir + '/.tests'): shutil.rmtree(baseDir + '/.tests', ignore_errors=False, onerror=None) @@ -1235,6 +1246,7 @@ def testPostMessageBetweenServers(baseDir: str) -> None: alicePersonCache, isArticle, systemLanguage, aliceSharedItemsFederatedDomains, aliceSharedItemFederationTokens, lowBandwidth, + contentLicenseUrl, inReplyTo, inReplyToAtomUri, subject) print('sendResult: ' + str(sendResult)) @@ -1433,6 +1445,7 @@ def testFollowBetweenServers(baseDir: str) -> None: httpPrefix = 'http' proxyType = None federationList = [] + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' if os.path.isdir(baseDir + '/.tests'): shutil.rmtree(baseDir + '/.tests', ignore_errors=False, onerror=None) @@ -1572,6 +1585,7 @@ def testFollowBetweenServers(baseDir: str) -> None: alicePersonCache, isArticle, systemLanguage, aliceSharedItemsFederatedDomains, aliceSharedItemFederationTokens, lowBandwidth, + contentLicenseUrl, inReplyTo, inReplyToAtomUri, subject) print('sendResult: ' + str(sendResult)) @@ -1620,6 +1634,7 @@ def testSharedItemsFederation(baseDir: str) -> None: httpPrefix = 'http' proxyType = None federationList = [] + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' if os.path.isdir(baseDir + '/.tests'): shutil.rmtree(baseDir + '/.tests', ignore_errors=False, onerror=None) @@ -1920,7 +1935,8 @@ def testSharedItemsFederation(baseDir: str) -> None: aliceSendThreads, alicePostLog, aliceCachedWebfingers, alicePersonCache, isArticle, systemLanguage, aliceSharedItemsFederatedDomains, - aliceSharedItemFederationTokens, lowBandwidth, True, + aliceSharedItemFederationTokens, lowBandwidth, + contentLicenseUrl, True, inReplyTo, inReplyToAtomUri, subject) print('sendResult: ' + str(sendResult)) @@ -2024,6 +2040,7 @@ def testGroupFollow(baseDir: str) -> None: httpPrefix = 'http' proxyType = None federationList = [] + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' if os.path.isdir(baseDir + '/.tests'): shutil.rmtree(baseDir + '/.tests', ignore_errors=False, onerror=None) @@ -2340,6 +2357,7 @@ def testGroupFollow(baseDir: str) -> None: alicePersonCache, isArticle, systemLanguage, aliceSharedItemsFederatedDomains, aliceSharedItemFederationTokens, lowBandwidth, + contentLicenseUrl, inReplyTo, inReplyToAtomUri, subject) print('sendResult: ' + str(sendResult)) @@ -2692,6 +2710,7 @@ def _testCreatePerson(baseDir: str): mediaType = None conversationId = None lowBandwidth = True + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' createPublicPost(baseDir, nickname, domain, port, httpPrefix, content, followersOnly, saveToFile, clientToServer, commentsEnabled, attachImageFilename, mediaType, @@ -2700,7 +2719,7 @@ def _testCreatePerson(baseDir: str): testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) os.chdir(currDir) shutil.rmtree(baseDir, ignore_errors=False, onerror=None) @@ -2763,6 +2782,7 @@ def testClientToServer(baseDir: str): global testServerAliceRunning global testServerBobRunning + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' testServerAliceRunning = False testServerBobRunning = False @@ -2879,6 +2899,7 @@ def testClientToServer(baseDir: str): attachedImageDescription, city, cachedWebfingers, personCache, isArticle, systemLanguage, lowBandwidth, + contentLicenseUrl, True, None, None, conversationId, None) print('sendResult: ' + str(sendResult)) @@ -4185,6 +4206,7 @@ def _testReplyToPublicPost(baseDir: str) -> None: testIsArticle = False conversationId = None lowBandwidth = True + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' reply = \ createPublicPost(baseDir, nickname, domain, port, httpPrefix, content, followersOnly, saveToFile, @@ -4195,7 +4217,7 @@ def _testReplyToPublicPost(baseDir: str) -> None: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) # print(str(reply)) assert reply['object']['content'] == \ '

' + \ @@ -4717,6 +4739,7 @@ def _testLinksWithinPost(baseDir: str) -> None: testIsArticle = False conversationId = None lowBandwidth = True + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' postJsonObject = \ createPublicPost(baseDir, nickname, domain, port, httpPrefix, @@ -4728,7 +4751,7 @@ def _testLinksWithinPost(baseDir: str) -> None: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) assert postJsonObject['object']['content'] == \ '

This is a test post with links.

' + \ @@ -4765,7 +4788,7 @@ def _testLinksWithinPost(baseDir: str) -> None: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) assert postJsonObject['object']['content'] == content assert postJsonObject['object']['contentMap'][systemLanguage] == content @@ -5703,6 +5726,7 @@ def _testCanReplyTo(baseDir: str) -> None: testIsArticle = False conversationId = None lowBandwidth = True + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' postJsonObject = \ createPublicPost(baseDir, nickname, domain, port, httpPrefix, @@ -5714,7 +5738,7 @@ def _testCanReplyTo(baseDir: str) -> None: testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, testIsArticle, systemLanguage, conversationId, - lowBandwidth) + lowBandwidth, contentLicenseUrl) # set the date on the post currDateStr = "2021-09-08T20:45:00Z" postJsonObject['published'] = currDateStr diff --git a/translations/ar.json b/translations/ar.json index f71b30e40..53e3d7da9 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -491,5 +491,6 @@ "System Monitor": "مراقب النظام", "Add content warnings for the following sites": "أضف تحذيرات المحتوى للمواقع التالية", "Known Web Crawlers": "برامج زحف الويب المعروفة", - "Add to the calendar": "أضف إلى التقويم" + "Add to the calendar": "أضف إلى التقويم", + "Content License": "ترخيص المحتوى" } diff --git a/translations/ca.json b/translations/ca.json index 07205607c..fb0b7340d 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -491,5 +491,6 @@ "System Monitor": "Monitor del sistema", "Add content warnings for the following sites": "Afegiu advertiments de contingut per als llocs següents", "Known Web Crawlers": "Exploradors web coneguts", - "Add to the calendar": "Afegeix al calendari" + "Add to the calendar": "Afegeix al calendari", + "Content License": "Llicència de contingut" } diff --git a/translations/cy.json b/translations/cy.json index 6a3715cb6..d1eaf3d76 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -491,5 +491,6 @@ "System Monitor": "Monitor System", "Add content warnings for the following sites": "Ychwanegwch rybuddion cynnwys ar gyfer y gwefannau canlynol", "Known Web Crawlers": "Crawlers Gwe Hysbys", - "Add to the calendar": "Ychwanegwch at y calendr" + "Add to the calendar": "Ychwanegwch at y calendr", + "Content License": "Trwydded Cynnwys" } diff --git a/translations/de.json b/translations/de.json index da33ff00f..a94268597 100644 --- a/translations/de.json +++ b/translations/de.json @@ -491,5 +491,6 @@ "System Monitor": "Systemmonitor", "Add content warnings for the following sites": "Inhaltswarnungen für die folgenden Websites hinzufügen", "Known Web Crawlers": "Bekannte Web-Crawler", - "Add to the calendar": "Zum Kalender hinzufügen" + "Add to the calendar": "Zum Kalender hinzufügen", + "Content License": "Inhaltslizenz" } diff --git a/translations/en.json b/translations/en.json index 551749a49..d96083f0b 100644 --- a/translations/en.json +++ b/translations/en.json @@ -491,5 +491,6 @@ "System Monitor": "System Monitor", "Add content warnings for the following sites": "Add content warnings for the following sites", "Known Web Crawlers": "Known Web Crawlers", - "Add to the calendar": "Add to the calendar" + "Add to the calendar": "Add to the calendar", + "Content License": "Content License" } diff --git a/translations/es.json b/translations/es.json index d89b835ec..00a945f28 100644 --- a/translations/es.json +++ b/translations/es.json @@ -491,5 +491,6 @@ "System Monitor": "Monitor del sistema", "Add content warnings for the following sites": "Agregue advertencias de contenido para los siguientes sitios", "Known Web Crawlers": "Rastreadores web conocidos", - "Add to the calendar": "Agregar al calendario" + "Add to the calendar": "Agregar al calendario", + "Content License": "Licencia de contenido" } diff --git a/translations/fr.json b/translations/fr.json index 73982bb9e..0d082c4a9 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -491,5 +491,6 @@ "System Monitor": "Moniteur système", "Add content warnings for the following sites": "Ajouter des avertissements de contenu pour les sites suivants", "Known Web Crawlers": "Crawlers Web connus", - "Add to the calendar": "Ajouter au calendrier" + "Add to the calendar": "Ajouter au calendrier", + "Content License": "Licence de contenu" } diff --git a/translations/ga.json b/translations/ga.json index 8af1069ed..1d91c0471 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -491,5 +491,6 @@ "System Monitor": "Monatóir Córais", "Add content warnings for the following sites": "Cuir rabhaidh ábhair leis na suíomhanna seo a leanas", "Known Web Crawlers": "Crawlers Gréasáin Aitheanta", - "Add to the calendar": "Cuir leis an bhféilire" + "Add to the calendar": "Cuir leis an bhféilire", + "Content License": "Ceadúnas Ábhar" } diff --git a/translations/hi.json b/translations/hi.json index e044eb261..93a81c7e6 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -491,5 +491,6 @@ "System Monitor": "सिस्टम मॉनिटर", "Add content warnings for the following sites": "निम्नलिखित साइटों के लिए सामग्री चेतावनियाँ जोड़ें", "Known Web Crawlers": "ज्ञात वेब क्रॉलर", - "Add to the calendar": "कैलेंडर में जोड़ें" + "Add to the calendar": "कैलेंडर में जोड़ें", + "Content License": "सामग्री लाइसेंस" } diff --git a/translations/it.json b/translations/it.json index c847b5dc5..64319f34a 100644 --- a/translations/it.json +++ b/translations/it.json @@ -491,5 +491,6 @@ "System Monitor": "Monitor di sistema", "Add content warnings for the following sites": "Aggiungi avvisi sui contenuti per i seguenti siti", "Known Web Crawlers": "Crawler Web conosciuti", - "Add to the calendar": "Aggiungi al calendario" + "Add to the calendar": "Aggiungi al calendario", + "Content License": "Licenza sui contenuti" } diff --git a/translations/ja.json b/translations/ja.json index 17f939fbe..89b880f99 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -491,5 +491,6 @@ "System Monitor": "システムモニター", "Add content warnings for the following sites": "次のサイトのコンテンツ警告を追加します", "Known Web Crawlers": "既知のWebクローラー", - "Add to the calendar": "カレンダーに追加" + "Add to the calendar": "カレンダーに追加", + "Content License": "コンテンツライセンス" } diff --git a/translations/ku.json b/translations/ku.json index 36698735f..54edfdbb8 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -491,5 +491,6 @@ "System Monitor": "System Monitor", "Add content warnings for the following sites": "Ji bo malperên jêrîn hişyariyên naverokê zêde bikin", "Known Web Crawlers": "Crawlerên Webê yên naskirî", - "Add to the calendar": "Di salnameyê de zêde bike" + "Add to the calendar": "Di salnameyê de zêde bike", + "Content License": "Naverok License de" } diff --git a/translations/oc.json b/translations/oc.json index dc898e1e4..8240c4a8e 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -487,5 +487,6 @@ "System Monitor": "System Monitor", "Add content warnings for the following sites": "Add content warnings for the following sites", "Known Web Crawlers": "Known Web Crawlers", - "Add to the calendar": "Add to the calendar" + "Add to the calendar": "Add to the calendar", + "Content License": "Content License" } diff --git a/translations/pt.json b/translations/pt.json index 936c7f095..958a40f6f 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -491,5 +491,6 @@ "System Monitor": "Monitor de Sistema", "Add content warnings for the following sites": "Adicione avisos de conteúdo para os seguintes sites", "Known Web Crawlers": "Rastreadores da Web conhecidos", - "Add to the calendar": "Adicionar ao calendário" + "Add to the calendar": "Adicionar ao calendário", + "Content License": "Licença de Conteúdo" } diff --git a/translations/ru.json b/translations/ru.json index bb92161d9..d59bf4869 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -491,5 +491,6 @@ "System Monitor": "Системный монитор", "Add content warnings for the following sites": "Добавить предупреждения о содержании для следующих сайтов", "Known Web Crawlers": "Известные веб-сканеры", - "Add to the calendar": "Добавить в календарь" + "Add to the calendar": "Добавить в календарь", + "Content License": "Лицензия на содержание" } diff --git a/translations/sw.json b/translations/sw.json index aace5f87e..d4a5ca713 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -491,5 +491,6 @@ "System Monitor": "Ufuatiliaji wa Mfumo", "Add content warnings for the following sites": "Ongeza maonyo ya yaliyomo kwa wavuti zifuatazo", "Known Web Crawlers": "Watambaji Wavuti Wanaojulikana", - "Add to the calendar": "Ongeza kwenye kalenda" + "Add to the calendar": "Ongeza kwenye kalenda", + "Content License": "Leseni ya Maudhui" } diff --git a/translations/zh.json b/translations/zh.json index bde6635db..a698fb5ce 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -491,5 +491,6 @@ "System Monitor": "系统监视器", "Add content warnings for the following sites": "为以下网站添加内容警告", "Known Web Crawlers": "已知的网络爬虫", - "Add to the calendar": "添加到日历" + "Add to the calendar": "添加到日历", + "Content License": "内容许可" } diff --git a/webapp_profile.py b/webapp_profile.py index 9d01178e0..269851597 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -53,6 +53,7 @@ from jami import getJamiAddress from cwtch import getCwtchAddress from filters import isFiltered from follow import isFollowerOfPerson +from follow import getFollowerDomains from webapp_frontscreen import htmlFrontScreen from webapp_utils import htmlKeyboardNavigation from webapp_utils import htmlHideFromScreenReader @@ -541,7 +542,8 @@ def htmlProfile(signingPrivateKeyPem: str, sharedItemsFederatedDomains: [], extraJson: {}, pageNumber: int, maxItemsPerPage: int, - CWlists: {}, listsEnabled: str) -> str: + CWlists: {}, listsEnabled: str, + contentLicenseUrl: str) -> str: """Show the profile page as html """ nickname = profileJson['preferredUsername'] @@ -716,9 +718,12 @@ def htmlProfile(signingPrivateKeyPem: str, break if selected == 'followers': if followApprovals: + currFollowerDomains = \ + getFollowerDomains(baseDir, nickname, domain) with open(followRequestsFilename, 'r') as f: for followerHandle in f: if len(line) > 0: + followerHandle = followerHandle.replace('\n', '') if '://' in followerHandle: followerActor = followerHandle else: @@ -726,13 +731,25 @@ def htmlProfile(signingPrivateKeyPem: str, dom = followerHandle.split('@')[1] followerActor = \ localActorUrl(httpPrefix, nick, dom) + + # is this a new domain? + # if so then append a new instance indicator + followerDomain, _ = \ + getDomainFromActor(followerActor) + newFollowerDomain = '' + if followerDomain not in currFollowerDomains: + newFollowerDomain = ' ✨' + basePath = '/users/' + nickname followApprovalsSection += '

' followApprovalsSection += \ '' followApprovalsSection += \ '' + \ - followerHandle + '' + followerHandle + \ + newFollowerDomain + '' + + # show Approve and Deny buttons followApprovalsSection += \ '' @@ -970,7 +987,8 @@ def htmlProfile(signingPrivateKeyPem: str, getConfigParam(baseDir, 'instanceTitle') profileStr = \ htmlHeaderWithPersonMarkup(cssFilename, instanceTitle, - profileJson, city) + \ + profileJson, city, + contentLicenseUrl) + \ profileStr + htmlFooter() return profileStr @@ -1268,6 +1286,10 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, getConfigParam(baseDir, 'instanceDescriptionShort') instanceTitle = \ getConfigParam(baseDir, 'instanceTitle') + contentLicenseUrl = \ + getConfigParam(baseDir, 'contentLicenseUrl') + if not contentLicenseUrl: + contentLicenseUrl = 'https://creativecommons.org/licenses/by/4.0' instanceStr = beginEditSection(translate['Instance Settings']) @@ -1283,6 +1305,10 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, editTextArea(translate['Instance Description'], 'instanceDescription', instanceDescription, 200, '', True) + instanceStr += \ + editTextField(translate['Content License'], + 'contentLicenseUrl', contentLicenseUrl) + instanceStr += '
\n' instanceStr += \ editTextField(translate['Custom post submit button text'], 'customSubmitText', customSubmitText) diff --git a/webapp_utils.py b/webapp_utils.py index cd68b6963..0647d899b 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -533,8 +533,15 @@ def htmlHeaderWithExternalStyle(cssFilename: str, instanceTitle: str, '\n' + \ ' \n' + \ ' \n' + \ - ' \n' + \ + ' \n' + \ ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ ' \n' + \ metadata + \ ' ' + instanceTitle + '\n' + \ @@ -545,6 +552,7 @@ def htmlHeaderWithExternalStyle(cssFilename: str, instanceTitle: str, def htmlHeaderWithPersonMarkup(cssFilename: str, instanceTitle: str, actorJson: {}, city: str, + contentLicenseUrl: str, lang='en') -> str: """html header which includes person markup https://schema.org/Person @@ -658,7 +666,6 @@ def htmlHeaderWithPersonMarkup(cssFilename: str, instanceTitle: str, ' "url": "' + actorJson['id'] + '"\n' + \ ' },\n' - licenseUrl = 'https://creativecommons.org/licenses/by/4.0' profileMarkup = \ ' \n' @@ -788,8 +795,11 @@ def htmlHeaderWithWebsiteMarkup(cssFilename: str, instanceTitle: str, def htmlHeaderWithBlogMarkup(cssFilename: str, instanceTitle: str, httpPrefix: str, domain: str, nickname: str, - systemLanguage: str, published: str, - title: str, snippet: str) -> str: + systemLanguage: str, + published: str, modified: str, + title: str, snippet: str, + translate: {}, url: str, + contentLicenseUrl: str) -> str: """html header which includes blog post markup https://schema.org/BlogPosting """ @@ -798,7 +808,6 @@ def htmlHeaderWithBlogMarkup(cssFilename: str, instanceTitle: str, # license for content on the site may be different from # the software license - contentLicenseUrl = 'https://creativecommons.org/licenses/by/3.0' blogMarkup = \ ' \n' + + ogMetadata = \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + htmlStr = \ - htmlHeaderWithExternalStyle(cssFilename, instanceTitle, blogMarkup, - systemLanguage) + htmlHeaderWithExternalStyle(cssFilename, instanceTitle, + ogMetadata + blogMarkup, systemLanguage) return htmlStr