diff --git a/blog.py b/blog.py index 2f0d4e70a..9963ae0ea 100644 --- a/blog.py +++ b/blog.py @@ -11,10 +11,10 @@ from datetime import datetime from content import replaceEmojiFromTags from webapp import getIconsDir -from webapp import getPostAttachmentsAsHtml from webapp import htmlHeader from webapp import htmlFooter -from webapp import addEmbeddedElements +from webapp_media import addEmbeddedElements +from webapp_utils import getPostAttachmentsAsHtml from utils import getNicknameFromActor from utils import getDomainFromActor from utils import locatePost diff --git a/daemon.py b/daemon.py index 967aac905..f9c85e10d 100644 --- a/daemon.py +++ b/daemon.py @@ -143,11 +143,8 @@ from webapp import htmlGetLoginCredentials from webapp import htmlNewPost from webapp import htmlFollowConfirm from webapp import htmlCalendar -from webapp import htmlSearch from webapp import htmlNewswireMobile from webapp import htmlLinksMobile -from webapp import htmlSearchEmoji -from webapp import htmlSearchEmojiTextEntry from webapp import htmlUnfollowConfirm from webapp import htmlProfileAfterSearch from webapp import htmlEditProfile @@ -155,13 +152,16 @@ from webapp import htmlEditLinks from webapp import htmlEditNewswire from webapp import htmlEditNewsPost from webapp import htmlTermsOfService -from webapp import htmlSkillsSearch -from webapp import htmlHistorySearch -from webapp import htmlHashtagSearch -from webapp import rssHashtagSearch from webapp import htmlModerationInfo -from webapp import htmlSearchSharedItems from webapp import htmlHashtagBlocked +from webapp_search import htmlSkillsSearch +from webapp_search import htmlHistorySearch +from webapp_search import htmlHashtagSearch +from webapp_search import rssHashtagSearch +from webapp_search import htmlSearchEmoji +from webapp_search import htmlSearchSharedItems +from webapp_search import htmlSearchEmojiTextEntry +from webapp_search import htmlSearch from shares import getSharesFeedForPerson from shares import addShare from shares import removeShare diff --git a/delete.py b/delete.py index 751b95aa0..3b195db7e 100644 --- a/delete.py +++ b/delete.py @@ -6,6 +6,8 @@ __maintainer__ = "Bob Mottram" __email__ = "bob@freedombone.net" __status__ = "Production" +import os +from datetime import datetime from utils import removeIdEnding from utils import getStatusNumber from utils import urlPermitted @@ -295,3 +297,33 @@ def outboxDelete(baseDir: str, httpPrefix: str, postFilename, debug, recentPostsCache) if debug: print('DEBUG: post deleted via c2s - ' + postFilename) + + +def removeOldHashtags(baseDir: str, maxMonths: int) -> str: + """Remove old hashtags + """ + if maxMonths > 11: + maxMonths = 11 + maxDaysSinceEpoch = \ + (datetime.utcnow() - datetime(1970, 1 + maxMonths, 1)).days + removeHashtags = [] + + for subdir, dirs, files in os.walk(baseDir + '/tags'): + for f in files: + tagsFilename = os.path.join(baseDir + '/tags', f) + if not os.path.isfile(tagsFilename): + continue + # get last modified datetime + modTimesinceEpoc = os.path.getmtime(tagsFilename) + lastModifiedDate = datetime.fromtimestamp(modTimesinceEpoc) + fileDaysSinceEpoch = (lastModifiedDate - datetime(1970, 1, 1)).days + + # check of the file is too old + if fileDaysSinceEpoch < maxDaysSinceEpoch: + removeHashtags.append(tagsFilename) + + for removeFilename in removeHashtags: + try: + os.remove(removeFilename) + except BaseException: + pass diff --git a/inbox.py b/inbox.py index 4916af2fa..5deb64b2a 100644 --- a/inbox.py +++ b/inbox.py @@ -58,7 +58,6 @@ from posts import sendSignedJson from posts import sendToFollowersThread from webapp import individualPostAsHtml from webapp import getIconsDir -from webapp import removeOldHashtags from question import questionUpdateVotes from media import replaceYouTube from git import isGitPatch @@ -66,6 +65,7 @@ from git import receiveGitPatch from followingCalendar import receivingCalendarEvents from content import dangerousMarkup from happening import saveEventPost +from delete import removeOldHashtags def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None: diff --git a/posts.py b/posts.py index ec59bc6db..5e16b369b 100644 --- a/posts.py +++ b/posts.py @@ -3977,3 +3977,27 @@ def sendUndoBlockViaServer(baseDir: str, session, print('DEBUG: c2s POST block success') return newBlockJson + + +def postIsMuted(baseDir: str, nickname: str, domain: str, + postJsonObject: {}, messageId: str) -> bool: + """ Returns true if the given post is muted + """ + isMuted = postJsonObject.get('muted') + if isMuted is True or isMuted is False: + return isMuted + postDir = baseDir + '/accounts/' + nickname + '@' + domain + muteFilename = \ + postDir + '/inbox/' + messageId.replace('/', '#') + '.json.muted' + if os.path.isfile(muteFilename): + return True + muteFilename = \ + postDir + '/outbox/' + messageId.replace('/', '#') + '.json.muted' + if os.path.isfile(muteFilename): + return True + muteFilename = \ + baseDir + '/accounts/cache/announce/' + nickname + \ + '/' + messageId.replace('/', '#') + '.json.muted' + if os.path.isfile(muteFilename): + return True + return False diff --git a/utils.py b/utils.py index b653d280c..23de5d1cf 100644 --- a/utils.py +++ b/utils.py @@ -1494,3 +1494,11 @@ def siteIsActive(url: str) -> bool: if e.errno == errno.ECONNRESET: print('WARN: connection was reset during siteIsActive') return False + + +def weekDayOfMonthStart(monthNumber: int, year: int) -> int: + """Gets the day number of the first day of the month + 1=sun, 7=sat + """ + firstDayOfMonth = datetime(year, monthNumber, 1, 0, 0) + return int(firstDayOfMonth.strftime("%w")) + 1 diff --git a/webapp.py b/webapp.py index dbfff8c99..b810d8fe9 100644 --- a/webapp.py +++ b/webapp.py @@ -8,10 +8,8 @@ __status__ = "Production" import time import os -import urllib.parse from datetime import datetime from datetime import date -from dateutil.parser import parse from shutil import copyfile from pprint import pprint from person import personBoxJson @@ -24,26 +22,15 @@ from ssb import getSSBAddress from tox import getToxAddress from matrix import getMatrixAddress from donate import getDonationUrl -from question import isQuestion -from utils import firstParagraphFromString +from utils import weekDayOfMonthStart from utils import getCSS from utils import isSystemAccount from utils import removeIdEnding -from utils import getProtocolPrefixes -from utils import searchBoxPosts -from utils import isEventPost -from utils import isBlogPost -from utils import isNewsPost -from utils import updateRecentPostsCache from utils import getNicknameFromActor from utils import getDomainFromActor from utils import locatePost from utils import noOfAccounts -from utils import isPublicPost from utils import isPublicPostFromUrl -from utils import getDisplayName -from utils import getCachedPostDirectory -from utils import getCachedPostFilename from utils import loadJson from utils import getConfigParam from utils import votesOnNewswireItem @@ -51,58 +38,40 @@ from utils import removeHtml from follow import isFollowingActor from follow import followerApprovalActive from webfinger import webfingerHandle -from posts import isDM from posts import getPersonBox from posts import getUserUrl from posts import parseUserFeed from posts import populateRepliesJson from posts import isModerator from posts import isEditor -from posts import downloadAnnounce from session import getJson -from auth import createPassword -from like import likedByPerson -from like import noOfLikes -from bookmarks import bookmarkedByPerson -from announce import announcedByPerson from blocking import isBlocked -from blocking import isBlockedHashtag -from content import htmlReplaceEmailQuote -from content import htmlReplaceQuoteMarks -from content import removeTextFormatting -from content import switchWords -from content import getMentionsFromHtml -from content import addHtmlTags -from content import replaceEmojiFromTags from content import removeLongWords from skills import getSkills -from cache import getPersonFromCache from shares import getValidSharedItemID from happening import todaysEventsCheck from happening import thisWeeksEventsCheck from happening import getCalendarEvents from happening import getTodaysEvents -from git import isGitPatch from theme import getThemesList from petnames import getPetName from followingCalendar import receivingCalendarEvents -from devices import E2EEdecryptMessageFromDevice -from feeds import rss2TagHeader -from feeds import rss2TagFooter from webapp_utils import getAltPath -from webapp_utils import getContentWarningButton from webapp_utils import getBlogAddress from webapp_utils import getPersonAvatarUrl -from webapp_utils import updateAvatarImageCache from webapp_utils import getIconsDir from webapp_utils import scheduledPostsExist from webapp_utils import sharesTimelineJson -from webapp_utils import postContainsPublic from webapp_utils import getImageFile from webapp_utils import getBannerFile -from webapp_utils import getSearchBannerFile from webapp_utils import getLeftImageFile from webapp_utils import getRightImageFile +from webapp_utils import htmlHeader +from webapp_utils import htmlFooter +from webapp_utils import addEmojiToDisplayName +from webapp_utils import htmlPostSeparator +from webapp_post import individualPostAsHtml +from webapp_post import preparePostFromHtmlCache def htmlFollowingList(cssCache: {}, baseDir: str, @@ -171,246 +140,6 @@ def htmlFollowingDataList(baseDir: str, nickname: str, return listStr -def htmlSearchEmoji(cssCache: {}, translate: {}, - baseDir: str, httpPrefix: str, - searchStr: str) -> str: - """Search results for emoji - """ - # emoji.json is generated so that it can be customized and the changes - # will be retained even if default_emoji.json is subsequently updated - if not os.path.isfile(baseDir + '/emoji/emoji.json'): - copyfile(baseDir + '/emoji/default_emoji.json', - baseDir + '/emoji/emoji.json') - - searchStr = searchStr.lower().replace(':', '').strip('\n').strip('\r') - cssFilename = baseDir + '/epicyon-profile.css' - if os.path.isfile(baseDir + '/epicyon.css'): - cssFilename = baseDir + '/epicyon.css' - - emojiCSS = getCSS(baseDir, cssFilename, cssCache) - if emojiCSS: - if httpPrefix != 'https': - emojiCSS = emojiCSS.replace('https://', - httpPrefix + '://') - emojiLookupFilename = baseDir + '/emoji/emoji.json' - - # create header - emojiForm = htmlHeader(cssFilename, emojiCSS) - emojiForm += '

