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 htmlUnfollowConfirm
from webinterface import htmlProfileAfterSearch
from webinterface import htmlEditProfile
from shares import getSharesFeedForPerson
from shares import outboxShareUpload
from shares import outboxUndoShareUpload
@ -79,6 +80,8 @@ from manualapprove import manualDenyFollowRequest
from manualapprove import manualApproveFollowRequest
from announce import createAnnounce
from announce import outboxAnnounce
from content import addMentions
from media import removeMetaData
import os
import sys
@ -756,6 +759,14 @@ class PubServer(BaseHTTPRequestHandler):
inReplyTo=self.path.split('?replyto=')[1]
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 \
(self.path.endswith('/newpost') or \
self.path.endswith('/newunlisted') or \
@ -1360,7 +1371,6 @@ class PubServer(BaseHTTPRequestHandler):
fd.close()
# send the post
if not fields.get('message'):
return -1
if fields.get('submitPost'):
@ -1602,6 +1612,116 @@ class PubServer(BaseHTTPRequestHandler):
self._redirect_headers(originPathStr,cookie)
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
if authorized and self.path.endswith('/searchhandle'):
actorStr=self.path.replace('/searchhandle','')

View File

@ -9,6 +9,7 @@ __status__ = "Production"
import json
import time
import os
import commentjson
from datetime import datetime
from shutil import copyfile
from pprint import pprint
@ -25,6 +26,71 @@ from auth import createPassword
from like import likedByPerson
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):
"""Receives login credentials via HTTPServer POST
"""
@ -297,7 +363,6 @@ def htmlProfile(baseDir: str,httpPrefix: str,authorized: bool, \
if port:
domainFull=domain+':'+str(port)
profileDescription=profileJson['summary']
profileDescription='A test description'
postsButton='button'
followingButton='button'
followersButton='button'
@ -322,10 +387,13 @@ def htmlProfile(baseDir: str,httpPrefix: str,authorized: bool, \
followApprovals=''
linkToTimelineStart=''
linkToTimelineEnd=''
editProfileStr=''
actor=profileJson['id']
if not authorized:
loginButton='<br><a href="/login"><button class="loginButton">Login</button></a>'
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">'
linkToTimelineEnd='</a>'
# 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+='</div>'
actor=profileJson['id']
profileStr= \
linkToTimelineStart+ \
' <div class="hero-image">' \
' <div class="hero-text">' \
' <div class="hero-text">'+ \
' <img src="'+profileJson['icon']['url']+'" alt="'+nickname+'@'+domainFull+'">' \
' <h1>'+preferredName+'</h1>' \
' <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+'/roles"><button class="'+rolesButton+'"><span>Roles </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>' \
'</div>'