__filename__ = "webapp_create_post.py" __author__ = "Bob Mottram" __license__ = "AGPL3+" __version__ = "1.2.0" __maintainer__ = "Bob Mottram" __email__ = "bob@libreserver.org" __status__ = "Production" __module_group__ = "Web Interface" import os from utils import getNewPostEndpoints from utils import isPublicPostFromUrl from utils import getNicknameFromActor from utils import getDomainFromActor from utils import getMediaFormats from utils import getConfigParam from utils import acctDir from utils import getCurrencies from utils import getCategoryTypes from webapp_utils import getBannerFile from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlFooter from webapp_utils import editTextField from webapp_utils import editNumberField from webapp_utils import editCurrencyField from webapp_post import individualPostAsHtml def _htmlFollowingDataList(base_dir: str, nickname: str, domain: str, domain_full: str) -> str: """Returns a datalist of handles being followed """ listStr = '\n' followingFilename = \ acctDir(base_dir, 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 + '@' + domain_full + '\n' if msg: # include petnames petnamesFilename = \ acctDir(base_dir, 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 += '\n' listStr += '\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, dropdownReportSuffix: str, noDropDown: bool, accessKeys: {}) -> str: """Returns the html for a drop down list of new post types """ dropDownContent = '\n' return dropDownContent dropDownContent += ' \n' dropDownContent += '\n' return dropDownContent def htmlNewPost(cssCache: {}, media_instance: bool, translate: {}, base_dir: str, http_prefix: str, path: str, inReplyTo: str, mentions: [], shareDescription: str, reportUrl: str, pageNumber: int, category: str, nickname: str, domain: str, domain_full: str, defaultTimeline: str, newswire: {}, theme: str, noDropDown: bool, accessKeys: {}, customSubmitText: str, conversationId: str, recentPostsCache: {}, max_recent_posts: int, session, cached_webfingers: {}, person_cache: {}, port: int, post_json_object: {}, project_version: str, yt_replace_domain: str, twitter_replacement_domain: str, show_published_date_only: bool, peertube_instances: [], allow_local_network_access: bool, system_language: str, max_like_count: int, signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, boxName: str) -> str: """New post screen """ replyStr = '' isNewReminder = False if path.endswith('/newreminder'): isNewReminder = True # the date and time dateAndTimeStr = '

\n' if not isNewReminder: dateAndTimeStr += \ '\n' # select a date and time for this post dateAndTimeStr += '\n' dateAndTimeStr += '\n' dateAndTimeStr += '\n

\n' showPublicOnDropdown = True messageBoxHeight = 400 # filename of the banner shown at the top bannerFile, bannerFilename = \ getBannerFile(base_dir, nickname, domain, theme) if not path.endswith('/newshare') and not path.endswith('/newwanted'): if not path.endswith('/newreport'): if not inReplyTo or isNewReminder: newPostText = '

' + \ translate['Write your post text below.'] + '

\n' else: newPostText = '' if category != 'accommodation': newPostText = \ '

' + \ translate['Write your reply to'] + \ ' ' + \ translate['this post'] + '

\n' if post_json_object: newPostText += \ individualPostAsHtml(signing_priv_key_pem, True, recentPostsCache, max_recent_posts, translate, None, base_dir, session, cached_webfingers, person_cache, nickname, domain, port, post_json_object, None, True, False, http_prefix, project_version, boxName, yt_replace_domain, twitter_replacement_domain, show_published_date_only, peertube_instances, allow_local_network_access, theme, system_language, max_like_count, False, False, False, False, False, False, cw_lists, lists_enabled) replyStr = '\n' # if replying to a non-public post then also make # this post non-public if not isPublicPostFromUrl(base_dir, 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 = \ '

' + translate['Write your report below.'] + '

\n' # custom report header with any additional instructions if os.path.isfile(base_dir + '/accounts/report.txt'): with open(base_dir + '/accounts/report.txt', 'r') as file: customReportText = file.read() if '

' not in customReportText: customReportText = \ '

' + \ customReportText + '

\n' repStr = '

' customReportText = \ customReportText.replace('

', repStr) newPostText += customReportText idx = 'This message only goes to moderators, even if it ' + \ 'mentions other fediverse addresses.' newPostText += \ '

' + translate[idx] + '

\n' + \ '

' + translate['Also see'] + \ ' ' + \ translate['Terms of Service'] + '

\n' else: if path.endswith('/newshare'): newPostText = \ '

' + \ translate['Enter the details for your shared item below.'] + \ '

\n' else: newPostText = \ '

' + \ translate['Enter the details for your wanted item below.'] + \ '

\n' if path.endswith('/newquestion'): newPostText = \ '

' + \ translate['Enter the choices for your question below.'] + \ '

\n' if os.path.isfile(base_dir + '/accounts/newpost.txt'): with open(base_dir + '/accounts/newpost.txt', 'r') as file: newPostText = \ '

' + file.read() + '

\n' cssFilename = base_dir + '/epicyon-profile.css' if os.path.isfile(base_dir + '/epicyon.css'): cssFilename = base_dir + '/epicyon.css' if '?' in path: path = path.split('?')[0] newPostEndpoints = getNewPostEndpoints() pathBase = path for currPostType in newPostEndpoints: pathBase = pathBase.replace('/' + currPostType, '') newPostImageSection = '
\n' newPostImageSection += \ editTextField(translate['Image description'], 'imageDescription', '') newPostImageSection += \ ' \n' newPostImageSection += '
\n' scopeIcon = 'scope_public.png' scopeDescription = translate['Public'] if shareDescription: if category == 'accommodation': placeholderSubject = translate['Request to stay'] else: placeholderSubject = translate['Ask about a shared item.'] + '..' else: placeholderSubject = \ translate['Subject or Content Warning (optional)'] + '...' placeholderMentions = '' if inReplyTo: placeholderMentions = \ translate['Replying to'] + '...' placeholderMessage = '' if category != 'accommodation': placeholderMessage = translate['Write something'] + '...' else: idx = 'Introduce yourself and specify the date ' + \ 'and time when you wish to stay' placeholderMessage = translate[idx] 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 isNewReminder: scopeIcon = 'scope_reminder.png' scopeDescription = translate['Reminder'] endpoint = 'newreminder' 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 = '
\n' extraFields += '
\n' for questionCtr in range(8): extraFields += \ '
\n' extraFields += \ '
\n' extraFields += '
' 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 = '
\n' extraFields += \ editNumberField(translate['Quantity'], 'itemQty', 1, 1, 999999, 1) extraFields += '
' + \ editTextField(translate['Type of shared item. eg. hat'] + ':', 'itemType', '', '', True) categoryTypes = getCategoryTypes(base_dir) catStr = translate['Category of shared item. eg. clothing'] extraFields += '
\n' extraFields += '
\n' extraFields += \ editNumberField(translate['Duration of listing in days'], 'duration', 14, 1, 365, 1) extraFields += '
\n' extraFields += '
\n' cityOrLocStr = translate['City or location of the shared item'] extraFields += editTextField(cityOrLocStr + ':', 'location', '') extraFields += '
\n' extraFields += '
\n' extraFields += \ editCurrencyField(translate['Price'] + ':', 'itemPrice', '0.00', '0.00', True) extraFields += '
' extraFields += \ '
\n' currencies = getCurrencies() extraFields += ' \n' extraFields += '
\n' elif path.endswith('/newwanted'): scopeIcon = 'scope_wanted.png' scopeDescription = translate['Wanted'] placeholderSubject = translate['Name of the wanted item'] + '...' placeholderMessage = \ translate['Description of the item wanted'] + '...' endpoint = 'newwanted' extraFields = '
\n' extraFields += \ editNumberField(translate['Quantity'], 'itemQty', 1, 1, 999999, 1) extraFields += '
' + \ editTextField(translate['Type of wanted item. eg. hat'] + ':', 'itemType', '', '', True) categoryTypes = getCategoryTypes(base_dir) catStr = translate['Category of wanted item. eg. clothes'] extraFields += '
\n' extraFields += '
\n' extraFields += \ editNumberField(translate['Duration of listing in days'], 'duration', 14, 1, 365, 1) extraFields += '
\n' extraFields += '
\n' cityOrLocStr = translate['City or location of the wanted item'] extraFields += editTextField(cityOrLocStr + ':', 'location', '') extraFields += '
\n' extraFields += '
\n' extraFields += \ editCurrencyField(translate['Maximum Price'] + ':', 'itemPrice', '0.00', '0.00', True) extraFields += '
' extraFields += \ '
\n' currencies = getCurrencies() extraFields += ' \n' extraFields += '
\n' citationsStr = '' if endpoint == 'newblog': citationsFilename = \ acctDir(base_dir, nickname, domain) + '/.citations.txt' if os.path.isfile(citationsFilename): citationsStr = '
\n' citationsStr += '

\n' citationsStr += ' \n' citationsStr += '
\n' dateAndLocation = '' if endpoint != 'newshare' and \ endpoint != 'newwanted' and \ endpoint != 'newreport' and \ endpoint != 'newquestion': if not isNewReminder: dateAndLocation = \ '
\n' if category != 'accommodation': dateAndLocation += \ '

\n' else: dateAndLocation += \ '\n' if endpoint == 'newpost': dateAndLocation += \ '

\n' if not inReplyTo: dateAndLocation += \ '

\n' dateAndLocation += dateAndTimeStr dateAndLocation += '
\n' dateAndLocation += '
\n' dateAndLocation += \ editTextField(translate['Location'], 'location', '') dateAndLocation += '
\n' instanceTitle = getConfigParam(base_dir, 'instanceTitle') newPostForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle, None) newPostForm += \ '
\n' + \ '\n' newPostForm += '\n' + \ '
\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' dropdownReminderSuffix = '/newreminder' dropdownReportSuffix = '/newreport' if inReplyTo or mentions: dropdownNewPostSuffix = '' dropdownNewBlogSuffix = '' dropdownUnlistedSuffix = '' dropdownFollowersSuffix = '' dropdownDMSuffix = '' 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 if conversationId and inReplyTo: dropdownNewPostSuffix += '?conversationId=' + conversationId dropdownNewBlogSuffix += '?conversationId=' + conversationId dropdownUnlistedSuffix += '?conversationId=' + conversationId dropdownFollowersSuffix += '?conversationId=' + conversationId dropdownDMSuffix += '?conversationId=' + conversationId dropDownContent = '' if not reportUrl and not shareDescription: dropDownContent = \ _htmlNewPostDropDown(scopeIcon, scopeDescription, replyStr, translate, showPublicOnDropdown, defaultTimeline, pathBase, dropdownNewPostSuffix, dropdownNewBlogSuffix, dropdownUnlistedSuffix, dropdownFollowersSuffix, dropdownDMSuffix, dropdownReminderSuffix, dropdownReportSuffix, noDropDown, accessKeys) else: if not shareDescription: # reporting a post to moderator mentionsStr = 'Re: ' + reportUrl + '\n\n' + mentionsStr newPostForm += \ '
\n' if conversationId: newPostForm += \ ' \n' newPostForm += '
\n' newPostForm += \ ' \n' newPostForm += '
\n' newPostForm += ' \n' newPostForm += ' \n' newPostForm += ' \n' newPostForm += ' \n' if newswire and path.endswith('/newblog'): newPostForm += ' \n' newPostForm += ' \n' else: newPostForm += ' \n' newPostForm += ' \n' newPostForm += '\n' newPostForm += '\n' newPostForm += \ ' \n' # for a new blog if newswire items exist then add a citations button if newswire and path.endswith('/newblog'): newPostForm += \ ' \n' submitText = translate['Submit'] if customSubmitText: submitText = customSubmitText newPostForm += \ ' \n' newPostForm += ' \n
' + dropDownContent + '' + \
        translate['Search for emoji'] + '
\n' newPostForm += '
\n' newPostForm += '
\n' newPostForm += '
\n' newPostForm += replyStr if media_instance and not replyStr: newPostForm += newPostImageSection if not shareDescription: shareDescription = '' # for reminders show the date and time at the top if isNewReminder: newPostForm += '
\n' newPostForm += dateAndTimeStr newPostForm += '
\n' newPostForm += \ editTextField(placeholderSubject, 'subject', shareDescription) newPostForm += '' selectedStr = ' selected' if inReplyTo or endpoint == 'newdm': if inReplyTo: newPostForm += \ '
\n' else: newPostForm += \ ' ' \ ' 📄
\n' newPostForm += \ ' \n' newPostForm += \ _htmlFollowingDataList(base_dir, nickname, domain, domain_full) newPostForm += '' selectedStr = '' newPostForm += \ '
' if media_instance: messageBoxHeight = 200 if endpoint == 'newquestion': messageBoxHeight = 100 elif endpoint == 'newblog': messageBoxHeight = 800 newPostForm += \ ' \n' newPostForm += extraFields + citationsStr + dateAndLocation if not media_instance or replyStr: newPostForm += newPostImageSection newPostForm += \ '
\n' + \ ' \n' + \ '
\n' + \ '
\n' + \ '
\n' if not reportUrl: newPostForm = \ newPostForm.replace('', '') newPostForm += htmlFooter() return newPostForm