diff --git a/daemon.py b/daemon.py index 94f079f3..3512259c 100644 --- a/daemon.py +++ b/daemon.py @@ -108,6 +108,7 @@ from roles import setRole from roles import clearModeratorStatus from skills import outboxSkills from availability import outboxAvailability +from webinterface import htmlBlogPost from webinterface import htmlCalendarDeleteConfirm from webinterface import htmlDeletePost from webinterface import htmlAbout @@ -866,6 +867,30 @@ class PubServer(BaseHTTPRequestHandler): print('POST TIMING|'+str(ctr)+'|'+timeDiff) ctr+=1 + def _pathContainsBlogLink(baseDir: str,httpPrefix: str,domain: str,path: str) -> (str,str): + """If the path contains a blog entry then return its filename + """ + userEnding=path.split('/users/') + if '/' not in userEnding: + return None,None + userEnding=userEnging.split('/') + if len(userEnding)!=2: + return None,None + if len(userEnding[1])<14: + return None,None + userEnding[1]=userEnding[1].strip() + if not userEnding[1].isdigit(): + return None,None + nickname=userEnding[0] + # check for blog posts + blogIndexFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/tlblogs.index' + if not os.path.isfile(blogIndexFilename): + return None,None + if '/'+userEnding[1]+'\n' not in open(blogIndexFilename).read(): + return None,None + messageId=httpPrefix+'://'+domain+'/'+nickname+'/statuses/'+nickname[1] + return locatePost(baseDir,nickname,domain,messageId),nickname + def do_GET(self): GETstartTime=time.time() GETtimings=[] @@ -895,6 +920,25 @@ class PubServer(BaseHTTPRequestHandler): # replace https://domain/@nick with https://domain/users/nick if self.path.startswith('/@'): self.path=self.path.replace('/@','/users/') + if self._requestHTTP(): + blogFilename,nickname= \ + self._pathContainsBlogLink(self.server.baseDir, \ + self.server.httpPrefix, \ + self.server.domain, \ + self.path) + if blogFilename and nickname: + postJsonObject=loadJson(blogFilename) + msg=htmlBlogPost(self.server.baseDir, \ + self.server.httpPrefix, \ + self.server.translate, \ + nickname,self.server.domain, \ + postJsonObject) + if msg: + self._set_headers('text/html',len(msg),cookie) + self._write(msg) + return + self._404() + return # redirect music to #nowplaying list if self.path=='/music' or self.path=='/nowplaying': diff --git a/epicyon-blog.css b/epicyon-blog.css new file mode 100644 index 00000000..c4f5e9e7 --- /dev/null +++ b/epicyon-blog.css @@ -0,0 +1,69 @@ +@charset "UTF-8"; + +:root { + --main-bg-color: #282c37; + --dropdown-bg-color: #111; + --dropdown-bg-color-hover: #333; + --main-bg-color-reply: #212c37; + --main-bg-color-dm: #222; + --main-bg-color-report: #221c27; + --main-header-color-roles: #282237; + --main-fg-color: #dddddd; + --main-link-color: #999; + --main-visited-color: #888; + --border-color: #505050; + --font-size-header: 18px; + --font-color-header: #ccc; + --font-size-button-mobile: 34px; + --font-size: 30px; + --font-size2: 24px; + --font-size3: 38px; + --font-size4: 22px; + --font-size5: 20px; + --text-entry-foreground: #ccc; + --text-entry-background: #111; + --time-color: #aaa; + --button-text: #FFFFFF; + --button-background: #999; + --button-selected: #666; + --button-highlighted: green; + --button-selected-highlighted: darkgreen; + --button-approve: darkgreen; + --button-deny: darkred; + --button-height: 10px; + --button-height-padding-mobile: 20px; + --button-height-padding: 10px; + --gallery-border: #ccc; + --gallery-hover: #777; + --gallery-text-color: #ccc; + --gallery-font-size: 22px; + --gallery-font-size-mobile: 35px; + --button-corner-radius: 15px; + --timeline-border-radius: 30px; +} + +body, html { + background-color: var(--main-bg-color); + color: var(--main-fg-color); + + height: 100%; + font-family: serif; + max-width: 80%; + min-width: 950px; + margin: 0 auto; + font-size: var(--font-size); +} + +a, u { + color: var(--main-fg-color); +} + +a:visited{ + color: var(--main-visited-color); + font-weight: bold; +} + +a:link { + color: var(--main-link-color); + font-weight: bold; +} diff --git a/theme.py b/theme.py index e64342af..ee4cc8f0 100644 --- a/theme.py +++ b/theme.py @@ -21,7 +21,7 @@ def setThemeInConfig(baseDir: str,name: str) -> bool: return saveJson(configJson,configFilename) def removeTheme(baseDir: str): - themeFiles=('epicyon.css','login.css','follow.css','suspended.css','calendar.css') + themeFiles=('epicyon.css','login.css','follow.css','suspended.css','calendar.css','blog.css') for filename in themeFiles: if os.path.isfile(baseDir+'/'+filename): os.remove(baseDir+'/'+filename) @@ -65,7 +65,7 @@ def setThemeFromDict(baseDir: str,name: str,themeParams: {}): """Uses a dictionary to set a theme """ setThemeInConfig(baseDir,name) - themeFiles=('epicyon.css','login.css','follow.css','suspended.css','calendar.css') + themeFiles=('epicyon.css','login.css','follow.css','suspended.css','calendar.css','blog.css') for filename in themeFiles: templateFilename=baseDir+'/epicyon-'+filename if filename=='epicyon.css': diff --git a/webinterface.py b/webinterface.py index e0760d37..42bb0e90 100644 --- a/webinterface.py +++ b/webinterface.py @@ -5001,3 +5001,39 @@ def htmlProfileAfterSearch(recentPostsCache: {},maxRecentPosts: int, \ break return htmlHeader(cssFilename,profileStyle)+profileStr+htmlFooter() + +def htmlBlogPost(baseDir: str,httpPrefix: str,translate: {}, \ + nickname: str,domain: str,postJsonObject: {}) -> str: + """Returns a html blog post + """ + blogStr='' + + cssFilename=baseDir+'/epicyon-blog.css' + if os.path.isfile(baseDir+'/blog.css'): + cssFilename=baseDir+'/blog.css' + with open(cssFilename, 'r') as cssFile: + blogCSS=cssFile.read() + blogStr=htmlHeader(cssFilename,blogCSS) + + if postJsonObject['object'].get('summary'): + blogStr+='