forked from indymedia/epicyon
Selecting profile images
parent
f5da898007
commit
52a4d17512
122
daemon.py
122
daemon.py
|
@ -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','')
|
||||
|
|
|
@ -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>'
|
||||
|
||||
|
|
Loading…
Reference in New Issue