' + \ - translate['Emoji Search'] + \ - '

' - - # does the lookup file exist? - if not os.path.isfile(emojiLookupFilename): - emojiForm += '
' + \ - translate['No results'] + '
' - emojiForm += htmlFooter() - return emojiForm - - emojiJson = loadJson(emojiLookupFilename) - if emojiJson: - results = {} - for emojiName, filename in emojiJson.items(): - if searchStr in emojiName: - results[emojiName] = filename + '.png' - for emojiName, filename in emojiJson.items(): - if emojiName in searchStr: - results[emojiName] = filename + '.png' - headingShown = False - emojiForm += '
' - msgStr1 = translate['Copy the text then paste it into your post'] - msgStr2 = ':' - emojiForm += '
' - - emojiForm += htmlFooter() - return emojiForm - - -def htmlSearchSharedItems(cssCache: {}, translate: {}, - baseDir: str, searchStr: str, - pageNumber: int, - resultsPerPage: int, - httpPrefix: str, - domainFull: str, actor: str, - callingDomain: str) -> str: - """Search results for shared items - """ - iconsDir = getIconsDir(baseDir) - currPage = 1 - ctr = 0 - sharedItemsForm = '' - searchStrLower = urllib.parse.unquote(searchStr) - searchStrLower = searchStrLower.lower().strip('\n').strip('\r') - searchStrLowerList = searchStrLower.split('+') - cssFilename = baseDir + '/epicyon-profile.css' - if os.path.isfile(baseDir + '/epicyon.css'): - cssFilename = baseDir + '/epicyon.css' - - sharedItemsCSS = getCSS(baseDir, cssFilename, cssCache) - if sharedItemsCSS: - if httpPrefix != 'https': - sharedItemsCSS = \ - sharedItemsCSS.replace('https://', - httpPrefix + '://') - sharedItemsForm = htmlHeader(cssFilename, sharedItemsCSS) - sharedItemsForm += \ - '

