mirror of https://gitlab.com/bashrc2/epicyon
Moderate button
parent
6f882ada1a
commit
cc81de178f
67
daemon.py
67
daemon.py
|
@ -67,6 +67,7 @@ from webinterface import htmlIndividualPost
|
|||
from webinterface import htmlProfile
|
||||
from webinterface import htmlInbox
|
||||
from webinterface import htmlOutbox
|
||||
from webinterface import htmlModeration
|
||||
from webinterface import htmlPostReplies
|
||||
from webinterface import htmlLogin
|
||||
from webinterface import htmlGetLoginCredentials
|
||||
|
@ -1185,8 +1186,9 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self._404()
|
||||
self.server.GETbusy=False
|
||||
return
|
||||
|
||||
# get the inbox for a given person
|
||||
if self.path.endswith('/inbox'):
|
||||
if self.path.endswith('/inbox') or '/inbox?page=' in self.path:
|
||||
if '/users/' in self.path:
|
||||
if authorized:
|
||||
inboxFeed=personBoxJson(self.server.baseDir, \
|
||||
|
@ -1243,7 +1245,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
print('DEBUG: GET access to inbox is unauthorized')
|
||||
self.send_response(405)
|
||||
self.end_headers()
|
||||
self.server.POSTbusy=False
|
||||
self.server.GETbusy=False
|
||||
return
|
||||
|
||||
# get outbox feed for a person
|
||||
|
@ -1290,6 +1292,67 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.server.GETbusy=False
|
||||
return
|
||||
|
||||
# get the moderation feed for a moderator
|
||||
if self.path.endswith('/moderation') or '/moderation?page=' in self.path:
|
||||
if '/users/' in self.path:
|
||||
if authorized:
|
||||
moderationFeed= \
|
||||
personBoxJson(self.server.baseDir, \
|
||||
self.server.domain, \
|
||||
self.server.port, \
|
||||
self.path, \
|
||||
self.server.httpPrefix, \
|
||||
maxPostsInFeed, 'moderation', \
|
||||
True,self.server.ocapAlways)
|
||||
if moderationFeed:
|
||||
if 'text/html' in self.headers['Accept']:
|
||||
nickname=self.path.replace('/users/','').replace('/moderation','')
|
||||
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
|
||||
moderationFeed= \
|
||||
personBoxJson(self.server.baseDir, \
|
||||
self.server.domain, \
|
||||
self.server.port, \
|
||||
self.path+'?page=1', \
|
||||
self.server.httpPrefix, \
|
||||
maxPostsInFeed, 'moderation', \
|
||||
True,self.server.ocapAlways)
|
||||
self._set_headers('text/html',cookie)
|
||||
self.wfile.write(htmlModeration(pageNumber,maxPostsInFeed, \
|
||||
self.server.session, \
|
||||
self.server.baseDir, \
|
||||
self.server.cachedWebfingers, \
|
||||
self.server.personCache, \
|
||||
nickname, \
|
||||
self.server.domain, \
|
||||
self.server.port, \
|
||||
moderationFeed, \
|
||||
True).encode('utf-8'))
|
||||
else:
|
||||
self._set_headers('application/json',None)
|
||||
self.wfile.write(json.dumps(moderationFeed).encode('utf-8'))
|
||||
self.server.GETbusy=False
|
||||
return
|
||||
else:
|
||||
if self.server.debug:
|
||||
nickname=self.path.replace('/users/','').replace('/moderation','')
|
||||
print('DEBUG: '+nickname+ \
|
||||
' was not authorized to access '+self.path)
|
||||
if self.server.debug:
|
||||
print('DEBUG: GET access to moderation feed is unauthorized')
|
||||
self.send_response(405)
|
||||
self.end_headers()
|
||||
self.server.GETbusy=False
|
||||
return
|
||||
|
||||
shares=getSharesFeedForPerson(self.server.baseDir, \
|
||||
self.server.domain, \
|
||||
self.server.port,self.path, \
|
||||
|
|
|
@ -151,7 +151,7 @@ a:link {
|
|||
text-align: center;
|
||||
font-size: 18px;
|
||||
padding: 10px;
|
||||
width: 20%;
|
||||
width: 15%;
|
||||
max-width: 200px;
|
||||
min-width: 100px;
|
||||
transition: all 0.5s;
|
||||
|
|
10
person.py
10
person.py
|
@ -19,6 +19,7 @@ from webfinger import createWebfingerEndpoint
|
|||
from webfinger import storeWebfingerEndpoint
|
||||
from posts import createInbox
|
||||
from posts import createOutbox
|
||||
from posts import createModeration
|
||||
from auth import storeBasicCredentials
|
||||
from roles import setRole
|
||||
from media import removeMetaData
|
||||
|
@ -340,9 +341,9 @@ def personLookup(domain: str,path: str,baseDir: str) -> {}:
|
|||
def personBoxJson(baseDir: str,domain: str,port: int,path: str, \
|
||||
httpPrefix: str,noOfItems: int,boxname: str, \
|
||||
authorized: bool,ocapAlways: bool) -> []:
|
||||
"""Obtain the inbox/outbox feed for the given person
|
||||
"""Obtain the inbox/outbox/moderation feed for the given person
|
||||
"""
|
||||
if boxname!='inbox' and boxname!='outbox':
|
||||
if boxname!='inbox' and boxname!='outbox' and boxname!='moderation':
|
||||
return None
|
||||
|
||||
if not '/'+boxname in path:
|
||||
|
@ -379,8 +380,13 @@ def personBoxJson(baseDir: str,domain: str,port: int,path: str, \
|
|||
if boxname=='inbox':
|
||||
return createInbox(baseDir,nickname,domain,port,httpPrefix, \
|
||||
noOfItems,headerOnly,ocapAlways,pageNumber)
|
||||
elif boxname=='outbox':
|
||||
return createOutbox(baseDir,nickname,domain,port,httpPrefix, \
|
||||
noOfItems,headerOnly,authorized,pageNumber)
|
||||
elif boxname=='moderation':
|
||||
return createModeration(baseDir,nickname,domain,port,httpPrefix, \
|
||||
noOfItems,headerOnly,authorized,pageNumber)
|
||||
return None
|
||||
|
||||
def personInboxJson(baseDir: str,domain: str,port: int,path: str, \
|
||||
httpPrefix: str,noOfItems: int,ocapAlways: bool) -> []:
|
||||
|
|
86
posts.py
86
posts.py
|
@ -47,6 +47,27 @@ try:
|
|||
except ImportError:
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
def isModerator(baseDir: str,nickname: str) -> bool:
|
||||
"""Returns true if the given nickname is a moderator
|
||||
"""
|
||||
moderatorsFile=baseDir+'/accounts/moderators.txt'
|
||||
|
||||
if not os.path.isfile(moderatorsFile):
|
||||
if getConfigParam(baseDir,'admin')==nickname:
|
||||
return True
|
||||
return False
|
||||
|
||||
with open(moderatorsFile, "r") as f:
|
||||
lines = f.readlines()
|
||||
if len(lines)==0:
|
||||
if getConfigParam(baseDir,'admin')==nickname:
|
||||
return True
|
||||
for moderator in lines:
|
||||
moderator=moderator.strip('\n')
|
||||
if moderator==nickname:
|
||||
return True
|
||||
return False
|
||||
|
||||
def noOfFollowersOnDomain(baseDir: str,handle: str, \
|
||||
domain: str, followFile='followers.txt') -> int:
|
||||
"""Returns the number of followers of the given handle from the given domain
|
||||
|
@ -551,10 +572,17 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
|
|||
|
||||
# if this is a moderation report then add a status
|
||||
if isModerationReport:
|
||||
# add status
|
||||
if newPost.get('object'):
|
||||
newPost['object']['moderationStatus']='pending'
|
||||
else:
|
||||
newPost['moderationStatus']='pending'
|
||||
# save to index file
|
||||
moderationIndexFile=baseDir+'/accounts/moderation.txt'
|
||||
modFile=open(moderationIndexFile, "a+")
|
||||
if modFile:
|
||||
modFile.write(newPostId+'\n')
|
||||
modFile.close()
|
||||
|
||||
if saveToFile:
|
||||
savePostToBox(baseDir,httpPrefix,newPostId, \
|
||||
|
@ -1298,11 +1326,69 @@ def createInbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str
|
|||
itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}:
|
||||
return createBoxBase(baseDir,'inbox',nickname,domain,port,httpPrefix, \
|
||||
itemsPerPage,headerOnly,True,ocapAlways,pageNumber)
|
||||
|
||||
def createOutbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \
|
||||
itemsPerPage: int,headerOnly: bool,authorized: bool,pageNumber=None) -> {}:
|
||||
return createBoxBase(baseDir,'outbox',nickname,domain,port,httpPrefix, \
|
||||
itemsPerPage,headerOnly,authorized,False,pageNumber)
|
||||
|
||||
def createModeration(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \
|
||||
itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}:
|
||||
boxDir = createPersonDir(nickname,domain,baseDir,'inbox')
|
||||
boxname='moderation'
|
||||
|
||||
if port!=80 and port!=443:
|
||||
domain = domain+':'+str(port)
|
||||
|
||||
if not pageNumber:
|
||||
pageNumber=1
|
||||
|
||||
pageStr='?page='+str(pageNumber)
|
||||
boxHeader = {'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'first': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true',
|
||||
'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname,
|
||||
'last': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true',
|
||||
'totalItems': 0,
|
||||
'type': 'OrderedCollection'}
|
||||
boxItems = {'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+pageStr,
|
||||
'orderedItems': [
|
||||
],
|
||||
'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname,
|
||||
'type': 'OrderedCollectionPage'}
|
||||
|
||||
if isModerator(baseDir,nickname):
|
||||
moderationIndexFile=baseDir+'/accounts/moderation.txt'
|
||||
if os.path.isfile(moderationIndexFile):
|
||||
with open(moderationIndexFile, "r") as f:
|
||||
lines = f.readlines()
|
||||
boxHeader['totalItems']=len(lines)
|
||||
if headerOnly:
|
||||
return boxHeader
|
||||
|
||||
pageLines=[]
|
||||
if len(lines)>0:
|
||||
endLineNumber=len(lines)-1-int(itemsPerPage*pageNumber)
|
||||
if endLineNumber<0:
|
||||
endLineNumber=0
|
||||
startLineNumber=len(lines)-1-int(itemsPerPage*(pageNumber-1))
|
||||
if startLineNumber<0:
|
||||
startLineNumber=0
|
||||
lineNumber=startLineNumber
|
||||
while lineNumber>=endLineNumber:
|
||||
pageLines.append(lines[lineNumber].strip('\n'))
|
||||
lineNumber-=1
|
||||
|
||||
for postUrl in pageLines:
|
||||
postFilename=boxDir+'/'+postUrl.replace('/','#')+'.json'
|
||||
if os.path.isfile(postFilename):
|
||||
with open(postFilename, 'r') as fp:
|
||||
postJsonObject=commentjson.load(fp)
|
||||
boxItems['orderedItems'].append(postJsonObject)
|
||||
if headerOnly:
|
||||
return boxHeader
|
||||
return boxItems
|
||||
|
||||
def getStatusNumberFromPostFilename(filename) -> int:
|
||||
"""Gets the status number from a post filename
|
||||
eg. https:##testdomain.com:8085#users#testuser567#statuses#1562958506952068.json
|
||||
|
|
17
utils.py
17
utils.py
|
@ -176,6 +176,23 @@ def deletePost(baseDir: str,httpPrefix: str,nickname: str,domain: str,postFilena
|
|||
# remove any attachment
|
||||
removeAttachment(baseDir,httpPrefix,domain,postJsonObject)
|
||||
|
||||
# remove from moderation index file
|
||||
if postJsonObject.get('moderationStatus'):
|
||||
moderationIndexFile=baseDir+'/accounts/moderation.txt'
|
||||
if os.path.isfile(moderationIndexFile):
|
||||
if postJsonObject.get('object'):
|
||||
if isinstance(postJsonObject['object'], dict):
|
||||
if postJsonObject['object'].get('id'):
|
||||
# get the id of the post
|
||||
postId=postJsonObject['object']['id'].replace('/activity','')
|
||||
if postId in open(moderationIndexFile).read():
|
||||
with open(moderationIndexFile, "r") as f:
|
||||
lines = f.readlines()
|
||||
with open(moderationIndexFile, "w+") as f:
|
||||
for line in lines:
|
||||
if line.strip("\n") != postId:
|
||||
f.write(line)
|
||||
|
||||
# remove any hashtags index entries
|
||||
removeHashtagIndex=False
|
||||
if postJsonObject.get('object'):
|
||||
|
|
|
@ -25,6 +25,7 @@ from posts import getPersonBox
|
|||
from posts import getUserUrl
|
||||
from posts import parseUserFeed
|
||||
from posts import populateRepliesJson
|
||||
from posts import isModerator
|
||||
from session import getJson
|
||||
from auth import createPassword
|
||||
from like import likedByPerson
|
||||
|
@ -34,6 +35,17 @@ from content import getMentionsFromHtml
|
|||
from config import getConfigParam
|
||||
from skills import getSkills
|
||||
|
||||
def noOfModerationPosts(baseDir: str) -> int:
|
||||
"""Returns the number of posts addressed to moderators
|
||||
"""
|
||||
moderationIndexFile=baseDir+'/accounts/moderation.txt'
|
||||
if not os.path.isfile(moderationIndexFile):
|
||||
return 0
|
||||
with open(moderationIndexFile, "r") as f:
|
||||
lines = f.readlines()
|
||||
return len(lines)
|
||||
return 0
|
||||
|
||||
def htmlHashtagSearch(baseDir: str,hashtag: str,pageNumber: int,postsPerPage: int,
|
||||
session,wfRequest: {},personCache: {}) -> str:
|
||||
"""Show a page containing search results for a hashtag
|
||||
|
@ -961,12 +973,17 @@ def htmlTimeline(pageNumber: int,itemsPerPage: int,session,baseDir: str, \
|
|||
cssFile.read().replace('banner.png', \
|
||||
'/users/'+nickname+'/banner.png')
|
||||
|
||||
moderator=isModerator(baseDir,nickname)
|
||||
|
||||
inboxButton='button'
|
||||
sentButton='button'
|
||||
moderationButton='button'
|
||||
if boxName=='inbox':
|
||||
inboxButton='buttonselected'
|
||||
elif boxName=='outbox':
|
||||
sentButton='buttonselected'
|
||||
elif boxName=='moderation':
|
||||
moderationButton='buttonselected'
|
||||
actor='/users/'+nickname
|
||||
|
||||
showIndividualPostIcons=True
|
||||
|
@ -983,6 +1000,11 @@ def htmlTimeline(pageNumber: int,itemsPerPage: int,session,baseDir: str, \
|
|||
followApprovals='<a href="'+actor+'/followers"><img class="right" alt="Approve follow requests" title="Approve follow requests" src="/icons/person.png"/></a>'
|
||||
break
|
||||
|
||||
moderationButtonStr=''
|
||||
if moderator:
|
||||
if noOfModerationPosts(baseDir)>0:
|
||||
moderationButtonStr='<a href="'+actor+'/moderation"><button class="'+moderationButton+'"><span>Moderate </span></button></a>'
|
||||
|
||||
tlStr=htmlHeader(profileStyle)
|
||||
tlStr+= \
|
||||
'<a href="/users/'+nickname+'" title="Switch to profile view" alt="Switch to profile view">' \
|
||||
|
@ -990,7 +1012,8 @@ def htmlTimeline(pageNumber: int,itemsPerPage: int,session,baseDir: str, \
|
|||
'</div></a>' \
|
||||
'<div class="container">\n'+ \
|
||||
' <a href="'+actor+'/inbox"><button class="'+inboxButton+'"><span>Inbox </span></button></a>' \
|
||||
' <a href="'+actor+'/outbox"><button class="'+sentButton+'"><span>Sent </span></button></a>' \
|
||||
' <a href="'+actor+'/outbox"><button class="'+sentButton+'"><span>Sent </span></button></a>'+ \
|
||||
moderationButtonStr+ \
|
||||
' <a href="'+actor+'/newpost"><img src="/icons/newpost.png" title="Create a new post" alt="Create a new post" class="right"/></a>'+ \
|
||||
' <a href="'+actor+'/search"><img src="/icons/search.png" title="Search and follow" alt="Search and follow" class="right"/></a>'+ \
|
||||
followApprovals+ \
|
||||
|
@ -1019,6 +1042,15 @@ def htmlInbox(pageNumber: int,itemsPerPage: int, \
|
|||
return htmlTimeline(pageNumber,itemsPerPage,session,baseDir,wfRequest,personCache, \
|
||||
nickname,domain,port,inboxJson,'inbox',allowDeletion)
|
||||
|
||||
def htmlModeration(pageNumber: int,itemsPerPage: int, \
|
||||
session,baseDir: str,wfRequest: {},personCache: {}, \
|
||||
nickname: str,domain: str,port: int,inboxJson: {}, \
|
||||
allowDeletion: bool) -> str:
|
||||
"""Show the moderation feed as html
|
||||
"""
|
||||
return htmlTimeline(pageNumber,itemsPerPage,session,baseDir,wfRequest,personCache, \
|
||||
nickname,domain,port,inboxJson,'moderation',allowDeletion)
|
||||
|
||||
def htmlOutbox(pageNumber: int,itemsPerPage: int, \
|
||||
session,baseDir: str,wfRequest: {},personCache: {}, \
|
||||
nickname: str,domain: str,port: int,outboxJson: {}, \
|
||||
|
|
Loading…
Reference in New Issue