diff --git a/auth.py b/auth.py index a9b83a938..7ac45cecf 100644 --- a/auth.py +++ b/auth.py @@ -121,7 +121,7 @@ def storeBasicCredentials(baseDir: str, nickname: str, password: str) -> bool: if os.path.isfile(passwordFile): if nickname + ':' in open(passwordFile).read(): with open(passwordFile, "r") as fin: - with open(passwordFile + '.new', "w") as fout: + with open(passwordFile + '.new', 'w+') as fout: for line in fin: if not line.startswith(nickname + ':'): fout.write(line) @@ -133,7 +133,7 @@ def storeBasicCredentials(baseDir: str, nickname: str, password: str) -> bool: with open(passwordFile, 'a+') as passfile: passfile.write(storeStr + '\n') else: - with open(passwordFile, "w") as passfile: + with open(passwordFile, 'w+') as passfile: passfile.write(storeStr + '\n') return True @@ -145,7 +145,7 @@ def removePassword(baseDir: str, nickname: str) -> None: passwordFile = baseDir + '/accounts/passwords' if os.path.isfile(passwordFile): with open(passwordFile, "r") as fin: - with open(passwordFile + '.new', "w") as fout: + with open(passwordFile + '.new', 'w+') as fout: for line in fin: if not line.startswith(nickname + ':'): fout.write(line) diff --git a/blog.py b/blog.py index e41aacdff..91db9ca66 100644 --- a/blog.py +++ b/blog.py @@ -73,7 +73,7 @@ def noOfBlogReplies(baseDir: str, httpPrefix: str, translate: {}, if lines and removals: print('Rewriting ' + postFilename + ' to remove ' + str(len(removals)) + ' entries') - with open(postFilename, "w") as f: + with open(postFilename, 'w+') as f: for replyPostId in lines: replyPostId = replyPostId.replace('\n', '').replace('\r', '') if replyPostId not in removals: diff --git a/cache.py b/cache.py index 37d1f4270..0ac180f80 100644 --- a/cache.py +++ b/cache.py @@ -14,7 +14,8 @@ from utils import getFileCaseInsensitive def storePersonInCache(baseDir: str, personUrl: str, - personJson: {}, personCache: {}) -> None: + personJson: {}, personCache: {}, + allowWriteToFile: bool) -> None: """Store an actor in the cache """ currTime = datetime.datetime.utcnow() @@ -26,25 +27,29 @@ def storePersonInCache(baseDir: str, personUrl: str, return # store to file - if os.path.isdir(baseDir+'/cache/actors'): - cacheFilename = baseDir + '/cache/actors/' + \ - personUrl.replace('/', '#')+'.json' - if not os.path.isfile(cacheFilename): - saveJson(personJson, cacheFilename) + if allowWriteToFile: + if os.path.isdir(baseDir+'/cache/actors'): + cacheFilename = baseDir + '/cache/actors/' + \ + personUrl.replace('/', '#')+'.json' + if not os.path.isfile(cacheFilename): + saveJson(personJson, cacheFilename) -def getPersonFromCache(baseDir: str, personUrl: str, personCache: {}) -> {}: +def getPersonFromCache(baseDir: str, personUrl: str, personCache: {}, + allowWriteToFile: bool) -> {}: """Get an actor from the cache """ # if the actor is not in memory then try to load it from file loadedFromFile = False if not personCache.get(personUrl): + # does the person exist as a cached file? cacheFilename = baseDir + '/cache/actors/' + \ personUrl.replace('/', '#')+'.json' if os.path.isfile(getFileCaseInsensitive(cacheFilename)): personJson = loadJson(getFileCaseInsensitive(cacheFilename)) if personJson: - storePersonInCache(baseDir, personUrl, personJson, personCache) + storePersonInCache(baseDir, personUrl, personJson, + personCache, False) loadedFromFile = True if personCache.get(personUrl): diff --git a/daemon.py b/daemon.py index 4300d4d47..3b322a57e 100644 --- a/daemon.py +++ b/daemon.py @@ -1118,22 +1118,20 @@ class PubServer(BaseHTTPRequestHandler): 'epicyon=; SameSite=Strict', callingDomain) - def _benchmarkGETtimings(self, GETstartTime, GETtimings: [], getID: int): - """Updates a list containing how long each segment of GET takes + def _benchmarkGETtimings(self, GETstartTime, GETtimings: {}, + prevGetId: str, currGetId: str): + """Updates a dictionary containing how long each segment of GET takes """ - if self.server.debug: - timeDiff = int((time.time() - GETstartTime) * 1000) - logEvent = False - if timeDiff > 100: - logEvent = True - if GETtimings: - timeDiff = int(timeDiff - int(GETtimings[-1])) - GETtimings.append(str(timeDiff)) - if logEvent: - ctr = 1 - for timeDiff in GETtimings: - print('GET TIMING|' + str(ctr) + '|' + timeDiff) - ctr += 1 + timeDiff = int((time.time() - GETstartTime) * 1000) + logEvent = False + if timeDiff > 100: + logEvent = True + if prevGetId: + if GETtimings.get(prevGetId): + timeDiff = int(timeDiff - int(GETtimings[prevGetId])) + GETtimings[currGetId] = str(timeDiff) + if logEvent: + print('GET TIMING ' + currGetId + ' = ' + str(timeDiff)) def _benchmarkPOSTtimings(self, POSTstartTime, POSTtimings: [], postID: int): @@ -1202,7 +1200,9 @@ class PubServer(BaseHTTPRequestHandler): return GETstartTime = time.time() - GETtimings = [] + GETtimings = {} + + self._benchmarkGETtimings(GETstartTime, GETtimings, None, 'start') # Since fediverse crawlers are quite active, # make returning info to them high priority @@ -1210,13 +1210,16 @@ class PubServer(BaseHTTPRequestHandler): if self._nodeinfo(callingDomain): return - self._benchmarkGETtimings(GETstartTime, GETtimings, 1) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'start', '_nodeinfo(callingDomain)') # minimal mastodon api if self._mastoApi(callingDomain): return - self._benchmarkGETtimings(GETstartTime, GETtimings, 2) + self._benchmarkGETtimings(GETstartTime, GETtimings, + '_nodeinfo(callingDomain)', + '_mastoApi(callingDomain)') if self.path == '/logout': msg = \ @@ -1224,9 +1227,14 @@ class PubServer(BaseHTTPRequestHandler): self.server.baseDir, False).encode('utf-8') self._logout_headers('text/html', len(msg), callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + '_nodeinfo(callingDomain)', + 'logout') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 3) + self._benchmarkGETtimings(GETstartTime, GETtimings, + '_nodeinfo(callingDomain)', + 'show logout') # replace https://domain/@nick with https://domain/users/nick if self.path.startswith('/@'): @@ -1248,7 +1256,8 @@ class PubServer(BaseHTTPRequestHandler): if self.headers.get('Cookie'): cookie = self.headers['Cookie'] - self._benchmarkGETtimings(GETstartTime, GETtimings, 4) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show logout', 'get cookie') # manifest for progressive web apps if '/manifest.json' in self.path: @@ -1341,6 +1350,8 @@ class PubServer(BaseHTTPRequestHandler): self._write(msg) if self.server.debug: print('Sent manifest: ' + callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show logout', 'send manifest') return # favicon image @@ -1400,7 +1411,8 @@ class PubServer(BaseHTTPRequestHandler): else: print('GET Not authorized') - self._benchmarkGETtimings(GETstartTime, GETtimings, 5) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show logout', 'isAuthorized') if not self.server.session: print('Starting new session during GET') @@ -1408,9 +1420,12 @@ class PubServer(BaseHTTPRequestHandler): if not self.server.session: print('ERROR: GET failed to create session duing GET') self._404() + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'isAuthorized', 'session fail') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 6) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'isAuthorized', 'create session') # is this a html request? htmlGET = False @@ -1433,6 +1448,9 @@ class PubServer(BaseHTTPRequestHandler): self._400() return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'create session', 'hasAccept') + # get fonts if '/fonts/' in self.path: fontStr = self.path.split('/fonts/')[1] @@ -1464,6 +1482,9 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('font sent from cache: ' + self.path + ' ' + callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'hasAccept', + 'send font from cache') return else: if os.path.isfile(fontFilename): @@ -1478,13 +1499,17 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('font sent from file: ' + self.path + ' ' + callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'hasAccept', + 'send font from file') return if self.server.debug: print('font not found: ' + self.path + ' ' + callingDomain) self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 7) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'hasAccept', 'fonts') # treat shared inbox paths consistently if self.path == '/sharedInbox' or \ @@ -1498,7 +1523,8 @@ class PubServer(BaseHTTPRequestHandler): self.path = '/inbox' - self._benchmarkGETtimings(GETstartTime, GETtimings, 8) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'fonts', 'sharedInbox enabled') # RSS 2.0 if self.path.startswith('/blog/') and \ @@ -1538,6 +1564,9 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('Sent rss2 feed: ' + self.path + ' ' + callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'sharedInbox enabled', + 'blog rss2') return if self.server.debug: print('Failed to get rss2 feed: ' + @@ -1545,6 +1574,9 @@ class PubServer(BaseHTTPRequestHandler): self._404() return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'sharedInbox enabled', 'rss2 done') + # RSS 3.0 if self.path.startswith('/blog/') and \ self.path.endswith('/rss.txt'): @@ -1582,6 +1614,9 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('Sent rss3 feed: ' + self.path + ' ' + callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'sharedInbox enabled', + 'blog rss3') return if self.server.debug: print('Failed to get rss3 feed: ' + @@ -1589,6 +1624,9 @@ class PubServer(BaseHTTPRequestHandler): self._404() return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'sharedInbox enabled', 'rss3 done') + # show the main blog page if htmlGET and (self.path == '/blog' or self.path == '/blog/' or @@ -1617,10 +1655,15 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'rss3 done', 'blog view') return self._404() return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'rss3 done', 'blog view done') + # show a particular page of blog entries # for a particular account if htmlGET and self.path.startswith('/blog/'): @@ -1665,6 +1708,8 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'blog view done', 'blog page') return self._404() return @@ -1687,6 +1732,14 @@ class PubServer(BaseHTTPRequestHandler): len(msg), None, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'blog page', + 'registered devices') + return + + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'blog view done', + 'registered devices done') if htmlGET and '/users/' in self.path: # show the person options screen with view/follow/block/report @@ -1715,7 +1768,8 @@ class PubServer(BaseHTTPRequestHandler): emailAddress = None actorJson = getPersonFromCache(self.server.baseDir, optionsActor, - self.server.personCache) + self.server.personCache, + True) if actorJson: donateUrl = getDonationUrl(actorJson) xmppAddress = getXmppAddress(actorJson) @@ -1742,6 +1796,9 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'registered devices done', + 'person options') return if callingDomain.endswith('.onion') and \ self.server.onionDomain: @@ -1759,6 +1816,9 @@ class PubServer(BaseHTTPRequestHandler): callingDomain) return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'registered devices done', + 'person options done') # show blog post blogFilename, nickname = \ self._pathContainsBlogLink(self.server.baseDir, @@ -1781,11 +1841,16 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'person options done', + 'blog post 2') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 9) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'person options done', + 'blog post 2 done') # remove a shared item if htmlGET and '?rmshare=' in self.path: @@ -1812,9 +1877,14 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'blog post 2 done', + 'remove shared item') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 10) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'blog post 2 done', + 'remove shared item done') if self.path.startswith('/terms'): if callingDomain.endswith('.onion') and \ @@ -1832,9 +1902,14 @@ class PubServer(BaseHTTPRequestHandler): msg = msg.encode('utf-8') self._login_headers('text/html', len(msg), callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'blog post 2 done', + 'terms of service shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 11) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'blog post 2 done', + 'terms of service done') # show a list of who you are following if htmlGET and authorized and '/users/' in self.path and \ @@ -1849,8 +1924,15 @@ class PubServer(BaseHTTPRequestHandler): msg = htmlFollowingList(self.server.baseDir, followingFilename) self._login_headers('text/html', len(msg), callingDomain) self._write(msg.encode('utf-8')) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'terms of service done', + 'following accounts shown') return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'terms of service done', + 'following accounts done') + if self.path.endswith('/about'): if callingDomain.endswith('.onion'): msg = \ @@ -1871,15 +1953,22 @@ class PubServer(BaseHTTPRequestHandler): msg = msg.encode('utf-8') self._login_headers('text/html', len(msg), callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'following accounts done', + 'show about screen') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 12) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'following accounts done', + 'show about screen done') # send robots.txt if asked if self._robotsTxt(): return - self._benchmarkGETtimings(GETstartTime, GETtimings, 13) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show about screen done', + 'robots txt') # if not authorized then show the login screen if htmlGET and self.path != '/login' and \ @@ -1928,9 +2017,14 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(self.server.httpPrefix + '://' + self.server.domainFull + '/login', None, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'robots txt', + 'show login screen') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 14) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'robots txt', + 'show login screen done') # get css # Note that this comes before the busy flag to avoid conflicts @@ -1950,11 +2044,16 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/css', len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show login screen done', + 'show profile.css') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 15) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show login screen done', + 'profile.css done') # manifest images used to create a home screen icon # when selecting "add to home screen" in browsers @@ -1992,10 +2091,17 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'profile.css done', + 'manifest logo shown') return self._404() return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'profile.css done', + 'manifest logo done') + # manifest images used to show example screenshots # for use by app stores if self.path == '/screenshot1.jpg' or \ @@ -2025,10 +2131,17 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'manifest logo done', + 'show screenshot') return self._404() return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'manifest logo done', + 'show screenshot done') + # image on login screen or qrcode if self.path == '/login.png' or \ self.path == '/login.gif' or \ @@ -2061,11 +2174,16 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show screenshot done', + 'login screen logo') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 16) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show screenshot done', + 'login screen logo done') # QR code for account handle if '/users/' in self.path and \ @@ -2099,10 +2217,17 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'login screen logo done', + 'account qrcode') return self._404() return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'login screen logo done', + 'account qrcode done') + # search screen banner image if '/users/' in self.path and \ self.path.endswith('/search_banner.png'): @@ -2132,11 +2257,16 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'account qrcode done', + 'search screen banner') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 17) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'account qrcode done', + 'search screen banner done') if '-background.' in self.path: for ext in ('webp', 'gif', 'jpg', 'png'): @@ -2171,11 +2301,18 @@ class PubServer(BaseHTTPRequestHandler): bgBinary, cookie, callingDomain) self._write(bgBinary) + self._benchmarkGETtimings(GETstartTime, + GETtimings, + 'search screen ' + + 'banner done', + 'background shown') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 18) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'search screen banner done', + 'background shown done') # emoji images if '/emoji/' in self.path: @@ -2205,11 +2342,16 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'background shown done', + 'show emoji') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 19) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'background shown done', + 'show emoji done') # show media # Note that this comes before the busy flag to avoid conflicts @@ -2250,11 +2392,16 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show emoji done', + 'show media') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 20) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show emoji done', + 'show media done') # show shared item images # Note that this comes before the busy flag to avoid conflicts @@ -2285,11 +2432,16 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show media done', + 'share files shown') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 21) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show media done', + 'share files done') # icon images # Note that this comes before the busy flag to avoid conflicts @@ -2320,11 +2472,16 @@ class PubServer(BaseHTTPRequestHandler): callingDomain) self._write(mediaBinary) self.server.iconsCache[mediaStr] = mediaBinary + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show files done', + 'icon shown') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 22) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show files done', + 'icon shown done') # cached avatar images # Note that this comes before the busy flag to avoid conflicts @@ -2367,11 +2524,16 @@ class PubServer(BaseHTTPRequestHandler): # self._404() return self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'icon shown done', + 'avatar shown') return self._404() return - self._benchmarkGETtimings(GETstartTime, GETtimings, 23) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'icon shown done', + 'avatar shown done') # show avatar or background image # Note that this comes before the busy flag to avoid conflicts @@ -2411,9 +2573,14 @@ class PubServer(BaseHTTPRequestHandler): mediaBinary, cookie, callingDomain) self._write(mediaBinary) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'icon shown done', + 'avatar background shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 24) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'icon shown done', + 'avatar background shown done') # This busy state helps to avoid flooding # Resources which are expected to be called from a web page @@ -2429,7 +2596,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.lastGET = currTimeGET self.server.GETbusy = True - self._benchmarkGETtimings(GETstartTime, GETtimings, 25) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'avatar background shown done', + 'GET busy time') if not self._permittedDir(self.path): if self.server.debug: @@ -2437,12 +2606,18 @@ class PubServer(BaseHTTPRequestHandler): self._404() self.server.GETbusy = False return + # get webfinger endpoint for a person if self._webfinger(callingDomain): self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'GET busy time', + 'webfinger called') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 26) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'GET busy time', + 'permitted directory') if self.path.startswith('/login') or \ (self.path == '/' and not authorized): @@ -2452,9 +2627,14 @@ class PubServer(BaseHTTPRequestHandler): self._login_headers('text/html', len(msg), callingDomain) self._write(msg) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'permitted directory', + 'login shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 27) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'permitted directory', + 'login shown done') # hashtag search if self.path.startswith('/tags/') or \ @@ -2516,9 +2696,14 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(originPathStrAbsolute + '/search', cookie, callingDomain) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'login shown done', + 'hashtag search') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 28) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'login shown done', + 'hashtag search done') # show or hide buttons in the web interface if htmlGET and '/users/' in self.path and \ @@ -2551,9 +2736,14 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'hashtag search done', + 'search screen shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 29) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'hashtag search done', + 'search screen shown done') # Show the calendar for a user if htmlGET and '/users/' in self.path: @@ -2566,8 +2756,15 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'search screen shown done', + 'calendar shown') return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'search screen shown done', + 'calendar shown done') + # Show confirmation for deleting a calendar event if htmlGET and '/users/' in self.path: if '/eventdelete' in self.path and \ @@ -2614,6 +2811,9 @@ class PubServer(BaseHTTPRequestHandler): self.path.split('/eventdelete')[0] self._redirect_headers(actor + '/calendar', cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'calendar shown done', + 'calendar delete shown') return msg = msg.encode('utf-8') self._set_headers('text/html', len(msg), @@ -2622,7 +2822,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 30) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'calendar shown done', + 'calendar delete shown done') # search for emoji by name if htmlGET and '/users/' in self.path: @@ -2635,9 +2837,14 @@ class PubServer(BaseHTTPRequestHandler): cookie, callingDomain) self._write(msg) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'calendar delete shown done', + 'emoji search shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 31) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'calendar delete shown done', + 'emoji search shown done') repeatPrivate = False if htmlGET and '?repeatprivate=' in self.path: @@ -2732,9 +2939,14 @@ class PubServer(BaseHTTPRequestHandler): timelineStr + '?page=' + str(pageNumber) + timelineBookmark, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'emoji search shown done', + 'show announce') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 32) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'emoji search shown done', + 'show announce done') if htmlGET and '?unrepeatprivate=' in self.path: self.path = self.path.replace('?unrepeatprivate=', '?unrepeat=') @@ -2823,9 +3035,14 @@ class PubServer(BaseHTTPRequestHandler): timelineStr + '?page=' + str(pageNumber) + timelineBookmark, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show announce done', + 'unannounce') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 33) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show announce done', + 'unannounce done') # send a follow request approval from the web interface if authorized and '/followapprove=' in self.path and \ @@ -2871,10 +3088,15 @@ class PubServer(BaseHTTPRequestHandler): 'http://' + self.server.i2pDomain + originPathStr self._redirect_headers(originPathStrAbsolute, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'unannounce done', + 'follow approve shown') self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 34) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'unannounce done', + 'follow approve done') # deny a follow request from the web interface if authorized and '/followdeny=' in self.path and \ @@ -2911,9 +3133,14 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(originPathStrAbsolute, cookie, callingDomain) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'follow approve done', + 'follow deny shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 35) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'follow approve done', + 'follow deny done') # like from the web interface icon if htmlGET and '?like=' in self.path: @@ -3013,9 +3240,14 @@ class PubServer(BaseHTTPRequestHandler): '?page=' + str(pageNumber) + timelineBookmark, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'follow deny done', + 'like shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 36) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'follow deny done', + 'like shown done') # undo a like from the web interface icon if htmlGET and '?unlike=' in self.path: @@ -3115,9 +3347,14 @@ class PubServer(BaseHTTPRequestHandler): '?page=' + str(pageNumber) + timelineBookmark, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'like shown done', + 'unlike shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 36) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'like shown done', + 'unlike shown done') # bookmark from the web interface icon if htmlGET and '?bookmark=' in self.path: @@ -3205,8 +3442,15 @@ class PubServer(BaseHTTPRequestHandler): '?page=' + str(pageNumber) + timelineBookmark, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'unlike shown done', + 'bookmark shown') return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'unlike shown done', + 'bookmark shown done') + # undo a bookmark from the web interface icon if htmlGET and '?unbookmark=' in self.path: pageNumber = 1 @@ -3293,9 +3537,14 @@ class PubServer(BaseHTTPRequestHandler): '?page=' + str(pageNumber) + timelineBookmark, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'bookmark shown done', + 'unbookmark shown') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 37) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'bookmark shown done', + 'unbookmark shown done') # delete a post from the web interface icon if htmlGET and '?delete=' in self.path: @@ -3389,8 +3638,15 @@ class PubServer(BaseHTTPRequestHandler): actor = 'http://' + self.server.i2pDomain + usersPath self._redirect_headers(actor + '/' + timelineStr, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'unbookmark shown done', + 'delete shown') return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'unbookmark shown done', + 'delete shown done') + # mute a post from the web interface icon if htmlGET and '?mute=' in self.path: pageNumber = 1 @@ -3436,8 +3692,15 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(actor + '/' + timelineStr + timelineBookmark, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'delete shown done', + 'post muted') return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'delete shown done', + 'post muted done') + # unmute a post from the web interface icon if htmlGET and '?unmute=' in self.path: pageNumber = 1 @@ -3486,8 +3749,15 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(actor + '/' + timelineStr + timelineBookmark, cookie, callingDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'post muted done', + 'unmute activated') return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'post muted done', + 'unmute activated done') + # reply from the web interface icon inReplyToUrl = None # replyWithDM = False @@ -3681,9 +3951,14 @@ class PubServer(BaseHTTPRequestHandler): cookie, callingDomain) self._write(msg) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'unmute activated done', + 'new post made') return - self._benchmarkGETtimings(GETstartTime, GETtimings, 38) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'unmute activated done', + 'new post done') # get an individual post from the path /@nickname/statusnumber if '/@' in self.path: @@ -3775,13 +4050,18 @@ class PubServer(BaseHTTPRequestHandler): else: self._404() self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'new post done', + 'individual post shown') return else: self._404() self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 39) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'new post done', + 'individual post done') # get replies to a post /users/nickname/statuses/number/replies if self.path.endswith('/replies') or '/replies?page=' in self.path: @@ -3999,6 +4279,14 @@ class PubServer(BaseHTTPRequestHandler): cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, + GETtimings, + 'indiv' + + 'idual' + + ' post done', + 'post ' + + 'replies ' + + 'done') else: if self._fetchAuthenticated(): msg = \ @@ -4016,7 +4304,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 40) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'individual post done', + 'post replies done') if self.path.endswith('/roles') and '/users/' in self.path: namedStatus = self.path.split('/users/')[1] @@ -4066,6 +4356,11 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, + GETtimings, + 'post replies ' + + 'done', + 'show roles') else: if self._fetchAuthenticated(): msg = json.dumps(actorJson['roles'], @@ -4080,6 +4375,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'post replies done', + 'show roles done') + # show skills on the profile page if self.path.endswith('/skills') and '/users/' in self.path: namedStatus = self.path.split('/users/')[1] @@ -4131,6 +4430,11 @@ class PubServer(BaseHTTPRequestHandler): cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, + GETtimings, + 'post roles ' + + 'done', + 'show skills') else: if self._fetchAuthenticated(): msg = json.dumps(actorJson['skills'], @@ -4158,7 +4462,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 41) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'post roles done', + 'show skills done') # get an individual post from the path # /users/nickname/statuses/number @@ -4240,6 +4546,11 @@ class PubServer(BaseHTTPRequestHandler): cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, + GETtimings, + 'show skills ' + + 'done', + 'show status') else: if self._fetchAuthenticated(): msg = json.dumps(postJsonObject, @@ -4258,7 +4569,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 42) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show skills done', + 'show status done') # get the inbox for a given person if self.path.endswith('/inbox') or '/inbox?page=' in self.path: @@ -4276,6 +4589,9 @@ class PubServer(BaseHTTPRequestHandler): authorized, self.server.ocapAlways) if inboxFeed: + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show status done', + 'show inbox json') if self._requestHTTP(): nickname = self.path.replace('/users/', '') nickname = nickname.replace('/inbox', '') @@ -4300,6 +4616,10 @@ class PubServer(BaseHTTPRequestHandler): maxPostsInFeed, 'inbox', authorized, self.server.ocapAlways) + self._benchmarkGETtimings(GETstartTime, + GETtimings, + 'show status done', + 'show inbox page') msg = htmlInbox(self.server.defaultTimeline, self.server.recentPostsCache, self.server.maxRecentPosts, @@ -4318,11 +4638,17 @@ class PubServer(BaseHTTPRequestHandler): self.server.projectVersion, self._isMinimal(nickname), self.server.YTReplacementDomain) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show status done', + 'show inbox html') msg = msg.encode('utf-8') self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show status done', + 'show inbox') else: # don't need authenticated fetch here because # there is already the authorization check @@ -4349,7 +4675,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 43) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show status done', + 'show inbox done') # get the direct messages for a given person if self.path.endswith('/dm') or '/dm?page=' in self.path: @@ -4415,6 +4743,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show inbox done', + 'show dms') else: # don't need authenticated fetch here because # there is already the authorization check @@ -4441,7 +4772,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 44) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show inbox done', + 'show dms done') # get the replies for a given person if self.path.endswith('/tlreplies') or '/tlreplies?page=' in self.path: @@ -4506,6 +4839,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show dms done', + 'show replies 2') else: # don't need authenticated fetch here because there is # already the authorization check @@ -4533,7 +4869,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 45) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show dms done', + 'show replies 2 done') # get the media for a given person if self.path.endswith('/tlmedia') or '/tlmedia?page=' in self.path: @@ -4598,6 +4936,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show replies 2 done', + 'show media 2') else: # don't need authenticated fetch here because there is # already the authorization check @@ -4625,6 +4966,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show replies 2 done', + 'show media 2 done') + # get the blogs for a given person if self.path.endswith('/tlblogs') or '/tlblogs?page=' in self.path: if '/users/' in self.path: @@ -4688,6 +5033,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show media 2 done', + 'show blogs 2') else: # don't need authenticated fetch here because there is # already the authorization check @@ -4715,7 +5063,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 46) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show media 2 done', + 'show blogs 2 done') # get the shared items timeline for a given person if self.path.endswith('/tlshares') or '/tlshares?page=' in self.path: @@ -4754,6 +5104,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show blogs 2 done', + 'show shares 2') self.server.GETbusy = False return # not the shares timeline @@ -4764,6 +5117,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show blogs 2 done', + 'show shares 2 done') + # get the bookmarks for a given person if self.path.endswith('/tlbookmarks') or \ '/tlbookmarks?page=' in self.path or \ @@ -4832,6 +5189,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show shares 2 done', + 'show bookmarks 2') else: # don't need authenticated fetch here because # there is already the authorization check @@ -4858,6 +5218,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show shares 2 done', + 'show bookmarks 2 done') + # get the events for a given person if self.path.endswith('/tlevents') or \ '/tlevents?page=' in self.path or \ @@ -4930,6 +5294,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show bookmarks 2 done', + 'show events') else: # don't need authenticated fetch here because # there is already the authorization check @@ -4955,7 +5322,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 47) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show bookmarks 2 done', + 'show events done') # get outbox feed for a person outboxFeed = \ @@ -5016,6 +5385,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show events done', + 'show outbox') else: if self._fetchAuthenticated(): msg = json.dumps(outboxFeed, @@ -5030,7 +5402,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 48) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show events done', + 'show outbox done') # get the moderation feed for a moderator if self.path.endswith('/moderation') or \ @@ -5094,6 +5468,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show outbox done', + 'show moderation') else: # don't need authenticated fetch here because # there is already the authorization check @@ -5119,7 +5496,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 49) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show outbox done', + 'show moderation done') shares = \ getSharesFeedForPerson(self.server.baseDir, @@ -5184,6 +5563,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show moderation done', + 'show profile 2') self.server.GETbusy = False return else: @@ -5200,7 +5582,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 50) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show moderation done', + 'show profile 2 done') following = \ getFollowingFeed(self.server.baseDir, self.server.domain, @@ -5265,6 +5649,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show profile 2 done', + 'show profile 3') return else: if self._fetchAuthenticated(): @@ -5279,7 +5666,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 51) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show profile 2 done', + 'show profile 3 done') followers = \ getFollowingFeed(self.server.baseDir, self.server.domain, @@ -5345,6 +5734,9 @@ class PubServer(BaseHTTPRequestHandler): cookie, callingDomain) self._write(msg) self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show profile 3 done', + 'show profile 4') return else: if self._fetchAuthenticated(): @@ -5359,7 +5751,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 52) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show profile 3 done', + 'show profile 4 done') # look up a person getPerson = \ @@ -5397,6 +5791,9 @@ class PubServer(BaseHTTPRequestHandler): len(msg), cookie, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show profile 4 done', + 'show profile posts') else: if self._fetchAuthenticated(): msg = json.dumps(getPerson, @@ -5410,7 +5807,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 53) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show profile 4 done', + 'show profile posts done') # check that a json file was requested if not self.path.endswith('.json'): @@ -5428,7 +5827,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return - self._benchmarkGETtimings(GETstartTime, GETtimings, 54) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show profile posts done', + 'authenticated fetch') # check that the file exists filename = self.server.baseDir + self.path @@ -5442,13 +5843,17 @@ class PubServer(BaseHTTPRequestHandler): len(msg), None, callingDomain) self._write(msg) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'authenticated fetch', + 'arbitrary json') else: if self.server.debug: print('DEBUG: GET Unknown file') self._404() self.server.GETbusy = False - self._benchmarkGETtimings(GETstartTime, GETtimings, 55) + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'arbitrary json', 'end benchmarks') def do_HEAD(self): callingDomain = self.server.domainFull @@ -7097,7 +7502,7 @@ class PubServer(BaseHTTPRequestHandler): if fields.get('followDMs'): if fields['followDMs'] == 'on': followDMsActive = True - with open(followDMsFilename, "w") as fFile: + with open(followDMsFilename, 'w+') as fFile: fFile.write('\n') if not followDMsActive: if os.path.isfile(followDMsFilename): @@ -7111,21 +7516,39 @@ class PubServer(BaseHTTPRequestHandler): if fields.get('removeTwitter'): if fields['removeTwitter'] == 'on': removeTwitterActive = True - with open(removeTwitterFilename, "w") as rFile: + with open(removeTwitterFilename, 'w+') as rFile: rFile.write('\n') if not removeTwitterActive: if os.path.isfile(removeTwitterFilename): os.remove(removeTwitterFilename) - # notify about new Likes + # hide Like button + hideLikeButtonFile = \ + self.server.baseDir + '/accounts/' + \ + nickname + '@' + self.server.domain + \ + '/.hideLikeButton' notifyLikesFilename = \ self.server.baseDir + '/accounts/' + \ nickname + '@' + self.server.domain + \ '/.notifyLikes' + hideLikeButtonActive = False + if fields.get('hideLikeButton'): + if fields['hideLikeButton'] == 'on': + hideLikeButtonActive = True + with open(hideLikeButtonFile, 'w+') as rFile: + rFile.write('\n') + # remove notify likes selection + if os.path.isfile(notifyLikesFilename): + os.remove(notifyLikesFilename) + if not hideLikeButtonActive: + if os.path.isfile(hideLikeButtonFile): + os.remove(hideLikeButtonFile) + # notify about new Likes notifyLikesActive = False if fields.get('notifyLikes'): - if fields['notifyLikes'] == 'on': + if fields['notifyLikes'] == 'on' and \ + not hideLikeButtonActive: notifyLikesActive = True - with open(notifyLikesFilename, "w") as rFile: + with open(notifyLikesFilename, 'w+') as rFile: rFile.write('\n') if not notifyLikesActive: if os.path.isfile(notifyLikesFilename): @@ -7162,7 +7585,7 @@ class PubServer(BaseHTTPRequestHandler): nickname + '@' + self.server.domain + \ '/filters.txt' if fields.get('filteredWords'): - with open(filterFilename, "w") as filterfile: + with open(filterFilename, 'w+') as filterfile: filterfile.write(fields['filteredWords']) else: if os.path.isfile(filterFilename): @@ -7173,7 +7596,7 @@ class PubServer(BaseHTTPRequestHandler): nickname + '@' + self.server.domain + \ '/replacewords.txt' if fields.get('switchWords'): - with open(switchFilename, "w") as switchfile: + with open(switchFilename, 'w+') as switchfile: switchfile.write(fields['switchWords']) else: if os.path.isfile(switchFilename): @@ -7184,7 +7607,7 @@ class PubServer(BaseHTTPRequestHandler): nickname + '@' + self.server.domain + \ '/blocking.txt' if fields.get('blocked'): - with open(blockedFilename, "w") as blockedfile: + with open(blockedFilename, 'w+') as blockedfile: blockedfile.write(fields['blocked']) else: if os.path.isfile(blockedFilename): @@ -7195,7 +7618,7 @@ class PubServer(BaseHTTPRequestHandler): nickname + '@' + self.server.domain + \ '/allowedinstances.txt' if fields.get('allowedInstances'): - with open(allowedInstancesFilename, "w") as aFile: + with open(allowedInstancesFilename, 'w+') as aFile: aFile.write(fields['allowedInstances']) else: if os.path.isfile(allowedInstancesFilename): @@ -7206,7 +7629,7 @@ class PubServer(BaseHTTPRequestHandler): nickname + '@' + self.server.domain + \ '/gitprojects.txt' if fields.get('gitProjects'): - with open(gitProjectsFilename, "w") as aFile: + with open(gitProjectsFilename, 'w+') as aFile: aFile.write(fields['gitProjects'].lower()) else: if os.path.isfile(gitProjectsFilename): @@ -7230,7 +7653,8 @@ class PubServer(BaseHTTPRequestHandler): # personCache in memory storePersonInCache(self.server.baseDir, actorJson['id'], actorJson, - self.server.personCache) + self.server.personCache, + True) # clear any cached images for this actor idStr = actorJson['id'].replace('/', '-') removeAvatarFromCache(self.server.baseDir, idStr) diff --git a/follow.py b/follow.py index a2da2d991..6b764df37 100644 --- a/follow.py +++ b/follow.py @@ -210,7 +210,7 @@ def unfollowPerson(baseDir: str, nickname: str, domain: str, return with open(filename, "r") as f: lines = f.readlines() - with open(filename, "w") as f: + with open(filename, 'w+') as f: for line in lines: if line.strip("\n").strip("\r").lower() != handleToUnfollowLower: f.write(line) diff --git a/git.py b/git.py index 68680e33e..dda0f31a0 100644 --- a/git.py +++ b/git.py @@ -210,12 +210,12 @@ def receiveGitPatch(baseDir: str, nickname: str, domain: str, return False patchStr = \ gitAddFromHandle(patchStr, '@' + fromNickname + '@' + fromDomain) - with open(patchFilename, "w") as patchFile: + with open(patchFilename, 'w+') as patchFile: patchFile.write(patchStr) patchNotifyFilename = \ baseDir + '/accounts/' + \ nickname + '@' + domain + '/.newPatchContent' - with open(patchNotifyFilename, "w") as patchFile: + with open(patchNotifyFilename, 'w+') as patchFile: patchFile.write(patchStr) return True return False diff --git a/happening.py b/happening.py index 1c81e0d5c..6f1d2b1e7 100644 --- a/happening.py +++ b/happening.py @@ -244,7 +244,7 @@ def getTodaysEvents(baseDir: str, nickname: str, domain: str, # if some posts have been deleted then regenerate the calendar file if recreateEventsFile: - calendarFile = open(calendarFilename, "w") + calendarFile = open(calendarFilename, 'w+') for postId in calendarPostIds: calendarFile.write(postId + '\n') calendarFile.close() @@ -412,7 +412,7 @@ def getThisWeeksEvents(baseDir: str, nickname: str, domain: str) -> {}: # if some posts have been deleted then regenerate the calendar file if recreateEventsFile: - calendarFile = open(calendarFilename, "w") + calendarFile = open(calendarFilename, 'w+') for postId in calendarPostIds: calendarFile.write(postId + '\n') calendarFile.close() @@ -494,7 +494,7 @@ def getCalendarEvents(baseDir: str, nickname: str, domain: str, # if some posts have been deleted then regenerate the calendar file if recreateEventsFile: - calendarFile = open(calendarFilename, "w") + calendarFile = open(calendarFilename, 'w+') for postId in calendarPostIds: calendarFile.write(postId + '\n') calendarFile.close() diff --git a/inbox.py b/inbox.py index ced05e1f7..107865bda 100644 --- a/inbox.py +++ b/inbox.py @@ -199,7 +199,8 @@ def getPersonPubKey(baseDir: str, session, personUrl: str, if debug: print('DEBUG: Obtaining public key for shared inbox') personUrl = personUrl.replace('/users/inbox', '/inbox') - personJson = getPersonFromCache(baseDir, personUrl, personCache) + personJson = \ + getPersonFromCache(baseDir, personUrl, personCache, True) if not personJson: if debug: print('DEBUG: Obtaining public key for ' + personUrl) @@ -228,7 +229,7 @@ def getPersonPubKey(baseDir: str, session, personUrl: str, if debug: print('DEBUG: Public key not found for ' + personUrl) - storePersonInCache(baseDir, personUrl, personJson, personCache) + storePersonInCache(baseDir, personUrl, personJson, personCache, True) return pubKey @@ -272,6 +273,8 @@ def inboxPermittedMessage(domain: str, messageJson: {}, return False if messageJson['object'].get('inReplyTo'): inReplyTo = messageJson['object']['inReplyTo'] + if not isinstance(inReplyTo, str): + return False if not urlPermitted(inReplyTo, federationList, "inbox:write"): return False @@ -863,7 +866,8 @@ def personReceiveUpdate(baseDir: str, 'cached actor when updating') return False # save to cache in memory - storePersonInCache(baseDir, personJson['id'], personJson, personCache) + storePersonInCache(baseDir, personJson['id'], personJson, + personCache, True) # save to cache on file if saveJson(personJson, actorFilename): print('actor updated for ' + personJson['id']) @@ -1596,6 +1600,8 @@ def populateReplies(baseDir: str, httpPrefix: str, domain: str, if not messageJson['object'].get('to'): return False replyTo = messageJson['object']['inReplyTo'] + if not isinstance(replyTo, str): + return False if debug: print('DEBUG: post contains a reply') # is this a reply to a post on this domain? @@ -1636,7 +1642,7 @@ def populateReplies(baseDir: str, httpPrefix: str, domain: str, repliesFile.write(messageId + '\n') repliesFile.close() else: - repliesFile = open(postRepliesFilename, "w") + repliesFile = open(postRepliesFilename, 'w+') repliesFile.write(messageId + '\n') repliesFile.close() return True @@ -1760,6 +1766,9 @@ def obtainAvatarForReplyPost(session, baseDir: str, httpPrefix: str, if not lookupActor: return + if not isinstance(lookupActor, str): + return + if not ('/users/' in lookupActor or '/accounts/' in lookupActor or '/channel/' in lookupActor or @@ -2355,13 +2364,17 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, if nickname != 'inbox': # replies index will be updated updateIndexList.append('tlreplies') - if not isMuted(baseDir, nickname, domain, - postJsonObject['object']['inReplyTo']): - replyNotify(baseDir, handle, - httpPrefix + '://' + domain + - '/users/' + nickname + '/tlreplies') - else: - isReplyToMutedPost = True + inReplyTo = postJsonObject['object']['inReplyTo'] + if inReplyTo: + if isinstance(inReplyTo, str): + if not isMuted(baseDir, nickname, domain, + inReplyTo): + replyNotify(baseDir, handle, + httpPrefix + '://' + domain + + '/users/' + nickname + + '/tlreplies') + else: + isReplyToMutedPost = True if isImageMedia(session, baseDir, httpPrefix, nickname, domain, postJsonObject, @@ -2386,7 +2399,7 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, # This enables you to ignore a threat that's getting boring if isReplyToMutedPost: print('MUTE REPLY: ' + destinationFilename) - muteFile = open(destinationFilename + '.muted', "w") + muteFile = open(destinationFilename + '.muted', 'w+') if muteFile: muteFile.write('\n') muteFile.close() diff --git a/person.py b/person.py index 0afdc7b14..af7020df6 100644 --- a/person.py +++ b/person.py @@ -350,7 +350,7 @@ def createPersonBase(baseDir: str, nickname: str, domain: str, port: int, if not os.path.isdir(baseDir + privateKeysSubdir): os.mkdir(baseDir + privateKeysSubdir) filename = baseDir + privateKeysSubdir + '/' + handle + '.key' - with open(filename, "w") as text_file: + with open(filename, 'w+') as text_file: print(privateKeyPem, file=text_file) # save the public key @@ -358,7 +358,7 @@ def createPersonBase(baseDir: str, nickname: str, domain: str, port: int, if not os.path.isdir(baseDir + publicKeysSubdir): os.mkdir(baseDir + publicKeysSubdir) filename = baseDir + publicKeysSubdir + '/' + handle + '.pem' - with open(filename, "w") as text_file: + with open(filename, 'w+') as text_file: print(publicKeyPem, file=text_file) if password: @@ -454,22 +454,22 @@ def createPerson(baseDir: str, nickname: str, domain: str, port: int, setRole(baseDir, nickname, domain, 'instance', 'delegator') setConfigParam(baseDir, 'admin', nickname) + if not os.path.isdir(baseDir + '/accounts'): + os.mkdir(baseDir + '/accounts') + if not os.path.isdir(baseDir + '/accounts/' + nickname + '@' + domain): + os.mkdir(baseDir + '/accounts/' + nickname + '@' + domain) + if manualFollowerApproval: followDMsFilename = baseDir + '/accounts/' + \ nickname + '@' + domain + '/.followDMs' - with open(followDMsFilename, "w") as fFile: + with open(followDMsFilename, 'w+') as fFile: fFile.write('\n') # notify when posts are liked notifyLikesFilename = baseDir + '/accounts/' + \ nickname + '@' + domain + '/.notifyLikes' - with open(notifyLikesFilename, "w") as fFile: - fFile.write('\n') - - if not os.path.isdir(baseDir + '/accounts'): - os.mkdir(baseDir + '/accounts') - if not os.path.isdir(baseDir + '/accounts/' + nickname + '@' + domain): - os.mkdir(baseDir + '/accounts/' + nickname + '@' + domain) + with open(notifyLikesFilename, 'w+') as nFile: + nFile.write('\n') if os.path.isfile(baseDir + '/img/default-avatar.png'): copyfile(baseDir + '/img/default-avatar.png', diff --git a/posts.py b/posts.py index 1517fea4d..95026c113 100644 --- a/posts.py +++ b/posts.py @@ -209,7 +209,8 @@ def getPersonBox(baseDir: str, session, wfRequest: {}, personUrl = httpPrefix + '://' + domain + '/users/' + nickname if not personUrl: return None, None, None, None, None, None, None, None - personJson = getPersonFromCache(baseDir, personUrl, personCache) + personJson = \ + getPersonFromCache(baseDir, personUrl, personCache, True) if not personJson: if '/channel/' in personUrl or '/accounts/' in personUrl: asHeader = { @@ -265,7 +266,7 @@ def getPersonBox(baseDir: str, session, wfRequest: {}, if personJson.get('name'): displayName = personJson['name'] - storePersonInCache(baseDir, personUrl, personJson, personCache) + storePersonInCache(baseDir, personUrl, personJson, personCache, True) return boxJson, pubKeyId, pubKey, personId, sharedInbox, \ capabilityAcquisition, avatarUrl, displayName @@ -388,15 +389,16 @@ def getPosts(session, outboxUrl: str, maxPosts: int, inReplyTo = '' if item['object'].get('inReplyTo'): if item['object']['inReplyTo']: - # No replies to non-permitted domains - if not urlPermitted(item['object']['inReplyTo'], - federationList, - "objects:read"): - if debug: - print('url not permitted ' + - item['object']['inReplyTo']) - continue - inReplyTo = item['object']['inReplyTo'] + if isinstance(item['object']['inReplyTo'], str): + # No replies to non-permitted domains + if not urlPermitted(item['object']['inReplyTo'], + federationList, + "objects:read"): + if debug: + print('url not permitted ' + + item['object']['inReplyTo']) + continue + inReplyTo = item['object']['inReplyTo'] conversation = '' if item['object'].get('conversation'): @@ -483,10 +485,11 @@ def getPostDomains(session, outboxUrl: str, maxPosts: int, if not isinstance(item['object'], dict): continue if item['object'].get('inReplyTo'): - postDomain, postPort = \ - getDomainFromActor(item['object']['inReplyTo']) - if postDomain not in postDomains: - postDomains.append(postDomain) + if isinstance(item['object']['inReplyTo'], str): + postDomain, postPort = \ + getDomainFromActor(item['object']['inReplyTo']) + if postDomain not in postDomains: + postDomains.append(postDomain) if item['object'].get('tag'): for tagItem in item['object']['tag']: @@ -2675,8 +2678,9 @@ def isReply(postJsonObject: {}, actor: str) -> bool: postJsonObject['object']['type'] != 'Article': return False if postJsonObject['object'].get('inReplyTo'): - if postJsonObject['object']['inReplyTo'].startswith(actor): - return True + if isinstance(postJsonObject['object']['inReplyTo'], str): + if postJsonObject['object']['inReplyTo'].startswith(actor): + return True if not postJsonObject['object'].get('tag'): return False if not isinstance(postJsonObject['object']['tag'], list): @@ -3532,7 +3536,7 @@ def mutePost(baseDir: str, nickname: str, domain: str, postId: str, return print('MUTE: ' + postFilename) - muteFile = open(postFilename + '.muted', "w") + muteFile = open(postFilename + '.muted', 'w+') if muteFile: muteFile.write('\n') muteFile.close() diff --git a/question.py b/question.py index a52a9bb09..9618154ae 100644 --- a/question.py +++ b/question.py @@ -25,6 +25,8 @@ def questionUpdateVotes(baseDir: str, nickname: str, domain: str, return None if not replyJson['object']['inReplyTo']: return None + if not isinstance(replyJson['object']['inReplyTo'], str): + return None if not replyJson['object'].get('name'): return None inReplyTo = replyJson['object']['inReplyTo'] @@ -64,7 +66,7 @@ def questionUpdateVotes(baseDir: str, nickname: str, domain: str, votersFilename = questionPostFilename.replace('.json', '.voters') if not os.path.isfile(votersFilename): # create a new voters file - votersFile = open(votersFilename, "w") + votersFile = open(votersFilename, 'w+') if votersFile: votersFile.write(replyJson['actor'] + votersFileSeparator + @@ -97,7 +99,7 @@ def questionUpdateVotes(baseDir: str, nickname: str, domain: str, else: newlines.append(voteLine) if saveVotersFile: - with open(votersFilename, "w") as votersFile: + with open(votersFilename, 'w+') as votersFile: for voteLine in newlines: votersFile.write(voteLine) else: diff --git a/roles.py b/roles.py index 967997b07..f2bc57374 100644 --- a/roles.py +++ b/roles.py @@ -52,7 +52,7 @@ def addModerator(baseDir: str, nickname: str, domain: str) -> None: if moderator == nickname: return lines.append(nickname) - with open(moderatorsFile, "w") as f: + with open(moderatorsFile, 'w+') as f: for moderator in lines: moderator = moderator.strip('\n').strip('\r') if len(moderator) > 1: @@ -74,7 +74,7 @@ def removeModerator(baseDir: str, nickname: str): return with open(moderatorsFile, "r") as f: lines = f.readlines() - with open(moderatorsFile, "w") as f: + with open(moderatorsFile, 'w+') as f: for moderator in lines: moderator = moderator.strip('\n').strip('\r') if len(moderator) > 1 and moderator != nickname: diff --git a/tests.py b/tests.py index 59b97c462..13e3a2892 100644 --- a/tests.py +++ b/tests.py @@ -209,8 +209,8 @@ def testCache(): "test": "This is a test" } personCache = {} - storePersonInCache(None, personUrl, personJson, personCache) - result = getPersonFromCache(None, personUrl, personCache) + storePersonInCache(None, personUrl, personJson, personCache, True) + result = getPersonFromCache(None, personUrl, personCache, True) assert result['id'] == 123456 assert result['test'] == 'This is a test' @@ -2064,6 +2064,9 @@ def testTranslations(): langDict = {} for lang in languagesStr: langJson = loadJson('translations/' + lang + '.json') + if not langJson: + print('Missing language file ' + + 'translations/' + lang + '.json') assert langJson langDict[lang] = langJson diff --git a/translations/ar.json b/translations/ar.json index b6223d1d1..6faa4aa4a 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -282,5 +282,6 @@ "Create a new event": "أنشئ حدثًا جديدًا", "Moderation policy or code of conduct": "سياسة الوسطية أو قواعد السلوك", "Edit event": "تحرير الحدث", - "Notify when posts are liked": "يخطر عندما يتم اعجاب المشاركات" + "Notify when posts are liked": "يخطر عندما يتم اعجاب المشاركات", + "Don't show the Like button": "لا تظهر زر أعجبني" } diff --git a/translations/ca.json b/translations/ca.json index 343576942..c59b79eb8 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -282,5 +282,6 @@ "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", - "Notify when posts are liked": "Notifiqueu-ho quan us agradin les publicacions" + "Notify when posts are liked": "Notifiqueu-ho quan us agradin les publicacions", + "Don't show the Like button": "No mostreu el botó M'agrada" } diff --git a/translations/cy.json b/translations/cy.json index 631892be2..e77c7ecea 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -282,5 +282,6 @@ "Create a new event": "Creu digwyddiad newydd", "Moderation policy or code of conduct": "Polisi cymedroli neu god ymddygiad", "Edit event": "Golygu digwyddiad", - "Notify when posts are liked": "Hysbysu pryd mae swyddi'n cael eu hoffi" + "Notify when posts are liked": "Hysbysu pryd mae swyddi'n cael eu hoffi", + "Don't show the Like button": "Peidiwch â dangos y botwm Hoffi" } diff --git a/translations/de.json b/translations/de.json index 99794eb22..957c54f6b 100644 --- a/translations/de.json +++ b/translations/de.json @@ -282,5 +282,6 @@ "Create a new event": "Erstellen Sie ein neues Ereignis", "Moderation policy or code of conduct": "Moderationsrichtlinie oder Verhaltenskodex", "Edit event": "Ereignis bearbeiten", - "Notify when posts are liked": "Benachrichtigen, wenn Beiträge gefallen" + "Notify when posts are liked": "Benachrichtigen, wenn Beiträge gefallen", + "Don't show the Like button": "Zeigen Sie nicht die Schaltfläche \"Gefällt mir\" an" } diff --git a/translations/en.json b/translations/en.json index 2476be31e..aaa545bc7 100644 --- a/translations/en.json +++ b/translations/en.json @@ -282,5 +282,6 @@ "Create a new event": "Create a new event", "Moderation policy or code of conduct": "Moderation policy or code of conduct", "Edit event": "Edit event", - "Notify when posts are liked": "Notify when posts are liked" + "Notify when posts are liked": "Notify when posts are liked", + "Don't show the Like button": "Don't show the Like button" } diff --git a/translations/es.json b/translations/es.json index a6969342a..07d010a93 100644 --- a/translations/es.json +++ b/translations/es.json @@ -282,5 +282,6 @@ "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", - "Notify when posts are liked": "Notificar cuando les gusten las publicaciones" + "Notify when posts are liked": "Notificar cuando les gusten las publicaciones", + "Don't show the Like button": "No mostrar el botón Me gusta" } diff --git a/translations/fr.json b/translations/fr.json index b113e888b..2f85eecda 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -282,5 +282,6 @@ "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", - "Notify when posts are liked": "Notifier lorsque les messages sont aimés" + "Notify when posts are liked": "Notifier lorsque les messages sont aimés", + "Don't show the Like button": "Ne pas afficher le bouton J'aime" } diff --git a/translations/ga.json b/translations/ga.json index 04ad6f2f3..f758411f2 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -282,5 +282,6 @@ "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", - "Notify when posts are liked": "Cuir in iúl cathain is maith poist" + "Notify when posts are liked": "Cuir in iúl cathain is maith poist", + "Don't show the Like button": "Ná taispeáin an cnaipe Cosúil" } diff --git a/translations/hi.json b/translations/hi.json index a8f984adb..6da7225e2 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -282,5 +282,6 @@ "Create a new event": "एक नई घटना बनाएँ", "Moderation policy or code of conduct": "मॉडरेशन पॉलिसी या आचार संहिता", "Edit event": "घटना संपादित करें", - "Notify when posts are liked": "पोस्ट पसंद आने पर सूचित करें" + "Notify when posts are liked": "पोस्ट पसंद आने पर सूचित करें", + "Don't show the Like button": "लाइक बटन न दिखाएं" } diff --git a/translations/it.json b/translations/it.json index 35443ffa4..71952e1a6 100644 --- a/translations/it.json +++ b/translations/it.json @@ -282,5 +282,6 @@ "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", - "Notify when posts are liked": "Avvisa quando i post sono piaciuti" + "Notify when posts are liked": "Avvisa quando i post sono piaciuti", + "Don't show the Like button": "Non mostrare il pulsante Mi piace" } diff --git a/translations/ja.json b/translations/ja.json index a11bf5ea1..384098456 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -282,5 +282,6 @@ "Create a new event": "新しいイベントを作成する", "Moderation policy or code of conduct": "モデレートポリシーまたは行動規範", "Edit event": "イベントを編集", - "Notify when posts are liked": "投稿が高く評価されたときに通知する" + "Notify when posts are liked": "投稿が高く評価されたときに通知する", + "Don't show the Like button": "「いいね!」ボタンを表示しない" } diff --git a/translations/oc.json b/translations/oc.json index 2a020527c..fae634148 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -278,5 +278,6 @@ "Create a new event": "Create a new event", "Moderation policy or code of conduct": "Moderation policy or code of conduct", "Edit event": "Edit event", - "Notify when posts are liked": "Notify when posts are liked" + "Notify when posts are liked": "Notify when posts are liked", + "Don't show the Like button": "Don't show the Like button" } diff --git a/translations/pt.json b/translations/pt.json index 27a0466bb..010df87da 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -282,5 +282,6 @@ "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", - "Notify when posts are liked": "Notificar quando as postagens forem curtidas" + "Notify when posts are liked": "Notificar quando as postagens forem curtidas", + "Don't show the Like button": "Não mostrar o botão Curtir" } diff --git a/translations/ru.json b/translations/ru.json index 2b5d61d44..f4742761f 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -282,5 +282,6 @@ "Create a new event": "Создать новое мероприятие", "Moderation policy or code of conduct": "Политика модерации или кодекс поведения", "Edit event": "Изменить мероприятие", - "Notify when posts are liked": "Уведомлять, когда публикации нравятся" + "Notify when posts are liked": "Уведомлять, когда публикации нравятся", + "Don't show the Like button": "Не показывать кнопку \"Нравится\"" } diff --git a/translations/zh.json b/translations/zh.json index 3ce3f2d7d..7c3fffd88 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -282,5 +282,6 @@ "Create a new event": "建立新活动", "Moderation policy or code of conduct": "审核政策或行为准则", "Edit event": "编辑活动", - "Notify when posts are liked": "通知喜欢的帖子" + "Notify when posts are liked": "通知喜欢的帖子", + "Don't show the Like button": "不显示“赞”按钮" } diff --git a/utils.py b/utils.py index 073e820a8..f143bebbd 100644 --- a/utils.py +++ b/utils.py @@ -75,12 +75,12 @@ def saveJson(jsonObject: {}, filename: str) -> bool: return False -def loadJson(filename: str, delaySec=2) -> {}: +def loadJson(filename: str, delaySec=2, maxTries=5) -> {}: """Makes a few attempts to load a json formatted file """ jsonObject = None tries = 0 - while tries < 5: + while tries < maxTries: try: with open(filename, 'r') as fp: data = fp.read() @@ -351,7 +351,7 @@ def followPerson(baseDir: str, nickname: str, domain: str, for line in lines: if handleToFollow not in line: newLines += line - with open(unfollowedFilename, "w") as f: + with open(unfollowedFilename, 'w+') as f: f.write(newLines) if not os.path.isdir(baseDir + '/accounts'): @@ -383,7 +383,7 @@ def followPerson(baseDir: str, nickname: str, domain: str, followNickname, followDomain) if debug: print('DEBUG: creating new following file to follow ' + handleToFollow) - with open(filename, "w") as followfile: + with open(filename, 'w+') as followfile: followfile.write(handleToFollow + '\n') return True @@ -473,6 +473,8 @@ def isReplyToBlogPost(baseDir: str, nickname: str, domain: str, return False if not postJsonObject['object'].get('inReplyTo'): return False + if not isinstance(postJsonObject['object']['inReplyTo'], str): + return False blogsIndexFilename = baseDir + '/accounts/' + \ nickname + '@' + domain + '/tlblogs.index' if not os.path.isfile(blogsIndexFilename): @@ -905,12 +907,20 @@ def searchBoxPosts(baseDir: str, nickname: str, domain: str, def getFileCaseInsensitive(path: str) -> str: """Returns a case specific filename given a case insensitive version of it """ + # does the given file exist? If so then we don't need + # to do a directory search + if os.path.isfile(path): + return path + if path != path.lower(): + if os.path.isfile(path.lower()): + return path.lower() directory, filename = os.path.split(path) directory, filename = (directory or '.'), filename.lower() for f in os.listdir(directory): - newpath = os.path.join(directory, f) - if os.path.isfile(newpath) and f.lower() == filename: - return newpath + if f.lower() == filename: + newpath = os.path.join(directory, f) + if os.path.isfile(newpath): + return newpath return path diff --git a/webinterface.py b/webinterface.py index a2e451b4d..778565009 100644 --- a/webinterface.py +++ b/webinterface.py @@ -199,7 +199,8 @@ def setBlogAddress(actorJson: {}, blogAddress: str) -> None: def updateAvatarImageCache(session, baseDir: str, httpPrefix: str, actor: str, avatarUrl: str, - personCache: {}, force=False) -> str: + personCache: {}, allowDownloads: bool, + force=False) -> str: """Updates the cached avatar for the given actor """ if not avatarUrl: @@ -232,7 +233,8 @@ def updateAvatarImageCache(session, baseDir: str, httpPrefix: str, avatarImageFilename = avatarImagePath + '.webp' else: return None - if not os.path.isfile(avatarImageFilename) or force: + + if (not os.path.isfile(avatarImageFilename) or force) and allowDownloads: try: print('avatar image url: ' + avatarUrl) result = session.get(avatarUrl, @@ -282,18 +284,23 @@ def updateAvatarImageCache(session, baseDir: str, httpPrefix: str, "public keys don't match when downloading actor for " + actor) return None - storePersonInCache(baseDir, actor, personJson, personCache) - return getPersonAvatarUrl(baseDir, actor, personCache) + storePersonInCache(baseDir, actor, personJson, personCache, + allowDownloads) + return getPersonAvatarUrl(baseDir, actor, personCache, + allowDownloads) return None return avatarImageFilename.replace(baseDir + '/cache', '') -def getPersonAvatarUrl(baseDir: str, personUrl: str, personCache: {}) -> str: +def getPersonAvatarUrl(baseDir: str, personUrl: str, personCache: {}, + allowDownloads: bool) -> str: """Returns the avatar url for the person """ - personJson = getPersonFromCache(baseDir, personUrl, personCache) + personJson = \ + getPersonFromCache(baseDir, personUrl, personCache, allowDownloads) if not personJson: return None + # get from locally stored image actorStr = personJson['id'].replace('/', '-') avatarImagePath = baseDir + '/cache/avatars/' + actorStr @@ -796,7 +803,7 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, showIndividualPostIcons = True allowDeletion = False hashtagSearchForm += \ - individualPostAsHtml(recentPostsCache, + individualPostAsHtml(True, recentPostsCache, maxRecentPosts, iconsDir, translate, None, baseDir, session, wfRequest, @@ -1025,7 +1032,7 @@ def htmlHistorySearch(translate: {}, baseDir: str, showIndividualPostIcons = True allowDeletion = False historySearchForm += \ - individualPostAsHtml(recentPostsCache, + individualPostAsHtml(True, recentPostsCache, maxRecentPosts, iconsDir, translate, None, baseDir, session, wfRequest, @@ -1084,6 +1091,7 @@ def htmlEditProfile(translate: {}, baseDir: str, path: str, followDMs = '' removeTwitter = '' notifyLikes = '' + hideLikeButton = '' mediaInstanceStr = '' displayNickname = nickname bioStr = '' @@ -1134,6 +1142,9 @@ def htmlEditProfile(translate: {}, baseDir: str, path: str, if os.path.isfile(baseDir + '/accounts/' + nickname + '@' + domain + '/.notifyLikes'): notifyLikes = 'checked' + if os.path.isfile(baseDir + '/accounts/' + + nickname + '@' + domain + '/.hideLikeButton'): + hideLikeButton = 'checked' mediaInstance = getConfigParam(baseDir, "mediaInstance") if mediaInstance: @@ -1473,6 +1484,10 @@ def htmlEditProfile(translate: {}, baseDir: str, path: str, ' ' + \ translate['Notify when posts are liked'] + '
\n' + editProfileForm += \ + ' ' + \ + translate["Don't show the Like button"] + '
\n' editProfileForm += \ '