' + translate['Shared Items Search'] + \ - '

' - resultsExist = False - for subdir, dirs, files in os.walk(baseDir + '/accounts'): - for handle in dirs: - if '@' not in handle: - continue - contactNickname = handle.split('@')[0] - sharesFilename = baseDir + '/accounts/' + handle + \ - '/shares.json' - if not os.path.isfile(sharesFilename): - continue - - sharesJson = loadJson(sharesFilename) - if not sharesJson: - continue - - for name, sharedItem in sharesJson.items(): - matched = True - for searchSubstr in searchStrLowerList: - subStrMatched = False - searchSubstr = searchSubstr.strip() - if searchSubstr in sharedItem['location'].lower(): - subStrMatched = True - elif searchSubstr in sharedItem['summary'].lower(): - subStrMatched = True - elif searchSubstr in sharedItem['displayName'].lower(): - subStrMatched = True - elif searchSubstr in sharedItem['category'].lower(): - subStrMatched = True - if not subStrMatched: - matched = False - break - if matched: - if currPage == pageNumber: - sharedItemsForm += '
\n' - sharedItemsForm += \ - '

' + \ - sharedItem['displayName'] + '

\n' - if sharedItem.get('imageUrl'): - sharedItemsForm += \ - '\n' - sharedItemsForm += \ - 'Item image\n' - sharedItemsForm += \ - '

