Moderate button

master
Bob Mottram 2019-08-12 14:22:17 +01:00
parent 6f882ada1a
commit cc81de178f
6 changed files with 212 additions and 8 deletions

View File

@ -67,6 +67,7 @@ from webinterface import htmlIndividualPost
from webinterface import htmlProfile from webinterface import htmlProfile
from webinterface import htmlInbox from webinterface import htmlInbox
from webinterface import htmlOutbox from webinterface import htmlOutbox
from webinterface import htmlModeration
from webinterface import htmlPostReplies from webinterface import htmlPostReplies
from webinterface import htmlLogin from webinterface import htmlLogin
from webinterface import htmlGetLoginCredentials from webinterface import htmlGetLoginCredentials
@ -1185,8 +1186,9 @@ class PubServer(BaseHTTPRequestHandler):
self._404() self._404()
self.server.GETbusy=False self.server.GETbusy=False
return return
# get the inbox for a given person # 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 '/users/' in self.path:
if authorized: if authorized:
inboxFeed=personBoxJson(self.server.baseDir, \ inboxFeed=personBoxJson(self.server.baseDir, \
@ -1243,7 +1245,7 @@ class PubServer(BaseHTTPRequestHandler):
print('DEBUG: GET access to inbox is unauthorized') print('DEBUG: GET access to inbox is unauthorized')
self.send_response(405) self.send_response(405)
self.end_headers() self.end_headers()
self.server.POSTbusy=False self.server.GETbusy=False
return return
# get outbox feed for a person # get outbox feed for a person
@ -1290,6 +1292,67 @@ class PubServer(BaseHTTPRequestHandler):
self.server.GETbusy=False self.server.GETbusy=False
return 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, \ shares=getSharesFeedForPerson(self.server.baseDir, \
self.server.domain, \ self.server.domain, \
self.server.port,self.path, \ self.server.port,self.path, \

View File

@ -151,7 +151,7 @@ a:link {
text-align: center; text-align: center;
font-size: 18px; font-size: 18px;
padding: 10px; padding: 10px;
width: 20%; width: 15%;
max-width: 200px; max-width: 200px;
min-width: 100px; min-width: 100px;
transition: all 0.5s; transition: all 0.5s;

View File

@ -19,6 +19,7 @@ from webfinger import createWebfingerEndpoint
from webfinger import storeWebfingerEndpoint from webfinger import storeWebfingerEndpoint
from posts import createInbox from posts import createInbox
from posts import createOutbox from posts import createOutbox
from posts import createModeration
from auth import storeBasicCredentials from auth import storeBasicCredentials
from roles import setRole from roles import setRole
from media import removeMetaData 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, \ def personBoxJson(baseDir: str,domain: str,port: int,path: str, \
httpPrefix: str,noOfItems: int,boxname: str, \ httpPrefix: str,noOfItems: int,boxname: str, \
authorized: bool,ocapAlways: bool) -> []: 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 return None
if not '/'+boxname in path: if not '/'+boxname in path:
@ -379,8 +380,13 @@ def personBoxJson(baseDir: str,domain: str,port: int,path: str, \
if boxname=='inbox': if boxname=='inbox':
return createInbox(baseDir,nickname,domain,port,httpPrefix, \ return createInbox(baseDir,nickname,domain,port,httpPrefix, \
noOfItems,headerOnly,ocapAlways,pageNumber) noOfItems,headerOnly,ocapAlways,pageNumber)
return createOutbox(baseDir,nickname,domain,port,httpPrefix, \ elif boxname=='outbox':
noOfItems,headerOnly,authorized,pageNumber) 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, \ def personInboxJson(baseDir: str,domain: str,port: int,path: str, \
httpPrefix: str,noOfItems: int,ocapAlways: bool) -> []: httpPrefix: str,noOfItems: int,ocapAlways: bool) -> []:

View File

@ -47,6 +47,27 @@ try:
except ImportError: except ImportError:
from bs4 import BeautifulSoup 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, \ def noOfFollowersOnDomain(baseDir: str,handle: str, \
domain: str, followFile='followers.txt') -> int: domain: str, followFile='followers.txt') -> int:
"""Returns the number of followers of the given handle from the given domain """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 this is a moderation report then add a status
if isModerationReport: if isModerationReport:
# add status
if newPost.get('object'): if newPost.get('object'):
newPost['object']['moderationStatus']='pending' newPost['object']['moderationStatus']='pending'
else: else:
newPost['moderationStatus']='pending' 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: if saveToFile:
savePostToBox(baseDir,httpPrefix,newPostId, \ 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) -> {}: itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}:
return createBoxBase(baseDir,'inbox',nickname,domain,port,httpPrefix, \ return createBoxBase(baseDir,'inbox',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,True,ocapAlways,pageNumber) itemsPerPage,headerOnly,True,ocapAlways,pageNumber)
def createOutbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ def createOutbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \
itemsPerPage: int,headerOnly: bool,authorized: bool,pageNumber=None) -> {}: itemsPerPage: int,headerOnly: bool,authorized: bool,pageNumber=None) -> {}:
return createBoxBase(baseDir,'outbox',nickname,domain,port,httpPrefix, \ return createBoxBase(baseDir,'outbox',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,authorized,False,pageNumber) 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: def getStatusNumberFromPostFilename(filename) -> int:
"""Gets the status number from a post filename """Gets the status number from a post filename
eg. https:##testdomain.com:8085#users#testuser567#statuses#1562958506952068.json eg. https:##testdomain.com:8085#users#testuser567#statuses#1562958506952068.json

View File

@ -176,6 +176,23 @@ def deletePost(baseDir: str,httpPrefix: str,nickname: str,domain: str,postFilena
# remove any attachment # remove any attachment
removeAttachment(baseDir,httpPrefix,domain,postJsonObject) 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 # remove any hashtags index entries
removeHashtagIndex=False removeHashtagIndex=False
if postJsonObject.get('object'): if postJsonObject.get('object'):

View File

@ -25,6 +25,7 @@ from posts import getPersonBox
from posts import getUserUrl from posts import getUserUrl
from posts import parseUserFeed from posts import parseUserFeed
from posts import populateRepliesJson from posts import populateRepliesJson
from posts import isModerator
from session import getJson from session import getJson
from auth import createPassword from auth import createPassword
from like import likedByPerson from like import likedByPerson
@ -34,6 +35,17 @@ from content import getMentionsFromHtml
from config import getConfigParam from config import getConfigParam
from skills import getSkills 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, def htmlHashtagSearch(baseDir: str,hashtag: str,pageNumber: int,postsPerPage: int,
session,wfRequest: {},personCache: {}) -> str: session,wfRequest: {},personCache: {}) -> str:
"""Show a page containing search results for a hashtag """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', \ cssFile.read().replace('banner.png', \
'/users/'+nickname+'/banner.png') '/users/'+nickname+'/banner.png')
moderator=isModerator(baseDir,nickname)
inboxButton='button' inboxButton='button'
sentButton='button' sentButton='button'
moderationButton='button'
if boxName=='inbox': if boxName=='inbox':
inboxButton='buttonselected' inboxButton='buttonselected'
elif boxName=='outbox': elif boxName=='outbox':
sentButton='buttonselected' sentButton='buttonselected'
elif boxName=='moderation':
moderationButton='buttonselected'
actor='/users/'+nickname actor='/users/'+nickname
showIndividualPostIcons=True 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>' followApprovals='<a href="'+actor+'/followers"><img class="right" alt="Approve follow requests" title="Approve follow requests" src="/icons/person.png"/></a>'
break 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=htmlHeader(profileStyle)
tlStr+= \ tlStr+= \
'<a href="/users/'+nickname+'" title="Switch to profile view" alt="Switch to profile view">' \ '<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></a>' \
'<div class="container">\n'+ \ '<div class="container">\n'+ \
' <a href="'+actor+'/inbox"><button class="'+inboxButton+'"><span>Inbox </span></button></a>' \ ' <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+'/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>'+ \ ' <a href="'+actor+'/search"><img src="/icons/search.png" title="Search and follow" alt="Search and follow" class="right"/></a>'+ \
followApprovals+ \ followApprovals+ \
@ -1019,6 +1042,15 @@ def htmlInbox(pageNumber: int,itemsPerPage: int, \
return htmlTimeline(pageNumber,itemsPerPage,session,baseDir,wfRequest,personCache, \ return htmlTimeline(pageNumber,itemsPerPage,session,baseDir,wfRequest,personCache, \
nickname,domain,port,inboxJson,'inbox',allowDeletion) 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, \ def htmlOutbox(pageNumber: int,itemsPerPage: int, \
session,baseDir: str,wfRequest: {},personCache: {}, \ session,baseDir: str,wfRequest: {},personCache: {}, \
nickname: str,domain: str,port: int,outboxJson: {}, \ nickname: str,domain: str,port: int,outboxJson: {}, \