Selecting profile images

master
Bob Mottram 2019-08-02 10:52:12 +01:00
parent f5da898007
commit 52a4d17512
2 changed files with 193 additions and 5 deletions

122
daemon.py
View File

@ -69,6 +69,7 @@ from webinterface import htmlFollowConfirm
from webinterface import htmlSearch from webinterface import htmlSearch
from webinterface import htmlUnfollowConfirm from webinterface import htmlUnfollowConfirm
from webinterface import htmlProfileAfterSearch from webinterface import htmlProfileAfterSearch
from webinterface import htmlEditProfile
from shares import getSharesFeedForPerson from shares import getSharesFeedForPerson
from shares import outboxShareUpload from shares import outboxShareUpload
from shares import outboxUndoShareUpload from shares import outboxUndoShareUpload
@ -79,6 +80,8 @@ from manualapprove import manualDenyFollowRequest
from manualapprove import manualApproveFollowRequest from manualapprove import manualApproveFollowRequest
from announce import createAnnounce from announce import createAnnounce
from announce import outboxAnnounce from announce import outboxAnnounce
from content import addMentions
from media import removeMetaData
import os import os
import sys import sys
@ -756,6 +759,14 @@ class PubServer(BaseHTTPRequestHandler):
inReplyTo=self.path.split('?replyto=')[1] inReplyTo=self.path.split('?replyto=')[1]
self.path=self.path.split('?replyto=')[0]+'/newpost' self.path=self.path.split('?replyto=')[0]+'/newpost'
# edit profile in web interface
if '/users/' in self.path and self.path.endswith('/editprofile'):
self._set_headers('text/html',cookie)
self.wfile.write(htmlEditProfile(self.server.baseDir,self.path,self.server.domain,self.server.port).encode())
self.server.GETbusy=False
return
# Various types of new post in the web interface
if '/users/' in self.path and \ if '/users/' in self.path and \
(self.path.endswith('/newpost') or \ (self.path.endswith('/newpost') or \
self.path.endswith('/newunlisted') or \ self.path.endswith('/newunlisted') or \
@ -1360,7 +1371,6 @@ class PubServer(BaseHTTPRequestHandler):
fd.close() fd.close()
# send the post # send the post
if not fields.get('message'): if not fields.get('message'):
return -1 return -1
if fields.get('submitPost'): if fields.get('submitPost'):
@ -1602,6 +1612,116 @@ class PubServer(BaseHTTPRequestHandler):
self._redirect_headers(originPathStr,cookie) self._redirect_headers(originPathStr,cookie)
self.server.POSTbusy=False self.server.POSTbusy=False
# update of profile/avatar from web interface
if authorized and self.path.endswith('/profiledata'):
if ' boundary=' in self.headers['Content-type']:
boundary=self.headers['Content-type'].split('boundary=')[1]
if ';' in boundary:
boundary=boundary.split(';')[0]
actorStr=self.path.replace('/profiledata','').replace('/editprofile','')
nickname=getNicknameFromActor(actorStr)
if not nickname:
self._redirect_headers(actorStr,cookie)
self.server.POSTbusy=False
return
length = int(self.headers['Content-length'])
postBytes=self.rfile.read(length)
msg = email.parser.BytesParser().parsebytes(postBytes)
messageFields=msg.get_payload(decode=False).split(boundary)
fields={}
filename=None
lastImageLocation=0
for f in messageFields:
if f=='--':
continue
if ' name="' in f:
postStr=f.split(' name="',1)[1]
if '"' in postStr:
postKey=postStr.split('"',1)[0]
postValueStr=postStr.split('"',1)[1]
if ';' not in postValueStr:
if '\r\n' in postValueStr:
postLines=postValueStr.split('\r\n')
postValue=''
if len(postLines)>2:
for line in range(2,len(postLines)-1):
if line>2:
postValue+='\n'
postValue+=postLines[line]
fields[postKey]=postValue
else:
# directly search the binary array for the beginning
# of an image
searchStr=b'Content-Type: image/png'
imageLocation=postBytes.find(searchStr,lastImageLocation)
filenameBase=self.server.baseDir+'/accounts/'+nickname+'@'+self.server.domain+'/'+postKey
if imageLocation>-1:
filename=filenameBase+'.png.temp'
else:
searchStr=b'Content-Type: image/jpeg'
imageLocation=postBytes.find(searchStr,lastImageLocation)
if imageLocation>-1:
filename=filenameBase+'.jpg.temp'
else:
searchStr=b'Content-Type: image/gif'
imageLocation=postBytes.find(searchStr,lastImageLocation)
if imageLocation>-1:
filename=filenameBase+'.gif.temp'
if filename and imageLocation>-1:
# locate the beginning of the image, after any
# carriage returns
startPos=imageLocation+len(searchStr)
for offset in range(1,8):
if postBytes[startPos+offset]!=10:
if postBytes[startPos+offset]!=13:
startPos+=offset
break
# look for the end
imageLocationEnd=postBytes.find(b'-------',imageLocation+1)
fd = open(filename, 'wb')
if imageLocationEnd>-1:
fd.write(postBytes[startPos:][:imageLocationEnd-startPos])
else:
fd.write(postBytes[startPos:])
fd.close()
removeMetaData(filename,filename.replace('.temp',''))
os.remove(filename)
lastImageLocation=imageLocation+1
actorFilename=self.server.baseDir+'/accounts/'+nickname+'@'+self.server.domain+'.json'
if os.path.isfile(actorFilename):
with open(actorFilename, 'r') as fp:
actorJson=commentjson.load(fp)
actorChanged=False
if fields.get('preferredNickname'):
if fields['preferredNickname']!=actorJson['preferredUsername']:
actorJson['preferredUsername']=fields['preferredNickname']
actorChanged=True
if fields.get('bio'):
if fields['bio']!=actorJson['summary']:
actorJson['summary']= \
addMentions(self.server.baseDir, \
self.server.httpPrefix, \
nickname, \
self.server.domain,fields['bio'])
actorChanged=True
if fields.get('approveFollowers'):
approveFollowers=False
if fields['approveFollowers']!='no':
approveFollowers=True
if approveFollowers!=actorJson['manuallyApprovesFollowers']:
actorJson['manuallyApprovesFollowers']=approveFollowers
actorChanged=True
if actorChanged:
with open(actorFilename, 'w') as fp:
commentjson.dump(actorJson, fp, indent=4, sort_keys=False)
self._redirect_headers(actorStr,cookie)
self.server.POSTbusy=False
return
# decision to follow in the web interface is confirmed # decision to follow in the web interface is confirmed
if authorized and self.path.endswith('/searchhandle'): if authorized and self.path.endswith('/searchhandle'):
actorStr=self.path.replace('/searchhandle','') actorStr=self.path.replace('/searchhandle','')

View File

@ -9,6 +9,7 @@ __status__ = "Production"
import json import json
import time import time
import os import os
import commentjson
from datetime import datetime from datetime import datetime
from shutil import copyfile from shutil import copyfile
from pprint import pprint from pprint import pprint
@ -25,6 +26,71 @@ from auth import createPassword
from like import likedByPerson from like import likedByPerson
from announce import announcedByPerson from announce import announcedByPerson
def htmlEditProfile(baseDir: str,path: str,domain: str,port: int) -> str:
"""Shows the edit profile screen
"""
pathOriginal=path
path=path.replace('/inbox','').replace('/outbox','').replace('/shares','')
nickname=getNicknameFromActor(path)
domainFull=domain
if port:
if port!=80 and port!=443:
domainFull=domain+':'+str(port)
actorFilename=baseDir+'/accounts/'+nickname+'@'+domain+'.json'
if not os.path.isfile(actorFilename):
return ''
preferredNickname=nickname
bioStr=''
manuallyApprovesFollowers='checked'
with open(actorFilename, 'r') as fp:
actorJson=commentjson.load(fp)
if actorJson.get('preferredUsername'):
preferredNickname=actorJson['preferredUsername']
if actorJson.get('summary'):
bioStr=actorJson['summary']
if actorJson.get('manuallyApprovesFollowers'):
if actorJson['manuallyApprovesFollowers']:
manuallyApprovesFollowers='checked'
else:
manuallyApprovesFollowers=''
with open(baseDir+'/epicyon-profile.css', 'r') as cssFile:
newPostCSS = cssFile.read()
editProfileForm=htmlHeader(newPostCSS)
editProfileForm+= \
'<form enctype="multipart/form-data" method="POST" action="'+path+'/profiledata">' \
' <div class="vertical-center">' \
' <p class="new-post-text">Edit profile for '+nickname+'@'+domainFull+'</p>' \
' <div class="container">' \
' <input type="text" placeholder="Preferred name" name="preferredNickname" value="'+preferredNickname+'">' \
' <textarea id="message" name="bio" placeholder="Your bio..." style="height:200px">'+bioStr+'</textarea>' \
' </div>' \
' <div class="container">' \
' Avatar image' \
' <input type="file" id="avatar" name="avatar"' \
' accept=".png">' \
' <br>Background image' \
' <input type="file" id="image" name="image"' \
' accept=".png">' \
' <br>Timeline banner image' \
' <input type="file" id="banner" name="banner"' \
' accept=".png">' \
' </div>' \
' <div class="container">' \
' <input type="checkbox" name="approveFollowers" '+manuallyApprovesFollowers+'>Approve follower requests<br>' \
' </div>' \
' <div class="container">' \
' <input type="submit" name="submitProfile" value="Submit">' \
' <a href="'+pathOriginal+'"><button class="cancelbtn">Cancel</button></a>' \
' </div>'+ \
' </div>' \
'</form>'
editProfileForm+=htmlFooter()
return editProfileForm
def htmlGetLoginCredentials(loginParams: str,lastLoginTime: int) -> (str,str): def htmlGetLoginCredentials(loginParams: str,lastLoginTime: int) -> (str,str):
"""Receives login credentials via HTTPServer POST """Receives login credentials via HTTPServer POST
""" """
@ -297,7 +363,6 @@ def htmlProfile(baseDir: str,httpPrefix: str,authorized: bool, \
if port: if port:
domainFull=domain+':'+str(port) domainFull=domain+':'+str(port)
profileDescription=profileJson['summary'] profileDescription=profileJson['summary']
profileDescription='A test description'
postsButton='button' postsButton='button'
followingButton='button' followingButton='button'
followersButton='button' followersButton='button'
@ -322,10 +387,13 @@ def htmlProfile(baseDir: str,httpPrefix: str,authorized: bool, \
followApprovals='' followApprovals=''
linkToTimelineStart='' linkToTimelineStart=''
linkToTimelineEnd='' linkToTimelineEnd=''
editProfileStr=''
actor=profileJson['id']
if not authorized: if not authorized:
loginButton='<br><a href="/login"><button class="loginButton">Login</button></a>' loginButton='<br><a href="/login"><button class="loginButton">Login</button></a>'
else: else:
editProfileStr='<a href="'+actor+'/editprofile"><button class="button"><span>Edit </span></button></a>'
linkToTimelineStart='<a href="/users/'+nickname+'/inbox" title="Switch to timeline view" alt="Switch to timeline view">' linkToTimelineStart='<a href="/users/'+nickname+'/inbox" title="Switch to timeline view" alt="Switch to timeline view">'
linkToTimelineEnd='</a>' linkToTimelineEnd='</a>'
# are there any follow requests? # are there any follow requests?
@ -356,11 +424,10 @@ def htmlProfile(baseDir: str,httpPrefix: str,authorized: bool, \
followApprovalsSection+='<button class="followDeny">Deny</button></a>' followApprovalsSection+='<button class="followDeny">Deny</button></a>'
followApprovalsSection+='</div>' followApprovalsSection+='</div>'
actor=profileJson['id']
profileStr= \ profileStr= \
linkToTimelineStart+ \ linkToTimelineStart+ \
' <div class="hero-image">' \ ' <div class="hero-image">' \
' <div class="hero-text">' \ ' <div class="hero-text">'+ \
' <img src="'+profileJson['icon']['url']+'" alt="'+nickname+'@'+domainFull+'">' \ ' <img src="'+profileJson['icon']['url']+'" alt="'+nickname+'@'+domainFull+'">' \
' <h1>'+preferredName+'</h1>' \ ' <h1>'+preferredName+'</h1>' \
' <p><b>@'+nickname+'@'+domainFull+'</b></p>' \ ' <p><b>@'+nickname+'@'+domainFull+'</b></p>' \
@ -376,7 +443,8 @@ def htmlProfile(baseDir: str,httpPrefix: str,authorized: bool, \
' <a href="'+actor+'/followers"><button class="'+followersButton+'"><span>Followers </span>'+followApprovals+'</button></a>' \ ' <a href="'+actor+'/followers"><button class="'+followersButton+'"><span>Followers </span>'+followApprovals+'</button></a>' \
' <a href="'+actor+'/roles"><button class="'+rolesButton+'"><span>Roles </span></button></a>' \ ' <a href="'+actor+'/roles"><button class="'+rolesButton+'"><span>Roles </span></button></a>' \
' <a href="'+actor+'/skills"><button class="'+skillsButton+'"><span>Skills </span></button></a>' \ ' <a href="'+actor+'/skills"><button class="'+skillsButton+'"><span>Skills </span></button></a>' \
' <a href="'+actor+'/shares"><button class="'+sharesButton+'"><span>Shares </span></button></a>' \ ' <a href="'+actor+'/shares"><button class="'+sharesButton+'"><span>Shares </span></button></a>'+ \
editProfileStr+ \
' </center>' \ ' </center>' \
'</div>' '</div>'