' + sharedItem['summary'] + '

\n' - sharedItemsForm += \ - '

' + translate['Type'] + \ - ': ' + sharedItem['itemType'] + ' ' - sharedItemsForm += \ - '' + translate['Category'] + \ - ': ' + sharedItem['category'] + ' ' - sharedItemsForm += \ - '' + translate['Location'] + \ - ': ' + sharedItem['location'] + '

\n' - contactActor = \ - httpPrefix + '://' + domainFull + \ - '/users/' + contactNickname - sharedItemsForm += \ - '

\n' - if actor.endswith('/users/' + contactNickname): - sharedItemsForm += \ - ' \n' - sharedItemsForm += '

\n' - if not resultsExist and currPage > 1: - postActor = \ - getAltPath(actor, domainFull, - callingDomain) - # previous page link, needs to be a POST - sharedItemsForm += \ - '
\n' - sharedItemsForm += \ - ' \n' - sharedItemsForm += \ - '
\n' - sharedItemsForm += \ - '
\n' + \ - ' \n' - sharedItemsForm += \ - ' ' + translate['Page up'] + \
-                                    '\n' - sharedItemsForm += '
\n' - sharedItemsForm += '
\n' - resultsExist = True - ctr += 1 - if ctr >= resultsPerPage: - currPage += 1 - if currPage > pageNumber: - postActor = \ - getAltPath(actor, domainFull, - callingDomain) - # next page link, needs to be a POST - sharedItemsForm += \ - '
\n' - sharedItemsForm += \ - ' \n' - sharedItemsForm += \ - '
\n' - sharedItemsForm += \ - '
\n' + \ - ' \n' - sharedItemsForm += \ - ' ' + translate['Page down'] + \
-                                    '\n' - sharedItemsForm += '
\n' - sharedItemsForm += '
\n' - break - ctr = 0 - if not resultsExist: - sharedItemsForm += \ - '
' + translate['No results'] + '
\n' - sharedItemsForm += htmlFooter() - return sharedItemsForm - - def htmlModerationInfo(cssCache: {}, translate: {}, baseDir: str, httpPrefix: str) -> str: msgStr1 = \ @@ -477,494 +206,6 @@ def htmlModerationInfo(cssCache: {}, translate: {}, return infoForm -def htmlHashtagSearch(cssCache: {}, - nickname: str, domain: str, port: int, - recentPostsCache: {}, maxRecentPosts: int, - translate: {}, - baseDir: str, hashtag: str, pageNumber: int, - postsPerPage: int, - session, wfRequest: {}, personCache: {}, - httpPrefix: str, projectVersion: str, - YTReplacementDomain: str, - showPublishedDateOnly: bool) -> str: - """Show a page containing search results for a hashtag - """ - if hashtag.startswith('#'): - hashtag = hashtag[1:] - hashtag = urllib.parse.unquote(hashtag) - hashtagIndexFile = baseDir + '/tags/' + hashtag + '.txt' - if not os.path.isfile(hashtagIndexFile): - if hashtag != hashtag.lower(): - hashtag = hashtag.lower() - hashtagIndexFile = baseDir + '/tags/' + hashtag + '.txt' - if not os.path.isfile(hashtagIndexFile): - print('WARN: hashtag file not found ' + hashtagIndexFile) - return None - - iconsDir = getIconsDir(baseDir) - separatorStr = htmlPostSeparator(baseDir, None) - - # check that the directory for the nickname exists - if nickname: - if not os.path.isdir(baseDir + '/accounts/' + - nickname + '@' + domain): - nickname = None - - # read the index - with open(hashtagIndexFile, "r") as f: - lines = f.readlines() - - # read the css - cssFilename = baseDir + '/epicyon-profile.css' - if os.path.isfile(baseDir + '/epicyon.css'): - cssFilename = baseDir + '/epicyon.css' - - hashtagSearchCSS = getCSS(baseDir, cssFilename, cssCache) - if hashtagSearchCSS: - if httpPrefix != 'https': - hashtagSearchCSS = \ - hashtagSearchCSS.replace('https://', - httpPrefix + '://') - - # ensure that the page number is in bounds - if not pageNumber: - pageNumber = 1 - elif pageNumber < 1: - pageNumber = 1 - - # get the start end end within the index file - startIndex = int((pageNumber - 1) * postsPerPage) - endIndex = startIndex + postsPerPage - noOfLines = len(lines) - if endIndex >= noOfLines and noOfLines > 0: - endIndex = noOfLines - 1 - - # add the page title - hashtagSearchForm = htmlHeader(cssFilename, hashtagSearchCSS) - if nickname: - hashtagSearchForm += '
\n' + \ - '

