forked from indymedia/epicyon
Functions for shared items
parent
ac8e920bcf
commit
643ca568bc
74
daemon.py
74
daemon.py
|
@ -49,6 +49,7 @@ from webinterface import htmlProfile
|
|||
from webinterface import htmlInbox
|
||||
from webinterface import htmlOutbox
|
||||
from webinterface import htmlPostReplies
|
||||
from shares import getSharesFeedForPerson
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
@ -58,6 +59,9 @@ maxPostsInFeed=20
|
|||
# number of follows/followers per page
|
||||
followsPerPage=12
|
||||
|
||||
# number of item shares per page
|
||||
sharesPerPage=12
|
||||
|
||||
def readFollowList(filename: str):
|
||||
"""Returns a list of ActivityPub addresses to follow
|
||||
"""
|
||||
|
@ -354,6 +358,28 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
return
|
||||
self._404()
|
||||
return
|
||||
# show shared item images
|
||||
# Note that this comes before the busy flag to avoid conflicts
|
||||
if '/sharefiles/' in self.path:
|
||||
if self.path.endswith('.png') or \
|
||||
self.path.endswith('.jpg') or \
|
||||
self.path.endswith('.gif'):
|
||||
mediaStr=self.path.split('/sharefiles/')[1]
|
||||
mediaFilename= \
|
||||
self.server.baseDir+'/sharefiles/'+mediaStr
|
||||
if os.path.isfile(mediaFilename):
|
||||
if mediaFilename.endswith('.png'):
|
||||
self._set_headers('image/png')
|
||||
elif mediaFilename.endswith('.jpg'):
|
||||
self._set_headers('image/jpeg')
|
||||
else:
|
||||
self._set_headers('image/gif')
|
||||
with open(mediaFilename, 'rb') as avFile:
|
||||
mediaBinary = avFile.read()
|
||||
self.wfile.write(mediaBinary)
|
||||
return
|
||||
self._404()
|
||||
return
|
||||
# show avatar or background image
|
||||
# Note that this comes before the busy flag to avoid conflicts
|
||||
if '/users/' in self.path:
|
||||
|
@ -699,7 +725,46 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.server.GETbusy=False
|
||||
return
|
||||
authorized=self._isAuthorized()
|
||||
|
||||
|
||||
shares=getSharesFeedForPerson(self.server.baseDir,self.server.domain, \
|
||||
self.server.port,self.path, \
|
||||
self.server.httpPrefix, \
|
||||
sharesPerPage)
|
||||
if shares:
|
||||
if 'text/html' in self.headers['Accept']:
|
||||
if 'page=' not in self.path:
|
||||
# get a page of shares, not the summary
|
||||
shares=getSharesFeedForPerson(self.server.baseDir,self.server.domain, \
|
||||
self.server.port,self.path+'?page=true', \
|
||||
self.server.httpPrefix, \
|
||||
sharesPerPage)
|
||||
getPerson = personLookup(self.server.domain,self.path.replace('/shares',''), \
|
||||
self.server.baseDir)
|
||||
if getPerson:
|
||||
if not self.server.session:
|
||||
if self.server.debug:
|
||||
print('DEBUG: creating new session')
|
||||
self.server.session= \
|
||||
createSession(self.server.domain,self.server.port,self.server.useTor)
|
||||
|
||||
self._set_headers('text/html')
|
||||
self.wfile.write(htmlProfile(self.server.baseDir, \
|
||||
self.server.httpPrefix, \
|
||||
authorized, \
|
||||
self.server.ocapAlways, \
|
||||
getPerson,'shares', \
|
||||
self.server.session, \
|
||||
self.server.cachedWebfingers, \
|
||||
self.server.personCache, \
|
||||
shares).encode('utf-8'))
|
||||
self.server.GETbusy=False
|
||||
return
|
||||
else:
|
||||
self._set_headers('application/json')
|
||||
self.wfile.write(json.dumps(shares).encode('utf-8'))
|
||||
self.server.GETbusy=False
|
||||
return
|
||||
|
||||
following=getFollowingFeed(self.server.baseDir,self.server.domain, \
|
||||
self.server.port,self.path, \
|
||||
self.server.httpPrefix, \
|
||||
|
@ -715,17 +780,12 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
getPerson = personLookup(self.server.domain,self.path.replace('/following',''), \
|
||||
self.server.baseDir)
|
||||
if getPerson:
|
||||
if not self.server.session:
|
||||
if self.server.debug:
|
||||
print('DEBUG: creating new session for c2s')
|
||||
self.server.session= \
|
||||
createSession(self.server.domain,self.server.port,self.server.useTor)
|
||||
|
||||
if not self.server.session:
|
||||
if self.server.debug:
|
||||
print('DEBUG: creating new session')
|
||||
self.server.session= \
|
||||
createSession(self.server.domain,self.server.port,self.server.useTor)
|
||||
|
||||
self._set_headers('text/html')
|
||||
self.wfile.write(htmlProfile(self.server.baseDir, \
|
||||
self.server.httpPrefix, \
|
||||
|
|
|
@ -137,6 +137,7 @@ def createPersonBase(baseDir: str,nickname: str,domain: str,port: int, \
|
|||
'endpoints': {
|
||||
'id': httpPrefix+'://'+domain+'/users/'+nickname+'/endpoints',
|
||||
'sharedInbox': httpPrefix+'://'+domain+'/inbox',
|
||||
'shares': httpPrefix+'://'+domain+'/shares'
|
||||
},
|
||||
'capabilityAcquisitionEndpoint': httpPrefix+'://'+domain+'/caps/new',
|
||||
'followers': httpPrefix+'://'+domain+'/users/'+nickname+'/followers',
|
||||
|
@ -144,7 +145,6 @@ def createPersonBase(baseDir: str,nickname: str,domain: str,port: int, \
|
|||
'orgSchema': None,
|
||||
'skills': {},
|
||||
'roles': {},
|
||||
'shares': {},
|
||||
'availability': None,
|
||||
'icon': {'mediaType': 'image/png',
|
||||
'type': 'Image',
|
||||
|
@ -233,6 +233,10 @@ def createPerson(baseDir: str,nickname: str,domain: str,port: int, \
|
|||
setRole(baseDir,nickname,domain,'instance','admin')
|
||||
setRole(baseDir,nickname,domain,'instance','moderator')
|
||||
setRole(baseDir,nickname,domain,'instance','delegator')
|
||||
|
||||
if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain):
|
||||
os.mkdir(baseDir+'/accounts/'+nickname+'@'+domain)
|
||||
|
||||
if os.path.isfile(baseDir+'/img/default-avatar.png'):
|
||||
copyfile(baseDir+'/img/default-avatar.png',baseDir+'/accounts/'+nickname+'@'+domain+'/avatar.png')
|
||||
if os.path.isfile(baseDir+'/img/image.png'):
|
||||
|
|
8
posts.py
8
posts.py
|
@ -114,16 +114,16 @@ def parseUserFeed(session,feedUrl: str,asHeader: {}) -> None:
|
|||
yield item
|
||||
|
||||
def getPersonBox(session,wfRequest: {},personCache: {}, \
|
||||
boxName='inbox') -> (str,str,str,str,str,str):
|
||||
boxName='inbox') -> (str,str,str,str,str,str,str,str):
|
||||
asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'}
|
||||
personUrl = getUserUrl(wfRequest)
|
||||
if not personUrl:
|
||||
return None,None,None,None,None,None,None
|
||||
return None,None,None,None,None,None,None,None
|
||||
personJson = getPersonFromCache(personUrl,personCache)
|
||||
if not personJson:
|
||||
personJson = getJson(session,personUrl,asHeader,None)
|
||||
if not personJson:
|
||||
return None,None,None,None,None,None,None
|
||||
return None,None,None,None,None,None,None,None
|
||||
boxJson=None
|
||||
if not personJson.get(boxName):
|
||||
if personJson.get('endpoints'):
|
||||
|
@ -133,7 +133,7 @@ def getPersonBox(session,wfRequest: {},personCache: {}, \
|
|||
boxJson=personJson[boxName]
|
||||
|
||||
if not boxJson:
|
||||
return None,None,None,None,None,None,None
|
||||
return None,None,None,None,None,None,None,None
|
||||
|
||||
personId=None
|
||||
if personJson.get('id'):
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
__filename__ = "shares.py"
|
||||
__author__ = "Bob Mottram"
|
||||
__license__ = "AGPL3+"
|
||||
__version__ = "0.0.1"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__status__ = "Production"
|
||||
|
||||
import json
|
||||
import commentjson
|
||||
import os
|
||||
import time
|
||||
from shutil import copyfile
|
||||
from person import validNickname
|
||||
from webfinger import webfingerHandle
|
||||
from auth import createBasicAuthHeader
|
||||
from posts import getPersonBox
|
||||
from session import postJson
|
||||
from utils import getNicknameFromActor
|
||||
from utils import getDomainFromActor
|
||||
|
||||
def removeShare(baseDir: str,nickname: str,domain: str, \
|
||||
displayName: str) -> None:
|
||||
"""Removes a share for a person
|
||||
"""
|
||||
sharesFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/shares.json'
|
||||
if os.path.isfile(sharesFilename):
|
||||
with open(sharesFilename, 'r') as fp:
|
||||
sharesJson=commentjson.load(fp)
|
||||
|
||||
itemID=displayName.replace(' ','')
|
||||
if sharesJson.get(itemID):
|
||||
# remove any image for the item
|
||||
published=sharesJson[itemID]['published']
|
||||
itemIDfile=baseDir+'/sharefiles/'+str(published)+itemID
|
||||
if sharesJson[itemID]['imageUrl']:
|
||||
if sharesJson[itemID]['imageUrl'].endswith('.png'):
|
||||
os.remove(itemIDfile+'.png')
|
||||
if sharesJson[itemID]['imageUrl'].endswith('.jpg'):
|
||||
os.remove(itemIDfile+'.jpg')
|
||||
if sharesJson[itemID]['imageUrl'].endswith('.gif'):
|
||||
os.remove(itemIDfile+'.gif')
|
||||
# remove the item itself
|
||||
del sharesJson[itemID]
|
||||
with open(sharesFilename, 'w') as fp:
|
||||
commentjson.dump(sharesJson, fp, indent=4, sort_keys=True)
|
||||
|
||||
def addShare(baseDir: str,nickname: str,domain: str, \
|
||||
displayName: str, \
|
||||
summary: str, \
|
||||
imageFilename: str, \
|
||||
itemType: str, \
|
||||
itemCategory: str, \
|
||||
location: str, \
|
||||
duration: str,
|
||||
debug: bool) -> None:
|
||||
"""Updates the likes collection within a post
|
||||
"""
|
||||
sharesFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/shares.json'
|
||||
sharesJson={}
|
||||
if os.path.isfile(sharesFilename):
|
||||
with open(sharesFilename, 'r') as fp:
|
||||
sharesJson=commentjson.load(fp)
|
||||
|
||||
duration=duration.lower()
|
||||
durationSec=0
|
||||
published=int(time.time())
|
||||
if ' ' in duration:
|
||||
durationList=duration.split(' ')
|
||||
if durationList[0].isdigit():
|
||||
if 'hour' in durationList[1]:
|
||||
durationSec=published+(int(durationList[0])*60*60)
|
||||
if 'day' in durationList[1]:
|
||||
durationSec=published+(int(durationList[0])*60*60*24)
|
||||
if 'week' in durationList[1]:
|
||||
durationSec=published+(int(durationList[0])*60*60*24*7)
|
||||
if 'month' in durationList[1]:
|
||||
durationSec=published+(int(durationList[0])*60*60*24*30)
|
||||
if 'year' in durationList[1]:
|
||||
durationSec=published+(int(durationList[0])*60*60*24*365)
|
||||
|
||||
itemID=displayName.replace(' ','')
|
||||
|
||||
imageUrl=None
|
||||
if imageFilename:
|
||||
if os.path.isfile(imageFilename):
|
||||
if not os.path.isdir(baseDir+'/sharefiles'):
|
||||
os.mkdir(baseDir+'/sharefiles')
|
||||
itemIDfile=baseDir+'/sharefiles/'+str(published)+itemID
|
||||
if imageFilename.endswidth('.png'):
|
||||
copyfile(imageFilename,itemIDfile+'.png')
|
||||
imageUrl='/sharefiles/'+str(published)+itemID+'.png'
|
||||
if imageFilename.endswidth('.jpg'):
|
||||
copyfile(imageFilename,itemIDfile+'.jpg')
|
||||
imageUrl='/sharefiles/'+str(published)+itemID+'.jpg'
|
||||
if imageFilename.endswidth('.gif'):
|
||||
copyfile(imageFilename,itemIDfile+'.gif')
|
||||
imageUrl='/sharefiles/'+str(published)+itemID+'.gif'
|
||||
|
||||
sharesJson[itemID] = {
|
||||
"displayName": displayName,
|
||||
"summary": summary,
|
||||
"imageUrl": imageUrl,
|
||||
"type": itemType,
|
||||
"category": category,
|
||||
"location": location,
|
||||
"published": published,
|
||||
"expire": durationSec
|
||||
}
|
||||
|
||||
with open(sharesFilename, 'w') as fp:
|
||||
commentjson.dump(sharesJson, fp, indent=4, sort_keys=True)
|
||||
|
||||
def expireShares(baseDir: str,nickname: str,domain: str) -> None:
|
||||
"""Removes expired items from shares
|
||||
"""
|
||||
handleDomain=domain
|
||||
if ':' in handleDomain:
|
||||
handleDomain=domain.split(':')[0]
|
||||
handle=nickname+'@'+handleDomain
|
||||
sharesFilename=baseDir+'/accounts/'+handle+'/shares.json'
|
||||
if os.path.isfile(sharesFilename):
|
||||
with open(sharesFilename, 'r') as fp:
|
||||
sharesJson=commentjson.load(fp)
|
||||
currTime=int(time.time())
|
||||
deleteItemID=[]
|
||||
for itemID,item in sharesJson.items():
|
||||
if currTime>item['expire']:
|
||||
deleteItemID.append(itemID)
|
||||
if deleteItemID:
|
||||
for itemID in deleteItemID:
|
||||
del sharesJson[itemID]
|
||||
with open(sharesFilename, 'w') as fp:
|
||||
commentjson.dump(sharesJson, fp, indent=4, sort_keys=True)
|
||||
|
||||
def getSharesFeedForPerson(baseDir: str, \
|
||||
nickname: str,domain: str,port: int, \
|
||||
path: str,httpPrefix: str, \
|
||||
sharesPerPage=12) -> {}:
|
||||
"""Returns the shares for an account from GET requests
|
||||
"""
|
||||
if '/shares' not in path:
|
||||
return None
|
||||
# handle page numbers
|
||||
headerOnly=True
|
||||
pageNumber=None
|
||||
if '?page=' in path:
|
||||
pageNumber=path.split('?page=')[1]
|
||||
if pageNumber=='true':
|
||||
pageNumber=1
|
||||
else:
|
||||
try:
|
||||
pageNumber=int(pageNumber)
|
||||
except:
|
||||
pass
|
||||
path=path.split('?page=')[0]
|
||||
headerOnly=False
|
||||
|
||||
if not path.endswith('/shares'):
|
||||
return None
|
||||
nickname=None
|
||||
if path.startswith('/users/'):
|
||||
nickname=path.replace('/users/','',1).replace('/shares','')
|
||||
if path.startswith('/@'):
|
||||
nickname=path.replace('/@','',1).replace('/shares','')
|
||||
if not nickname:
|
||||
return None
|
||||
if not validNickname(nickname):
|
||||
return None
|
||||
|
||||
if port!=80 and port!=443:
|
||||
domain=domain+':'+str(port)
|
||||
|
||||
handleDomain=domain
|
||||
if ':' in handleDomain:
|
||||
handleDomain=domain.split(':')[0]
|
||||
handle=nickname+'@'+handleDomain
|
||||
sharesFilename=baseDir+'/accounts/'+handle+'/shares.json'
|
||||
|
||||
if headerOnly:
|
||||
noOfShares=0
|
||||
if os.path.isfile(sharesFilename):
|
||||
with open(sharesFilename, 'r') as fp:
|
||||
sharesJson=commentjson.load(fp)
|
||||
noOfShares=len(sharesJson.items())
|
||||
shares = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'first': httpPrefix+'://'+domain+'/users/'+nickname+'/shares?page=1',
|
||||
'id': httpPrefix+'://'+domain+'/users/'+nickname+'/shares',
|
||||
'totalItems': str(noOfShares),
|
||||
'type': 'OrderedCollection'}
|
||||
return shares
|
||||
|
||||
if not pageNumber:
|
||||
pageNumber=1
|
||||
|
||||
nextPageNumber=int(pageNumber+1)
|
||||
shares = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': httpPrefix+'://'+domain+'/users/'+nickname+'/shares?page='+str(pageNumber),
|
||||
'orderedItems': [],
|
||||
'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/shares',
|
||||
'totalItems': 0,
|
||||
'type': 'OrderedCollectionPage'}
|
||||
|
||||
if not os.path.isfile(sharesFilename):
|
||||
return shares
|
||||
currPage=1
|
||||
pageCtr=0
|
||||
totalCtr=0
|
||||
|
||||
with open(sharesFilename, 'r') as fp:
|
||||
sharesJson=commentjson.load(fp)
|
||||
for itemID,item in sharesJson.items():
|
||||
pageCtr += 1
|
||||
totalCtr += 1
|
||||
if currPage==pageNumber:
|
||||
shares['orderedItems'].append(item)
|
||||
if pageCtr>=sharesPerPage:
|
||||
pageCtr=0
|
||||
currPage += 1
|
||||
shares['totalItems']=totalCtr
|
||||
lastPage=int(totalCtr/sharesPerPage)
|
||||
if lastPage<1:
|
||||
lastPage=1
|
||||
if nextPageNumber>lastPage:
|
||||
shares['next']=httpPrefix+'://'+domain+'/users/'+nickname+'/shares?page='+str(lastPage)
|
||||
return shares
|
|
@ -98,6 +98,18 @@ def htmlProfileSkills(nickname: str,domain: str,skillsJson: {}) -> str:
|
|||
profileStr='<center><div class="skill-title">'+profileStr+'</div></center>'
|
||||
return profileStr
|
||||
|
||||
def htmlProfileShares(nickname: str,domain: str,sharesJson: {}) -> str:
|
||||
"""Shows shares on the profile screen
|
||||
"""
|
||||
profileStr=''
|
||||
for item in sharesJson['orderedItems']:
|
||||
profileStr+='<div>TODO</div><br>'
|
||||
if len(profileStr)==0:
|
||||
profileStr+='<p>@'+nickname+'@'+domain+' is not sharing any items</p>'
|
||||
else:
|
||||
profileStr='<center><div class="share-title">'+profileStr+'</div></center>'
|
||||
return profileStr
|
||||
|
||||
def htmlProfile(baseDir: str,httpPrefix: str,authorized: bool, \
|
||||
ocapAlways: bool,profileJson: {},selected: str, \
|
||||
session,wfRequest: {},personCache: {}, \
|
||||
|
@ -175,6 +187,9 @@ def htmlProfile(baseDir: str,httpPrefix: str,authorized: bool, \
|
|||
if selected=='skills':
|
||||
profileStr+= \
|
||||
htmlProfileSkills(nickname,domainFull,extraJson)
|
||||
if selected=='shares':
|
||||
profileStr+= \
|
||||
htmlProfileShares(nickname,domainFull,extraJson)
|
||||
profileStr=htmlHeader(profileStyle)+profileStr+htmlFooter()
|
||||
return profileStr
|
||||
|
||||
|
|
Loading…
Reference in New Issue