Blogs timeline

merge-requests/30/head
Bob Mottram 2020-02-24 14:39:25 +00:00
parent 9b48742ae3
commit 0bd778d553
8 changed files with 205 additions and 14 deletions

102
daemon.py
View File

@ -115,6 +115,7 @@ from webinterface import htmlRemoveSharedItem
from webinterface import htmlInboxDMs
from webinterface import htmlInboxReplies
from webinterface import htmlInboxMedia
from webinterface import htmlInboxBlogs
from webinterface import htmlUnblockConfirm
from webinterface import htmlPersonOptions
from webinterface import htmlIndividualPost
@ -186,6 +187,9 @@ maxPostsInFeed=12
# reduced posts for media feed because it can take a while
maxPostsInMediaFeed=6
# Blogs can be longer, so don't show many per page
maxPostsInBlogsFeed=4
# number of follows/followers per page
followsPerPage=12
@ -2894,6 +2898,84 @@ class PubServer(BaseHTTPRequestHandler):
self.server.GETbusy=False
return
# get the blogs for a given person
if self.path.endswith('/tlblogs') or '/tlblogs?page=' in self.path:
if '/users/' in self.path:
if authorized:
inboxBlogsFeed= \
personBoxJson(self.server.recentPostsCache, \
self.server.session, \
self.server.baseDir, \
self.server.domain, \
self.server.port, \
self.path, \
self.server.httpPrefix, \
maxPostsInBlogsFeed, 'tlblogs', \
True,self.server.ocapAlways)
if not inboxBlogsFeed:
inboxBlogsFeed=[]
if self._requestHTTP():
nickname=self.path.replace('/users/','').replace('/tlblogs','')
pageNumber=1
if '?page=' in nickname:
pageNumber=nickname.split('?page=')[1]
nickname=nickname.split('?page=')[0]
if pageNumber.isdigit():
pageNumber=int(pageNumber)
else:
pageNumber=1
if 'page=' not in self.path:
# if no page was specified then show the first
inboxBlogsFeed= \
personBoxJson(self.server.recentPostsCache, \
self.server.session, \
self.server.baseDir, \
self.server.domain, \
self.server.port, \
self.path+'?page=1', \
self.server.httpPrefix, \
maxPostsInBlogsFeed, 'tlblogs', \
True,self.server.ocapAlways)
msg=htmlInboxBlogs(self.server.defaultTimeline, \
self.server.recentPostsCache, \
self.server.maxRecentPosts, \
self.server.translate, \
pageNumber,maxPostsInBlogsFeed, \
self.server.session, \
self.server.baseDir, \
self.server.cachedWebfingers, \
self.server.personCache, \
nickname, \
self.server.domain, \
self.server.port, \
inboxBlogsFeed, \
self.server.allowDeletion, \
self.server.httpPrefix, \
self.server.projectVersion).encode('utf-8')
self._set_headers('text/html',len(msg),cookie)
self._write(msg)
else:
# don't need authenticated fetch here because there is
# already the authorization check
msg=json.dumps(inboxBlogsFeed,ensure_ascii=False).encode('utf-8')
self._set_headers('application/json',len(msg),None)
self._write(msg)
self.server.GETbusy=False
return
else:
if self.server.debug:
nickname=self.path.replace('/users/','').replace('/tlblogs','')
print('DEBUG: '+nickname+ \
' was not authorized to access '+self.path)
if self.path!='/tlblogs':
# not the blogs inbox
if self.server.debug:
print('DEBUG: GET access to blogs is unauthorized')
self.send_response(405)
self.end_headers()
self.server.GETbusy=False
return
self._benchmarkGETtimings(GETstartTime,GETtimings,46)
# get the shared items timeline for a given person
@ -4343,6 +4425,20 @@ class PubServer(BaseHTTPRequestHandler):
self.server.defaultTimeline='inbox'
setConfigParam(self.server.baseDir,"mediaInstance", \
self.server.mediaInstance)
if fields.get('blogsInstance'):
self.server.blogsInstance=False
self.server.defaultTimeline='inbox'
if fields['blogsInstance']=='on':
self.server.blogsInstance=True
self.server.defaultTimeline='tlblogs'
setConfigParam(self.server.baseDir,"blogsInstance", \
self.server.blogsInstance)
else:
if self.server.blogsInstance:
self.server.blogsInstance=False
self.server.defaultTimeline='inbox'
setConfigParam(self.server.baseDir,"blogsInstance", \
self.server.blogsInstance)
# only receive DMs from accounts you follow
followDMsFilename= \
self.server.baseDir+'/accounts/'+ \
@ -5574,7 +5670,8 @@ def loadTokens(baseDir: str,tokensDict: {},tokensLookup: {}) -> None:
tokensDict[nickname]=token
tokensLookup[token]=nickname
def runDaemon(mediaInstance: bool,maxRecentPosts: int, \
def runDaemon(blogsInstance: bool,mediaInstance: bool, \
maxRecentPosts: int, \
enableSharedInbox: bool,registration: bool, \
language: str,projectVersion: str, \
instanceId: str,clientToServer: bool, \
@ -5615,9 +5712,12 @@ def runDaemon(mediaInstance: bool,maxRecentPosts: int, \
httpd.useBlurHash=useBlurHash
httpd.mediaInstance=mediaInstance
httpd.blogsInstance=blogsInstance
httpd.defaultTimeline='inbox'
if mediaInstance:
httpd.defaultTimeline='tlmedia'
if blogsInstance:
httpd.defaultTimeline='tlblogs'
# load translations dictionary
httpd.translate={}

View File

@ -151,6 +151,9 @@ parser.add_argument('-f','--federate', nargs='+',dest='federationList', \
parser.add_argument("--mediainstance", type=str2bool, nargs='?', \
const=True, default=False, \
help="Media Instance - favor media over text")
parser.add_argument("--blogsinstance", type=str2bool, nargs='?', \
const=True, default=False, \
help="Blogs Instance - favor blogs over microblogging")
parser.add_argument("--debug", type=str2bool, nargs='?', \
const=True, default=False, \
help="Show debug messages")
@ -386,6 +389,11 @@ if not args.mediainstance:
mediaInstance=getConfigParam(baseDir,'mediaInstance')
if mediaInstance!=None:
args.mediainstance=mediaInstance
if not args.blogsinstance:
blogsInstance=getConfigParam(baseDir,'blogsInstance')
if blogsInstance!=None:
args.blogsinstance=blogsInstance
# set the instance title in config.json
title=getConfigParam(baseDir,'instanceTitle')
@ -1504,7 +1512,8 @@ if not registration:
if setTheme(baseDir,themeName):
print('Theme set to '+themeName)
runDaemon(args.mediainstance,args.maxRecentPosts, \
runDaemon(args.blogsinstance,args.mediainstance, \
args.maxRecentPosts, \
not args.nosharedinbox, \
registration,args.language,__version__, \
instanceId,args.client,baseDir, \

View File

@ -62,6 +62,23 @@ from webinterface import getIconsDir
from question import questionUpdateVotes
from media import replaceYouTube
def isBlogPost(postJsonObject: {}) -> bool:
"""Is the given post a blog post?
"""
if postJsonObject['type']!='Create':
return False
if not postJsonObject.get('object'):
return False
if not isinstance(postJsonObject['object'], dict):
return False
if not postJsonObject['object'].get('type'):
return False
if not postJsonObject['object'].get('content'):
return False
if postJsonObject['object']['type']!='Article':
return False
return True
def storeHashTags(baseDir: str,nickname: str,postJsonObject: {}) -> None:
"""Extracts hashtags from an incoming post and updates the
relevant tags files.
@ -1924,6 +1941,9 @@ def inboxAfterCapabilities(recentPostsCache: {},maxRecentPosts: int, \
if isImageMedia(session,baseDir,httpPrefix,nickname,domain,postJsonObject):
# media index will be updated
updateIndexList.append('tlmedia')
if isBlogPost(postJsonObject):
# blogs index will be updated
updateIndexList.append('tlblogs')
# get the avatar for a reply/announce
obtainAvatarForReplyPost(session,baseDir,httpPrefix,domain,personCache,postJsonObject,debug)

View File

@ -21,6 +21,7 @@ from webfinger import storeWebfingerEndpoint
from posts import createDMTimeline
from posts import createRepliesTimeline
from posts import createMediaTimeline
from posts import createBlogsTimeline
from posts import createBookmarksTimeline
from posts import createInbox
from posts import createOutbox
@ -470,6 +471,7 @@ def personBoxJson(recentPostsCache: {}, \
"""
if boxname!='inbox' and boxname!='dm' and \
boxname!='tlreplies' and boxname!='tlmedia' and \
boxname!='tlblogs' and \
boxname!='outbox' and boxname!='moderation' and \
boxname!='tlbookmarks':
return None
@ -521,6 +523,9 @@ def personBoxJson(recentPostsCache: {}, \
elif boxname=='tlmedia':
return createMediaTimeline(session,baseDir,nickname,domain,port,httpPrefix, \
noOfItems,headerOnly,ocapAlways,pageNumber)
elif boxname=='tlblogs':
return createBlogsTimeline(session,baseDir,nickname,domain,port,httpPrefix, \
noOfItems,headerOnly,ocapAlways,pageNumber)
elif boxname=='outbox':
return createOutbox(session,baseDir,nickname,domain,port,httpPrefix, \
noOfItems,headerOnly,authorized,pageNumber)

View File

@ -1956,6 +1956,13 @@ def createInbox(recentPostsCache: {}, \
session,baseDir,'inbox',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,True,ocapAlways,pageNumber)
def createBlogsTimeline(recentPostsCache: {}, \
session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \
itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}:
return createBoxIndexed(recentPostsCache, \
session,baseDir,'tlblogs',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,True,ocapAlways,pageNumber)
def createBookmarksTimeline(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \
itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}:
return createBoxIndexed({},session,baseDir,'tlbookmarks',nickname,domain,port,httpPrefix, \
@ -2240,6 +2247,11 @@ def addPostStringToTimeline(postStr: str,boxname: str, \
elif boxname=='tlreplies':
if boxActor not in postStr:
return False
elif boxname=='tlblogs':
if '"Create"' not in postStr:
return False
if '"Article"' not in postStr:
return False
elif boxname=='tlmedia':
if '"Create"' in postStr:
if 'mediaType' not in postStr or 'image/' not in postStr:
@ -2270,11 +2282,13 @@ def createBoxIndexed(recentPostsCache: {}, \
if boxname!='inbox' and boxname!='dm' and \
boxname!='tlreplies' and boxname!='tlmedia' and \
boxname!='tlblogs' and \
boxname!='outbox' and boxname!='tlbookmarks':
return None
if boxname!='dm' and boxname!='tlreplies' and \
boxname!='tlmedia' and boxname!='tlbookmarks':
boxname!='tlmedia' and boxname!='tlblogs' and \
boxname!='tlbookmarks':
boxDir = createPersonDir(nickname,domain,baseDir,boxname)
else:
# extract DMs or replies or media from the inbox
@ -2284,7 +2298,7 @@ def createBoxIndexed(recentPostsCache: {}, \
sharedBoxDir=None
if boxname=='inbox' or boxname=='tlreplies' or \
boxname=='tlmedia':
boxname=='tlmedia' or boxname=='tlblogs':
sharedBoxDir = createPersonDir('inbox',domain,baseDir,boxname)
# bookmarks timeline is like the inbox but has its own separate index
@ -2297,6 +2311,8 @@ def createBoxIndexed(recentPostsCache: {}, \
indexBoxName='tlreplies'
elif boxname=='tlmedia':
indexBoxName='tlmedia'
elif boxname=='tlblogs':
indexBoxName='tlblogs'
if port:
if port!=80 and port!=443:

View File

@ -236,7 +236,7 @@ def createServerAlice(path: str,domain: str,port: int,bobAddress: str,federation
maxMentions=10
maxEmoji=10
print('Server running: Alice')
runDaemon(False,5,True,True,'en',__version__, \
runDaemon(False,False,5,True,True,'en',__version__, \
"instanceId",False,path,domain,port,port, \
httpPrefix,federationList,maxMentions,maxEmoji,False, \
noreply,nolike,nopics,noannounce,cw,ocapAlways, \
@ -292,7 +292,7 @@ def createServerBob(path: str,domain: str,port: int,aliceAddress: str,federation
maxMentions=10
maxEmoji=10
print('Server running: Bob')
runDaemon(False,5,True,True,'en',__version__, \
runDaemon(False,False,5,True,True,'en',__version__, \
"instanceId",False,path,domain,port,port, \
httpPrefix,federationList,maxMentions,maxEmoji,False, \
noreply,nolike,nopics,noannounce,cw,ocapAlways, \
@ -328,7 +328,7 @@ def createServerEve(path: str,domain: str,port: int,federationList: [], \
maxMentions=10
maxEmoji=10
print('Server running: Eve')
runDaemon(False,5,True,True,'en',__version__, \
runDaemon(False,False,5,True,True,'en',__version__, \
"instanceId",False,path,domain,port,port, \
httpPrefix,federationList,maxMentions,maxEmoji,False, \
noreply,nolike,nopics,noannounce,cw,ocapAlways, \

View File

@ -429,7 +429,7 @@ def validNickname(domain: str,nickname: str) -> bool:
return False
if nickname==domain:
return False
reservedNames=['inbox','dm','outbox','following','public','followers','profile','channel','capabilities','calendar','tlreplies','tlmedia','moderation','activity','undo','reply','replies','question','like','likes','users','statuses','updates','repeat','announce','shares']
reservedNames=['inbox','dm','outbox','following','public','followers','profile','channel','capabilities','calendar','tlreplies','tlmedia','tlblogs','moderation','activity','undo','reply','replies','question','like','likes','users','statuses','updates','repeat','announce','shares']
if nickname in reservedNames:
return False
return True

View File

@ -3395,6 +3395,7 @@ def htmlTimeline(defaultTimeline: str, \
moderator=isModerator(baseDir,nickname)
inboxButton='button'
blogsButton='button'
dmButton='button'
if newDM:
dmButton='buttonhighlighted'
@ -3412,6 +3413,8 @@ def htmlTimeline(defaultTimeline: str, \
moderationButton='buttonhighlighted'
if boxName=='inbox':
inboxButton='buttonselected'
elif boxName=='tlblogs':
blogsButton='buttonselected'
elif boxName=='dm':
dmButton='buttonselected'
if newDM:
@ -3511,16 +3514,24 @@ def htmlTimeline(defaultTimeline: str, \
tlStr+='<div class="timeline-banner">'
tlStr+='</div></a>'
tlStr+='<div class="container">\n'
if defaultTimeline!='tlmedia':
tlStr+= \
' <a href="'+actor+'/inbox"><button class="'+ \
inboxButton+'"><span>'+ \
translate['Inbox']+'</span></button></a>'
else:
# first button
if defaultTimeline=='tlmedia':
tlStr+= \
' <a href="'+actor+'/tlmedia"><button class="'+ \
mediaButton+'"><span>'+translate['Media']+ \
'</span></button></a>'
elif defaultTimeline=='tlblogs':
tlStr+= \
' <a href="'+actor+'/tlblogs"><button class="'+ \
blogsButton+'"><span>'+translate['Blogs']+ \
'</span></button></a>'
else:
tlStr+= \
' <a href="'+actor+'/inbox"><button class="'+ \
inboxButton+'"><span>'+ \
translate['Inbox']+'</span></button></a>'
tlStr+= \
' <a href="'+actor+'/dm"><button class="'+dmButton+ \
'"><span>'+translate['DM']+'</span></button></a>'
@ -3528,6 +3539,8 @@ def htmlTimeline(defaultTimeline: str, \
' <a href="'+actor+'/tlreplies"><button class="'+ \
repliesButton+'"><span>'+translate['Replies']+ \
'</span></button></a>'
# typically the media button
if defaultTimeline!='tlmedia':
tlStr+= \
' <a href="'+actor+'/tlmedia"><button class="'+ \
@ -3538,6 +3551,19 @@ def htmlTimeline(defaultTimeline: str, \
' <a href="'+actor+'/inbox"><button class="'+ \
inboxButton+'"><span>'+translate['Inbox']+ \
'</span></button></a>'
# typically the blogs button
if defaultTimeline!='tlblogs':
tlStr+= \
' <a href="'+actor+'/tlblogs"><button class="'+ \
blogsButton+'"><span>'+translate['Blogs']+ \
'</span></button></a>'
else:
tlStr+= \
' <a href="'+actor+'/inbox"><button class="'+ \
inboxButton+'"><span>'+translate['Inbox']+ \
'</span></button></a>'
tlStr+= \
' <a href="'+actor+'/outbox"><button class="'+ \
sentButton+'"><span>'+translate['Outbox']+ \
@ -3792,6 +3818,21 @@ def htmlInboxMedia(defaultTimeline: str, \
nickname,domain,port,inboxJson,'tlmedia',allowDeletion, \
httpPrefix,projectVersion,False)
def htmlInboxBlogs(defaultTimeline: str, \
recentPostsCache: {},maxRecentPosts: int, \
translate: {},pageNumber: int,itemsPerPage: int, \
session,baseDir: str,wfRequest: {},personCache: {}, \
nickname: str,domain: str,port: int,inboxJson: {}, \
allowDeletion: bool, \
httpPrefix: str,projectVersion: str) -> str:
"""Show the blogs timeline as html
"""
return htmlTimeline(defaultTimeline,recentPostsCache,maxRecentPosts, \
translate,pageNumber, \
itemsPerPage,session,baseDir,wfRequest,personCache, \
nickname,domain,port,inboxJson,'tlblogs',allowDeletion, \
httpPrefix,projectVersion,False)
def htmlModeration(defaultTimeline: str, \
recentPostsCache: {},maxRecentPosts: int, \
translate: {},pageNumber: int,itemsPerPage: int, \