#' + \ - hashtag + '

\n' + '
\n' - else: - hashtagSearchForm += '
\n' + \ - '

#' + hashtag + '

\n' + '
\n' - - # RSS link for hashtag feed - hashtagSearchForm += '
' - hashtagSearchForm += \ - 'RSS 2.0
' - - if startIndex > 0: - # previous page link - hashtagSearchForm += \ - '
\n' + \ - ' ' + translate['Page up'] + \
-            '\n
\n' - index = startIndex - while index <= endIndex: - postId = lines[index].strip('\n').strip('\r') - if ' ' not in postId: - nickname = getNicknameFromActor(postId) - if not nickname: - index += 1 - continue - else: - postFields = postId.split(' ') - if len(postFields) != 3: - index += 1 - continue - nickname = postFields[1] - postId = postFields[2] - postFilename = locatePost(baseDir, nickname, domain, postId) - if not postFilename: - index += 1 - continue - postJsonObject = loadJson(postFilename) - if postJsonObject: - if not isPublicPost(postJsonObject): - index += 1 - continue - showIndividualPostIcons = False - if nickname: - showIndividualPostIcons = True - allowDeletion = False - hashtagSearchForm += separatorStr + \ - individualPostAsHtml(True, recentPostsCache, - maxRecentPosts, - iconsDir, translate, None, - baseDir, session, wfRequest, - personCache, - nickname, domain, port, - postJsonObject, - None, True, allowDeletion, - httpPrefix, projectVersion, - 'search', - YTReplacementDomain, - showPublishedDateOnly, - showIndividualPostIcons, - showIndividualPostIcons, - False, False, False) - index += 1 - - if endIndex < noOfLines - 1: - # next page link - hashtagSearchForm += \ - '
\n' + \ - ' ' + translate['Page down'] + '' + \ - '
' - hashtagSearchForm += htmlFooter() - return hashtagSearchForm - - -def rssHashtagSearch(nickname: str, domain: str, port: int, - recentPostsCache: {}, maxRecentPosts: int, - translate: {}, - baseDir: str, hashtag: str, - postsPerPage: int, - session, wfRequest: {}, personCache: {}, - httpPrefix: str, projectVersion: str, - YTReplacementDomain: str) -> str: - """Show an rss feed for a hashtag - """ - if hashtag.startswith('#'): - hashtag = hashtag[1:] - hashtag = urllib.parse.unquote(hashtag) - hashtagIndexFile = baseDir + '/tags/' + hashtag + '.txt' - if not os.path.isfile(hashtagIndexFile): - if hashtag != hashtag.lower(): - hashtag = hashtag.lower() - hashtagIndexFile = baseDir + '/tags/' + hashtag + '.txt' - if not os.path.isfile(hashtagIndexFile): - print('WARN: hashtag file not found ' + hashtagIndexFile) - return None - - # check that the directory for the nickname exists - if nickname: - if not os.path.isdir(baseDir + '/accounts/' + - nickname + '@' + domain): - nickname = None - - # read the index - lines = [] - with open(hashtagIndexFile, "r") as f: - lines = f.readlines() - if not lines: - return None - - domainFull = domain - if port: - if port != 80 and port != 443: - domainFull = domain + ':' + str(port) - - maxFeedLength = 10 - hashtagFeed = \ - rss2TagHeader(hashtag, httpPrefix, domainFull) - for index in range(len(lines)): - postId = lines[index].strip('\n').strip('\r') - if ' ' not in postId: - nickname = getNicknameFromActor(postId) - if not nickname: - index += 1 - if index >= maxFeedLength: - break - continue - else: - postFields = postId.split(' ') - if len(postFields) != 3: - index += 1 - if index >= maxFeedLength: - break - continue - nickname = postFields[1] - postId = postFields[2] - postFilename = locatePost(baseDir, nickname, domain, postId) - if not postFilename: - index += 1 - if index >= maxFeedLength: - break - continue - postJsonObject = loadJson(postFilename) - if postJsonObject: - if not isPublicPost(postJsonObject): - index += 1 - if index >= maxFeedLength: - break - continue - # add to feed - if postJsonObject['object'].get('content') and \ - postJsonObject['object'].get('attributedTo') and \ - postJsonObject['object'].get('published'): - published = postJsonObject['object']['published'] - pubDate = datetime.strptime(published, "%Y-%m-%dT%H:%M:%SZ") - rssDateStr = pubDate.strftime("%a, %d %b %Y %H:%M:%S UT") - hashtagFeed += ' ' - hashtagFeed += \ - ' ' + \ - postJsonObject['object']['attributedTo'] + \ - '' - if postJsonObject['object'].get('summary'): - hashtagFeed += \ - ' ' + \ - postJsonObject['object']['summary'] + \ - '' - description = postJsonObject['object']['content'] - description = firstParagraphFromString(description) - hashtagFeed += \ - ' ' + description + '' - hashtagFeed += \ - ' ' + rssDateStr + '' - if postJsonObject['object'].get('attachment'): - for attach in postJsonObject['object']['attachment']: - if not attach.get('url'): - continue - hashtagFeed += \ - ' ' + attach['url'] + '' - hashtagFeed += ' ' - index += 1 - if index >= maxFeedLength: - break - - return hashtagFeed + rss2TagFooter() - - -def htmlSkillsSearch(cssCache: {}, translate: {}, baseDir: str, - httpPrefix: str, - skillsearch: str, instanceOnly: bool, - postsPerPage: int) -> str: - """Show a page containing search results for a skill - """ - if skillsearch.startswith('*'): - skillsearch = skillsearch[1:].strip() - - skillsearch = skillsearch.lower().strip('\n').strip('\r') - - results = [] - # search instance accounts - for subdir, dirs, files in os.walk(baseDir + '/accounts/'): - for f in files: - if not f.endswith('.json'): - continue - if '@' not in f: - continue - if f.startswith('inbox@'): - continue - actorFilename = os.path.join(subdir, f) - actorJson = loadJson(actorFilename) - if actorJson: - if actorJson.get('id') and \ - actorJson.get('skills') and \ - actorJson.get('name') and \ - actorJson.get('icon'): - actor = actorJson['id'] - for skillName, skillLevel in actorJson['skills'].items(): - skillName = skillName.lower() - if not (skillName in skillsearch or - skillsearch in skillName): - continue - skillLevelStr = str(skillLevel) - if skillLevel < 100: - skillLevelStr = '0' + skillLevelStr - if skillLevel < 10: - skillLevelStr = '0' + skillLevelStr - indexStr = \ - skillLevelStr + ';' + actor + ';' + \ - actorJson['name'] + \ - ';' + actorJson['icon']['url'] - if indexStr not in results: - results.append(indexStr) - if not instanceOnly: - # search actor cache - for subdir, dirs, files in os.walk(baseDir + '/cache/actors/'): - for f in files: - if not f.endswith('.json'): - continue - if '@' not in f: - continue - if f.startswith('inbox@'): - continue - actorFilename = os.path.join(subdir, f) - cachedActorJson = loadJson(actorFilename) - if cachedActorJson: - if cachedActorJson.get('actor'): - actorJson = cachedActorJson['actor'] - if actorJson.get('id') and \ - actorJson.get('skills') and \ - actorJson.get('name') and \ - actorJson.get('icon'): - actor = actorJson['id'] - for skillName, skillLevel in \ - actorJson['skills'].items(): - skillName = skillName.lower() - if not (skillName in skillsearch or - skillsearch in skillName): - continue - skillLevelStr = str(skillLevel) - if skillLevel < 100: - skillLevelStr = '0' + skillLevelStr - if skillLevel < 10: - skillLevelStr = '0' + skillLevelStr - indexStr = \ - skillLevelStr + ';' + actor + ';' + \ - actorJson['name'] + \ - ';' + actorJson['icon']['url'] - if indexStr not in results: - results.append(indexStr) - - results.sort(reverse=True) - - cssFilename = baseDir + '/epicyon-profile.css' - if os.path.isfile(baseDir + '/epicyon.css'): - cssFilename = baseDir + '/epicyon.css' - - skillSearchCSS = getCSS(baseDir, cssFilename, cssCache) - if skillSearchCSS: - if httpPrefix != 'https': - skillSearchCSS = \ - skillSearchCSS.replace('https://', - httpPrefix + '://') - skillSearchForm = htmlHeader(cssFilename, skillSearchCSS) - skillSearchForm += \ - '

