epicyon/webapp_create_post.py

778 lines
34 KiB
Python

__filename__ = "webapp_create_post.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
__module_group__ = "Web Interface"
import os
from utils import isPublicPostFromUrl
from utils import getNicknameFromActor
from utils import getDomainFromActor
from utils import getImageFormats
from utils import getMediaFormats
from utils import getConfigParam
from utils import acctDir
from webapp_utils import getBannerFile
from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter
def _htmlFollowingDataList(baseDir: str, nickname: str,
domain: str, domainFull: str) -> str:
"""Returns a datalist of handles being followed
"""
listStr = '<datalist id="followingHandles">\n'
followingFilename = \
acctDir(baseDir, nickname, domain) + '/following.txt'
msg = None
if os.path.isfile(followingFilename):
with open(followingFilename, 'r') as followingFile:
msg = followingFile.read()
# add your own handle, so that you can send DMs
# to yourself as reminders
msg += nickname + '@' + domainFull + '\n'
if msg:
# include petnames
petnamesFilename = \
acctDir(baseDir, nickname, domain) + '/petnames.txt'
if os.path.isfile(petnamesFilename):
followingList = []
with open(petnamesFilename, 'r') as petnamesFile:
petStr = petnamesFile.read()
# extract each petname and append it
petnamesList = petStr.split('\n')
for pet in petnamesList:
followingList.append(pet.split(' ')[0])
# add the following.txt entries
followingList += msg.split('\n')
else:
# no petnames list exists - just use following.txt
followingList = msg.split('\n')
followingList.sort()
if followingList:
for followingAddress in followingList:
if followingAddress:
listStr += '<option>@' + followingAddress + '</option>\n'
listStr += '</datalist>\n'
return listStr
def _htmlNewPostDropDown(scopeIcon: str, scopeDescription: str,
replyStr: str,
translate: {},
showPublicOnDropdown: bool,
defaultTimeline: str,
pathBase: str,
dropdownNewPostSuffix: str,
dropdownNewBlogSuffix: str,
dropdownUnlistedSuffix: str,
dropdownFollowersSuffix: str,
dropdownDMSuffix: str,
dropdownReminderSuffix: str,
dropdownEventSuffix: str,
dropdownReportSuffix: str,
noDropDown: bool,
accessKeys: {}) -> str:
"""Returns the html for a drop down list of new post types
"""
dropDownContent = '<nav><div class="newPostDropdown">\n'
if not noDropDown:
dropDownContent += ' <input type="checkbox" ' + \
'id="my-newPostDropdown" value="" name="my-checkbox">\n'
dropDownContent += ' <label for="my-newPostDropdown"\n'
dropDownContent += ' data-toggle="newPostDropdown">\n'
dropDownContent += ' <img loading="lazy" alt="" title="" src="/' + \
'icons/' + scopeIcon + '"/><b>' + scopeDescription + '</b></label>\n'
if noDropDown:
dropDownContent += '</div></nav>\n'
return dropDownContent
dropDownContent += ' <ul>\n'
if showPublicOnDropdown:
dropDownContent += \
'<li><a href="' + pathBase + dropdownNewPostSuffix + \
'" accesskey="' + accessKeys['Public'] + '">' + \
'<img loading="lazy" alt="" title="" src="/' + \
'icons/scope_public.png"/><b>' + \
translate['Public'] + '</b><br>' + \
translate['Visible to anyone'] + '</a></li>\n'
if defaultTimeline == 'tlfeatures':
dropDownContent += \
'<li><a href="' + pathBase + dropdownNewBlogSuffix + \
'" accesskey="' + accessKeys['menuBlogs'] + '">' + \
'<img loading="lazy" alt="" title="" src="/' + \
'icons/scope_blog.png"/><b>' + \
translate['Article'] + '</b><br>' + \
translate['Create an article'] + '</a></li>\n'
else:
dropDownContent += \
'<li><a href="' + pathBase + dropdownNewBlogSuffix + \
'" accesskey="' + accessKeys['menuBlogs'] + '">' + \
'<img loading="lazy" alt="" title="" src="/' + \
'icons/scope_blog.png"/><b>' + \
translate['Blog'] + '</b><br>' + \
translate['Publicly visible post'] + '</a></li>\n'
dropDownContent += \
'<li><a href="' + pathBase + dropdownUnlistedSuffix + \
'"><img loading="lazy" alt="" title="" src="/' + \
'icons/scope_unlisted.png"/><b>' + \
translate['Unlisted'] + '</b><br>' + \
translate['Not on public timeline'] + '</a></li>\n'
dropDownContent += \
'<li><a href="' + pathBase + dropdownFollowersSuffix + \
'" accesskey="' + accessKeys['menuFollowers'] + '">' + \
'<img loading="lazy" alt="" title="" src="/' + \
'icons/scope_followers.png"/><b>' + \
translate['Followers'] + '</b><br>' + \
translate['Only to followers'] + '</a></li>\n'
dropDownContent += \
'<li><a href="' + pathBase + dropdownDMSuffix + \
'" accesskey="' + accessKeys['menuDM'] + '">' + \
'<img loading="lazy" alt="" title="" src="/' + \
'icons/scope_dm.png"/><b>' + \
translate['DM'] + '</b><br>' + \
translate['Only to mentioned people'] + '</a></li>\n'
dropDownContent += \
'<li><a href="' + pathBase + dropdownReminderSuffix + \
'" accesskey="' + accessKeys['Reminder'] + '">' + \
'<img loading="lazy" alt="" title="" src="/' + \
'icons/scope_reminder.png"/><b>' + \
translate['Reminder'] + '</b><br>' + \
translate['Scheduled note to yourself'] + '</a></li>\n'
dropDownContent += \
'<li><a href="' + pathBase + dropdownReportSuffix + \
'" accesskey="' + accessKeys['reportButton'] + '">' + \
'<img loading="lazy" alt="" title="" src="/' + \
'icons/scope_report.png"/><b>' + \
translate['Report'] + '</b><br>' + \
translate['Send to moderators'] + '</a></li>\n'
if not replyStr:
dropDownContent += \
'<li><a href="' + pathBase + \
'/newshare" accesskey="' + accessKeys['menuShares'] + '">' + \
'<img loading="lazy" alt="" title="" src="/' + \
'icons/scope_share.png"/><b>' + \
translate['Shares'] + '</b><br>' + \
translate['Describe a shared item'] + '</a></li>\n'
dropDownContent += \
'<li><a href="' + pathBase + \
'/newquestion"><img loading="lazy" alt="" title="" src="/' + \
'icons/scope_question.png"/><b>' + \
translate['Question'] + '</b><br>' + \
translate['Ask a question'] + '</a></li>\n'
dropDownContent += ' </ul>\n'
dropDownContent += '</div></nav>\n'
return dropDownContent
def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
baseDir: str, httpPrefix: str,
path: str, inReplyTo: str,
mentions: [],
shareDescription: str,
reportUrl: str, pageNumber: int,
nickname: str, domain: str,
domainFull: str,
defaultTimeline: str, newswire: {},
theme: str, noDropDown: bool,
accessKeys: {}, customSubmitText: str) -> str:
"""New post screen
"""
replyStr = ''
showPublicOnDropdown = True
messageBoxHeight = 400
# filename of the banner shown at the top
bannerFile, bannerFilename = \
getBannerFile(baseDir, nickname, domain, theme)
if not path.endswith('/newshare'):
if not path.endswith('/newreport'):
if not inReplyTo or path.endswith('/newreminder'):
newPostText = '<h1>' + \
translate['Write your post text below.'] + '</h1>\n'
else:
newPostText = \
'<p class="new-post-text">' + \
translate['Write your reply to'] + \
' <a href="' + inReplyTo + \
'" rel="nofollow noopener noreferrer" ' + \
'target="_blank">' + \
translate['this post'] + '</a></p>\n'
replyStr = '<input type="hidden" ' + \
'name="replyTo" value="' + inReplyTo + '">\n'
# if replying to a non-public post then also make
# this post non-public
if not isPublicPostFromUrl(baseDir, nickname, domain,
inReplyTo):
newPostPath = path
if '?' in newPostPath:
newPostPath = newPostPath.split('?')[0]
if newPostPath.endswith('/newpost'):
path = path.replace('/newpost', '/newfollowers')
elif newPostPath.endswith('/newunlisted'):
path = path.replace('/newunlisted', '/newfollowers')
showPublicOnDropdown = False
else:
newPostText = \
'<h1>' + translate['Write your report below.'] + '</h1>\n'
# custom report header with any additional instructions
if os.path.isfile(baseDir + '/accounts/report.txt'):
with open(baseDir + '/accounts/report.txt', 'r') as file:
customReportText = file.read()
if '</p>' not in customReportText:
customReportText = \
'<p class="login-subtext">' + \
customReportText + '</p>\n'
repStr = '<p class="login-subtext">'
customReportText = \
customReportText.replace('<p>', repStr)
newPostText += customReportText
idx = 'This message only goes to moderators, even if it ' + \
'mentions other fediverse addresses.'
newPostText += \
'<p class="new-post-subtext">' + translate[idx] + '</p>\n' + \
'<p class="new-post-subtext">' + translate['Also see'] + \
' <a href="/terms">' + \
translate['Terms of Service'] + '</a></p>\n'
else:
newPostText = \
'<h1>' + \
translate['Enter the details for your shared item below.'] + \
'</h1>\n'
if path.endswith('/newquestion'):
newPostText = \
'<h1>' + \
translate['Enter the choices for your question below.'] + \
'</h1>\n'
if os.path.isfile(baseDir + '/accounts/newpost.txt'):
with open(baseDir + '/accounts/newpost.txt', 'r') as file:
newPostText = \
'<p>' + file.read() + '</p>\n'
cssFilename = baseDir + '/epicyon-profile.css'
if os.path.isfile(baseDir + '/epicyon.css'):
cssFilename = baseDir + '/epicyon.css'
if '?' in path:
path = path.split('?')[0]
pathBase = path.replace('/newreport', '').replace('/newpost', '')
pathBase = pathBase.replace('/newblog', '').replace('/newshare', '')
pathBase = pathBase.replace('/newunlisted', '')
pathBase = pathBase.replace('/newevent', '')
pathBase = pathBase.replace('/newreminder', '')
pathBase = pathBase.replace('/newfollowers', '').replace('/newdm', '')
newPostImageSection = ' <div class="container">'
if not path.endswith('/newevent'):
newPostImageSection += \
' <label class="labels">' + \
translate['Image description'] + '</label>\n'
else:
newPostImageSection += \
' <label class="labels">' + \
translate['Event banner image description'] + '</label>\n'
newPostImageSection += \
' <input type="text" name="imageDescription">\n'
if path.endswith('/newevent'):
newPostImageSection += \
' <label class="labels">' + \
translate['Banner image'] + '</label>\n'
newPostImageSection += \
' <input type="file" id="attachpic" name="attachpic"'
newPostImageSection += \
' accept="' + getImageFormats() + '">\n'
else:
newPostImageSection += \
' <input type="file" id="attachpic" name="attachpic"'
newPostImageSection += \
' accept="' + getMediaFormats() + '">\n'
newPostImageSection += ' </div>\n'
scopeIcon = 'scope_public.png'
scopeDescription = translate['Public']
if shareDescription:
placeholderSubject = translate['Ask about a shared item.'] + '..'
else:
placeholderSubject = \
translate['Subject or Content Warning (optional)'] + '...'
placeholderMentions = ''
if inReplyTo:
placeholderMentions = \
translate['Replying to'] + '...'
placeholderMessage = translate['Write something'] + '...'
extraFields = ''
endpoint = 'newpost'
if path.endswith('/newblog'):
placeholderSubject = translate['Title']
scopeIcon = 'scope_blog.png'
if defaultTimeline != 'tlfeatures':
scopeDescription = translate['Blog']
else:
scopeDescription = translate['Article']
endpoint = 'newblog'
elif path.endswith('/newunlisted'):
scopeIcon = 'scope_unlisted.png'
scopeDescription = translate['Unlisted']
endpoint = 'newunlisted'
elif path.endswith('/newfollowers'):
scopeIcon = 'scope_followers.png'
scopeDescription = translate['Followers']
endpoint = 'newfollowers'
elif path.endswith('/newdm'):
scopeIcon = 'scope_dm.png'
scopeDescription = translate['DM']
endpoint = 'newdm'
elif path.endswith('/newreminder'):
scopeIcon = 'scope_reminder.png'
scopeDescription = translate['Reminder']
endpoint = 'newreminder'
elif path.endswith('/newevent'):
scopeIcon = 'scope_event.png'
scopeDescription = translate['Event']
endpoint = 'newevent'
placeholderSubject = translate['Event name']
placeholderMessage = translate['Describe the event'] + '...'
elif path.endswith('/newreport'):
scopeIcon = 'scope_report.png'
scopeDescription = translate['Report']
endpoint = 'newreport'
elif path.endswith('/newquestion'):
scopeIcon = 'scope_question.png'
scopeDescription = translate['Question']
placeholderMessage = translate['Enter your question'] + '...'
endpoint = 'newquestion'
extraFields = '<div class="container">\n'
extraFields += ' <label class="labels">' + \
translate['Possible answers'] + ':</label><br>\n'
for questionCtr in range(8):
extraFields += \
' <input type="text" class="questionOption" placeholder="' + \
str(questionCtr + 1) + \
'" name="questionOption' + str(questionCtr) + '"><br>\n'
extraFields += \
' <label class="labels">' + \
translate['Duration of listing in days'] + \
':</label> <input type="number" name="duration" ' + \
'min="1" max="365" step="1" value="14"><br>\n'
extraFields += '</div>'
elif path.endswith('/newshare'):
scopeIcon = 'scope_share.png'
scopeDescription = translate['Shared Item']
placeholderSubject = translate['Name of the shared item'] + '...'
placeholderMessage = \
translate['Description of the item being shared'] + '...'
endpoint = 'newshare'
extraFields = '<div class="container">\n'
extraFields += \
' <label class="labels">' + \
translate['Type of shared item. eg. hat'] + ':</label>\n'
extraFields += \
' <input type="text" class="itemType" name="itemType">\n'
extraFields += \
' <br><label class="labels">' + \
translate['Category of shared item. eg. clothing'] + ':</label>\n'
extraFields += \
' <input type="text" class="category" name="category">\n'
extraFields += \
' <br><label class="labels">' + \
translate['Duration of listing in days'] + ':</label>\n'
extraFields += ' <input type="number" name="duration" ' + \
'min="1" max="365" step="1" value="14">\n'
extraFields += '</div>\n'
extraFields += '<div class="container">\n'
extraFields += \
'<label class="labels">' + \
translate['City or location of the shared item'] + ':</label>\n'
extraFields += '<input type="text" name="location">\n'
extraFields += '</div>\n'
citationsStr = ''
if endpoint == 'newblog':
citationsFilename = \
acctDir(baseDir, nickname, domain) + '/.citations.txt'
if os.path.isfile(citationsFilename):
citationsStr = '<div class="container">\n'
citationsStr += '<p><label class="labels">' + \
translate['Citations'] + ':</label></p>\n'
citationsStr += ' <ul>\n'
citationsSeparator = '#####'
with open(citationsFilename, 'r') as f:
citations = f.readlines()
for line in citations:
if citationsSeparator not in line:
continue
sections = line.strip().split(citationsSeparator)
if len(sections) != 3:
continue
title = sections[1]
link = sections[2]
citationsStr += \
' <li><a href="' + link + '"><cite>' + \
title + '</cite></a></li>'
citationsStr += ' </ul>\n'
citationsStr += '</div>\n'
dateAndLocation = ''
if endpoint != 'newshare' and \
endpoint != 'newreport' and \
endpoint != 'newquestion':
dateAndLocation = '<div class="container">\n'
if endpoint == 'newevent':
# event status
dateAndLocation += '<label class="labels">' + \
translate['Status of the event'] + ':</label><br>\n'
dateAndLocation += '<input type="radio" id="tentative" ' + \
'name="eventStatus" value="tentative">\n'
dateAndLocation += '<label class="labels" for="tentative">' + \
translate['Tentative'] + '</label><br>\n'
dateAndLocation += '<input type="radio" id="confirmed" ' + \
'name="eventStatus" value="confirmed" checked>\n'
dateAndLocation += '<label class="labels" for="confirmed">' + \
translate['Confirmed'] + '</label><br>\n'
dateAndLocation += '<input type="radio" id="cancelled" ' + \
'name="eventStatus" value="cancelled">\n'
dateAndLocation += '<label class="labels" for="cancelled">' + \
translate['Cancelled'] + '</label><br>\n'
dateAndLocation += '</div>\n'
dateAndLocation += '<div class="container">\n'
# maximum attendees
dateAndLocation += '<label class="labels" ' + \
'for="maximumAttendeeCapacity">' + \
translate['Maximum attendees'] + ':</label>\n'
dateAndLocation += '<input type="number" ' + \
'id="maximumAttendeeCapacity" ' + \
'name="maximumAttendeeCapacity" min="1" max="999999" ' + \
'value="100">\n'
dateAndLocation += '</div>\n'
dateAndLocation += '<div class="container">\n'
# event joining options
dateAndLocation += '<label class="labels">' + \
translate['Joining'] + ':</label><br>\n'
dateAndLocation += '<input type="radio" id="free" ' + \
'name="joinMode" value="free" checked>\n'
dateAndLocation += '<label class="labels" for="free">' + \
translate['Anyone can join'] + '</label><br>\n'
dateAndLocation += '<input type="radio" id="restricted" ' + \
'name="joinMode" value="restricted">\n'
dateAndLocation += '<label class="labels" for="female">' + \
translate['Apply to join'] + '</label><br>\n'
dateAndLocation += '<input type="radio" id="invite" ' + \
'name="joinMode" value="invite">\n'
dateAndLocation += '<label class="labels" for="other">' + \
translate['Invitation only'] + '</label>\n'
dateAndLocation += '</div>\n'
dateAndLocation += '<div class="container">\n'
# Event posts don't allow replies - they're just an announcement.
# They also have a few more checkboxes
dateAndLocation += \
'<p><input type="checkbox" class="profilecheckbox" ' + \
'name="privateEvent"><label class="labels"> ' + \
translate['This is a private event.'] + '</label></p>\n'
dateAndLocation += \
'<p><input type="checkbox" class="profilecheckbox" ' + \
'name="anonymousParticipationEnabled">' + \
'<label class="labels"> ' + \
translate['Allow anonymous participation.'] + '</label></p>\n'
else:
dateAndLocation += \
'<p><input type="checkbox" class="profilecheckbox" ' + \
'name="commentsEnabled" checked><label class="labels"> ' + \
translate['Allow replies.'] + '</label></p>\n'
if endpoint == 'newpost':
dateAndLocation += \
'<p><input type="checkbox" class="profilecheckbox" ' + \
'name="pinToProfile"><label class="labels"> ' + \
translate['Pin this post to your profile.'] + '</label></p>\n'
if not inReplyTo and endpoint != 'newevent':
dateAndLocation += \
'<p><input type="checkbox" class="profilecheckbox" ' + \
'name="schedulePost"><label class="labels"> ' + \
translate['This is a scheduled post.'] + '</label></p>\n'
if endpoint != 'newevent':
dateAndLocation += \
'<p><img loading="lazy" alt="" title="" ' + \
'class="emojicalendar" src="/' + \
'icons/calendar.png"/>\n'
# select a date and time for this post
dateAndLocation += '<label class="labels">' + \
translate['Date'] + ': </label>\n'
dateAndLocation += '<input type="date" name="eventDate">\n'
dateAndLocation += '<label class="labelsright">' + \
translate['Time'] + ':'
dateAndLocation += \
'<input type="time" name="eventTime"></label></p>\n'
else:
dateAndLocation += '</div>\n'
dateAndLocation += '<div class="container">\n'
dateAndLocation += \
'<p><img loading="lazy" alt="" title="" ' + \
'class="emojicalendar" src="/' + \
'icons/calendar.png"/>\n'
# select start time for the event
dateAndLocation += '<label class="labels">' + \
translate['Start Date'] + ': </label>\n'
dateAndLocation += '<input type="date" name="eventDate">\n'
dateAndLocation += '<label class="labelsright">' + \
translate['Time'] + ':'
dateAndLocation += \
'<input type="time" name="eventTime"></label></p>\n'
# select end time for the event
dateAndLocation += \
'<br><img loading="lazy" alt="" title="" ' + \
'class="emojicalendar" src="/' + \
'icons/calendar.png"/>\n'
dateAndLocation += '<label class="labels">' + \
translate['End Date'] + ': </label>\n'
dateAndLocation += '<input type="date" name="endDate">\n'
dateAndLocation += '<label class="labelsright">' + \
translate['Time'] + ':'
dateAndLocation += \
'<input type="time" name="endTime"></label>\n'
if endpoint == 'newevent':
dateAndLocation += '</div>\n'
dateAndLocation += '<div class="container">\n'
dateAndLocation += '<br><label class="labels">' + \
translate['Moderation policy or code of conduct'] + \
': </label>\n'
dateAndLocation += \
' <textarea id="message" ' + \
'name="repliesModerationOption" style="height:' + \
str(messageBoxHeight) + 'px" spellcheck="true" ' + \
'autocomplete="on"></textarea>\n'
dateAndLocation += '</div>\n'
dateAndLocation += '<div class="container">\n'
dateAndLocation += '<label class="labels">' + \
translate['Location'] + ': </label>\n'
dateAndLocation += '<input type="text" name="location">\n'
if endpoint == 'newevent':
dateAndLocation += '<br><label class="labels">' + \
translate['Ticket URL'] + ': </label>\n'
dateAndLocation += '<input type="text" name="ticketUrl">\n'
dateAndLocation += '<br><label class="labels">' + \
translate['Categories'] + ': </label>\n'
dateAndLocation += '<input type="text" name="category">\n'
dateAndLocation += '</div>\n'
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
newPostForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
newPostForm += \
'<header>\n' + \
'<a href="/users/' + nickname + '/' + defaultTimeline + '" title="' + \
translate['Switch to timeline view'] + '" alt="' + \
translate['Switch to timeline view'] + '" ' + \
'accesskey="' + accessKeys['menuTimeline'] + '">\n'
newPostForm += '<img loading="lazy" class="timeline-banner" src="' + \
'/users/' + nickname + '/' + bannerFile + '" alt="" /></a>\n' + \
'</header>\n'
mentionsStr = ''
for m in mentions:
mentionNickname = getNicknameFromActor(m)
if not mentionNickname:
continue
mentionDomain, mentionPort = getDomainFromActor(m)
if not mentionDomain:
continue
if mentionPort:
mentionsHandle = \
'@' + mentionNickname + '@' + \
mentionDomain + ':' + str(mentionPort)
else:
mentionsHandle = '@' + mentionNickname + '@' + mentionDomain
if mentionsHandle not in mentionsStr:
mentionsStr += mentionsHandle + ' '
# build suffixes so that any replies or mentions are
# preserved when switching between scopes
dropdownNewPostSuffix = '/newpost'
dropdownNewBlogSuffix = '/newblog'
dropdownUnlistedSuffix = '/newunlisted'
dropdownFollowersSuffix = '/newfollowers'
dropdownDMSuffix = '/newdm'
dropdownEventSuffix = '/newevent'
dropdownReminderSuffix = '/newreminder'
dropdownReportSuffix = '/newreport'
if inReplyTo or mentions:
dropdownNewPostSuffix = ''
dropdownNewBlogSuffix = ''
dropdownUnlistedSuffix = ''
dropdownFollowersSuffix = ''
dropdownDMSuffix = ''
dropdownEventSuffix = ''
dropdownReminderSuffix = ''
dropdownReportSuffix = ''
if inReplyTo:
dropdownNewPostSuffix += '?replyto=' + inReplyTo
dropdownNewBlogSuffix += '?replyto=' + inReplyTo
dropdownUnlistedSuffix += '?replyto=' + inReplyTo
dropdownFollowersSuffix += '?replyfollowers=' + inReplyTo
dropdownDMSuffix += '?replydm=' + inReplyTo
for mentionedActor in mentions:
dropdownNewPostSuffix += '?mention=' + mentionedActor
dropdownNewBlogSuffix += '?mention=' + mentionedActor
dropdownUnlistedSuffix += '?mention=' + mentionedActor
dropdownFollowersSuffix += '?mention=' + mentionedActor
dropdownDMSuffix += '?mention=' + mentionedActor
dropdownReportSuffix += '?mention=' + mentionedActor
dropDownContent = ''
if not reportUrl and not shareDescription:
dropDownContent = \
_htmlNewPostDropDown(scopeIcon, scopeDescription,
replyStr,
translate,
showPublicOnDropdown,
defaultTimeline,
pathBase,
dropdownNewPostSuffix,
dropdownNewBlogSuffix,
dropdownUnlistedSuffix,
dropdownFollowersSuffix,
dropdownDMSuffix,
dropdownReminderSuffix,
dropdownEventSuffix,
dropdownReportSuffix,
noDropDown, accessKeys)
else:
if not shareDescription:
# reporting a post to moderator
mentionsStr = 'Re: ' + reportUrl + '\n\n' + mentionsStr
newPostForm += \
'<form enctype="multipart/form-data" method="POST" ' + \
'accept-charset="UTF-8" action="' + \
path + '?' + endpoint + '?page=' + str(pageNumber) + '">\n'
newPostForm += ' <div class="vertical-center">\n'
newPostForm += \
' <label for="nickname"><b>' + newPostText + '</b></label>\n'
newPostForm += ' <div class="containerNewPost">\n'
newPostForm += ' <table style="width:100%" border="0">\n'
newPostForm += ' <colgroup>\n'
newPostForm += ' <col span="1" style="width:70%">\n'
newPostForm += ' <col span="1" style="width:10%">\n'
if newswire and path.endswith('/newblog'):
newPostForm += ' <col span="1" style="width:10%">\n'
newPostForm += ' <col span="1" style="width:10%">\n'
else:
newPostForm += ' <col span="1" style="width:20%">\n'
newPostForm += ' </colgroup>\n'
newPostForm += '<tr>\n'
newPostForm += '<td>' + dropDownContent + '</td>\n'
newPostForm += \
' <td><a href="' + pathBase + \
'/searchemoji"><img loading="lazy" class="emojisearch" ' + \
'src="/emoji/1F601.png" title="' + \
translate['Search for emoji'] + '" alt="' + \
translate['Search for emoji'] + '"/></a></td>\n'
# for a new blog if newswire items exist then add a citations button
if newswire and path.endswith('/newblog'):
newPostForm += \
' <td><input type="submit" name="submitCitations" value="' + \
translate['Citations'] + '"></td>\n'
submitText = translate['Submit']
if customSubmitText:
submitText = customSubmitText
newPostForm += \
' <td><input type="submit" name="submitPost" value="' + \
submitText + '" ' + \
'accesskey="' + accessKeys['submitButton'] + '"></td>\n'
newPostForm += ' </tr>\n</table>\n'
newPostForm += ' </div>\n'
newPostForm += ' <div class="containerSubmitNewPost"><center>\n'
newPostForm += ' </center></div>\n'
newPostForm += replyStr
if mediaInstance and not replyStr:
newPostForm += newPostImageSection
newPostForm += \
' <label class="labels">' + placeholderSubject + '</label><br>'
if not shareDescription:
shareDescription = ''
newPostForm += \
' <input type="text" name="subject" value="' + \
shareDescription + '">'
newPostForm += ''
selectedStr = ' selected'
if inReplyTo or endpoint == 'newdm':
if inReplyTo:
newPostForm += \
' <label class="labels">' + placeholderMentions + \
'</label><br>\n'
else:
newPostForm += \
' <a href="/users/' + nickname + \
'/followingaccounts" title="' + \
translate['Show a list of addresses to send to'] + '">' \
'<label class="labels">' + \
translate['Send to'] + ':' + '</label> 📄</a><br>\n'
newPostForm += \
' <input type="text" name="mentions" ' + \
'list="followingHandles" value="' + mentionsStr + '" selected>\n'
newPostForm += \
_htmlFollowingDataList(baseDir, nickname, domain, domainFull)
newPostForm += ''
selectedStr = ''
newPostForm += \
' <br><label class="labels">' + placeholderMessage + '</label>'
if mediaInstance:
messageBoxHeight = 200
if endpoint == 'newquestion':
messageBoxHeight = 100
elif endpoint == 'newblog':
messageBoxHeight = 800
newPostForm += \
' <textarea id="message" name="message" style="height:' + \
str(messageBoxHeight) + 'px"' + selectedStr + \
' spellcheck="true" autocomplete="on">' + \
'</textarea>\n'
newPostForm += extraFields + citationsStr + dateAndLocation
if not mediaInstance or replyStr:
newPostForm += newPostImageSection
newPostForm += \
' <div class="container">\n' + \
' <input type="submit" name="submitPost" value="' + \
submitText + '">\n' + \
' </div>\n' + \
' </div>\n' + \
'</form>\n'
if not reportUrl:
newPostForm = \
newPostForm.replace('<body>', '<body onload="focusOnMessage()">')
newPostForm += htmlFooter()
return newPostForm