' + translate['Skills search'] + ': ' + \ - skillsearch + '

' - - if len(results) == 0: - skillSearchForm += \ - '
' + translate['No results'] + \ - '
' - else: - skillSearchForm += '
' - ctr = 0 - for skillMatch in results: - skillMatchFields = skillMatch.split(';') - if len(skillMatchFields) != 4: - continue - actor = skillMatchFields[1] - actorName = skillMatchFields[2] - avatarUrl = skillMatchFields[3] - skillSearchForm += \ - '
' - skillSearchForm += \ - '' + actorName + \ - '
' - ctr += 1 - if ctr >= postsPerPage: - break - skillSearchForm += '
' - skillSearchForm += htmlFooter() - return skillSearchForm - - -def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str, - httpPrefix: str, - nickname: str, domain: str, - historysearch: str, - postsPerPage: int, pageNumber: int, - projectVersion: str, - recentPostsCache: {}, - maxRecentPosts: int, - session, - wfRequest, - personCache: {}, - port: int, - YTReplacementDomain: str, - showPublishedDateOnly: bool) -> str: - """Show a page containing search results for your post history - """ - if historysearch.startswith('!'): - historysearch = historysearch[1:].strip() - - historysearch = historysearch.lower().strip('\n').strip('\r') - - boxFilenames = \ - searchBoxPosts(baseDir, nickname, domain, - historysearch, postsPerPage) - - cssFilename = baseDir + '/epicyon-profile.css' - if os.path.isfile(baseDir + '/epicyon.css'): - cssFilename = baseDir + '/epicyon.css' - - historySearchCSS = getCSS(baseDir, cssFilename, cssCache) - if historySearchCSS: - if httpPrefix != 'https': - historySearchCSS = \ - historySearchCSS.replace('https://', - httpPrefix + '://') - historySearchForm = htmlHeader(cssFilename, historySearchCSS) - - # add the page title - historySearchForm += \ - '

' + translate['Your Posts'] + '

' - - if len(boxFilenames) == 0: - historySearchForm += \ - '
' + translate['No results'] + \ - '
' - return historySearchForm - - iconsDir = getIconsDir(baseDir) - separatorStr = htmlPostSeparator(baseDir, None) - - # ensure that the page number is in bounds - if not pageNumber: - pageNumber = 1 - elif pageNumber < 1: - pageNumber = 1 - - # get the start end end within the index file - startIndex = int((pageNumber - 1) * postsPerPage) - endIndex = startIndex + postsPerPage - noOfBoxFilenames = len(boxFilenames) - if endIndex >= noOfBoxFilenames and noOfBoxFilenames > 0: - endIndex = noOfBoxFilenames - 1 - - index = startIndex - while index <= endIndex: - postFilename = boxFilenames[index] - if not postFilename: - index += 1 - continue - postJsonObject = loadJson(postFilename) - if not postJsonObject: - index += 1 - continue - showIndividualPostIcons = True - allowDeletion = False - historySearchForm += separatorStr + \ - individualPostAsHtml(True, recentPostsCache, - maxRecentPosts, - iconsDir, translate, None, - baseDir, session, wfRequest, - personCache, - nickname, domain, port, - postJsonObject, - None, True, allowDeletion, - httpPrefix, projectVersion, - 'search', - YTReplacementDomain, - showPublishedDateOnly, - showIndividualPostIcons, - showIndividualPostIcons, - False, False, False) - index += 1 - - historySearchForm += htmlFooter() - return historySearchForm - - def htmlEditLinks(cssCache: {}, translate: {}, baseDir: str, path: str, domain: str, port: int, httpPrefix: str, defaultTimeline: str) -> str: @@ -2831,40 +2072,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, return newPostForm -def getFontFromCss(css: str) -> (str, str): - """Returns the font name and format - """ - if ' url(' not in css: - return None, None - fontName = css.split(" url(")[1].split(")")[0].replace("'", '') - fontFormat = css.split(" format('")[1].split("')")[0] - return fontName, fontFormat - - -def htmlHeader(cssFilename: str, css: str, lang='en') -> str: - htmlStr = '\n' - htmlStr += '\n' - htmlStr += ' \n' - htmlStr += ' \n' - fontName, fontFormat = getFontFromCss(css) - if fontName: - htmlStr += ' \n' - htmlStr += ' \n' - htmlStr += ' \n' - htmlStr += ' \n' - htmlStr += ' Epicyon\n' - htmlStr += ' \n' - htmlStr += ' \n' - return htmlStr - - -def htmlFooter() -> str: - htmlStr = ' \n' - htmlStr += '\n' - return htmlStr - - def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int, translate: {}, baseDir: str, httpPrefix: str, @@ -3530,1775 +2737,6 @@ def individualFollowAsHtml(translate: {}, return resultStr -def addEmbeddedAudio(translate: {}, content: str) -> str: - """Adds embedded audio for mp3/ogg - """ - if not ('.mp3' in content or '.ogg' in content): - return content - - if '