Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon into main
23
blog.py
|
@ -233,7 +233,28 @@ def htmlBlogPostContent(authorized: bool,
|
|||
'content')
|
||||
blogStr += '<br>' + contentStr + '\n'
|
||||
|
||||
blogStr += '<br>\n'
|
||||
citationsStr = ''
|
||||
if postJsonObject['object'].get('tag'):
|
||||
for tagJson in postJsonObject['object']['tag']:
|
||||
if not isinstance(tagJson, dict):
|
||||
continue
|
||||
if not tagJson.get('type'):
|
||||
continue
|
||||
if tagJson['type'] != 'Article':
|
||||
continue
|
||||
if not tagJson.get('name'):
|
||||
continue
|
||||
if not tagJson.get('url'):
|
||||
continue
|
||||
citationsStr += \
|
||||
'<li><a href="' + tagJson['url'] + '">' + \
|
||||
'<cite>' + tagJson['name'] + '</cite></a></li>\n'
|
||||
if citationsStr:
|
||||
citationsStr = '<p><b>' + translate['Citations'] + \
|
||||
':</b></p>' + \
|
||||
'<ul>\n' + citationsStr + '</ul>\n'
|
||||
|
||||
blogStr += '<br>\n' + citationsStr
|
||||
|
||||
if not linkedAuthor:
|
||||
blogStr += '<p class="about"><a class="about" href="' + \
|
||||
|
|
222
daemon.py
|
@ -87,6 +87,7 @@ from inbox import populateReplies
|
|||
from inbox import getPersonPubKey
|
||||
from follow import getFollowingFeed
|
||||
from follow import sendFollowRequest
|
||||
from follow import unfollowPerson
|
||||
from auth import authorize
|
||||
from auth import createPassword
|
||||
from auth import createBasicAuthHeader
|
||||
|
@ -112,6 +113,7 @@ from blog import htmlBlogView
|
|||
from blog import htmlBlogPage
|
||||
from blog import htmlBlogPost
|
||||
from blog import htmlEditBlog
|
||||
from webinterface import htmlCitations
|
||||
from webinterface import htmlFollowingList
|
||||
from webinterface import getBlogAddress
|
||||
from webinterface import setBlogAddress
|
||||
|
@ -1799,8 +1801,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
# person options screen, unfollow button
|
||||
# See htmlPersonOptions
|
||||
if '&submitUnfollow=' in optionsConfirmParams:
|
||||
if debug:
|
||||
print('Unfollowing ' + optionsActor)
|
||||
print('Unfollowing ' + optionsActor)
|
||||
msg = \
|
||||
htmlUnfollowConfirm(self.server.cssCache,
|
||||
self.server.translate,
|
||||
|
@ -1830,7 +1831,8 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
chooserNickname,
|
||||
domain,
|
||||
domainFull,
|
||||
self.server.defaultTimeline).encode('utf-8')
|
||||
self.server.defaultTimeline,
|
||||
self.server.newswire).encode('utf-8')
|
||||
self._set_headers('text/html', len(msg),
|
||||
cookie, callingDomain)
|
||||
self._write(msg)
|
||||
|
@ -1897,7 +1899,8 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
chooserNickname,
|
||||
domain,
|
||||
domainFull,
|
||||
self.server.defaultTimeline).encode('utf-8')
|
||||
self.server.defaultTimeline,
|
||||
self.server.newswire).encode('utf-8')
|
||||
self._set_headers('text/html', len(msg),
|
||||
cookie, callingDomain)
|
||||
self._write(msg)
|
||||
|
@ -1956,6 +1959,11 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
followingNickname = getNicknameFromActor(followingActor)
|
||||
followingDomain, followingPort = \
|
||||
getDomainFromActor(followingActor)
|
||||
followingDomainFull = followingDomain
|
||||
if followingPort:
|
||||
if followingPort != 80 and followingPort != 443:
|
||||
followingDomainFull = \
|
||||
followingDomain + ':' + str(followingPort)
|
||||
if followerNickname == followingNickname and \
|
||||
followingDomain == domain and \
|
||||
followingPort == port:
|
||||
|
@ -1984,6 +1992,9 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
}
|
||||
pathUsersSection = path.split('/users/')[1]
|
||||
self.postToNickname = pathUsersSection.split('/')[0]
|
||||
unfollowPerson(self.server.baseDir, self.postToNickname,
|
||||
self.server.domain,
|
||||
followingNickname, followingDomainFull)
|
||||
self._postToOutboxThread(unfollowJson)
|
||||
|
||||
if callingDomain.endswith('.onion') and onionDomain:
|
||||
|
@ -3045,6 +3056,109 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
cookie, callingDomain)
|
||||
self.server.POSTbusy = False
|
||||
|
||||
def _citationsUpdate(self, callingDomain: str, cookie: str,
|
||||
authorized: bool, path: str,
|
||||
baseDir: str, httpPrefix: str,
|
||||
domain: str, domainFull: str,
|
||||
onionDomain: str, i2pDomain: str, debug: bool,
|
||||
defaultTimeline: str,
|
||||
newswire: {}) -> None:
|
||||
"""Updates the citations for a blog post after hitting
|
||||
update button on the citations screen
|
||||
"""
|
||||
usersPath = path.replace('/citationsdata', '')
|
||||
actorStr = httpPrefix + '://' + domainFull + usersPath
|
||||
nickname = getNicknameFromActor(actorStr)
|
||||
|
||||
citationsFilename = \
|
||||
baseDir + '/accounts/' + \
|
||||
nickname + '@' + domain + '/.citations.txt'
|
||||
# remove any existing citations file
|
||||
if os.path.isfile(citationsFilename):
|
||||
os.remove(citationsFilename)
|
||||
|
||||
if newswire and \
|
||||
' boundary=' in self.headers['Content-type']:
|
||||
boundary = self.headers['Content-type'].split('boundary=')[1]
|
||||
if ';' in boundary:
|
||||
boundary = boundary.split(';')[0]
|
||||
|
||||
length = int(self.headers['Content-length'])
|
||||
|
||||
# check that the POST isn't too large
|
||||
if length > self.server.maxPostLength:
|
||||
if callingDomain.endswith('.onion') and \
|
||||
onionDomain:
|
||||
actorStr = \
|
||||
'http://' + onionDomain + usersPath
|
||||
elif (callingDomain.endswith('.i2p') and
|
||||
i2pDomain):
|
||||
actorStr = \
|
||||
'http://' + i2pDomain + usersPath
|
||||
print('Maximum citations data length exceeded ' + str(length))
|
||||
self._redirect_headers(actorStr, cookie, callingDomain)
|
||||
self.server.POSTbusy = False
|
||||
return
|
||||
|
||||
try:
|
||||
# read the bytes of the http form POST
|
||||
postBytes = self.rfile.read(length)
|
||||
except SocketError as e:
|
||||
if e.errno == errno.ECONNRESET:
|
||||
print('WARN: connection was reset while ' +
|
||||
'reading bytes from http form ' +
|
||||
'citation screen POST')
|
||||
else:
|
||||
print('WARN: error while reading bytes ' +
|
||||
'from http form citations screen POST')
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
self.server.POSTbusy = False
|
||||
return
|
||||
except ValueError as e:
|
||||
print('ERROR: failed to read bytes for ' +
|
||||
'citations screen POST')
|
||||
print(e)
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
self.server.POSTbusy = False
|
||||
return
|
||||
|
||||
# extract all of the text fields into a dict
|
||||
fields = \
|
||||
extractTextFieldsInPOST(postBytes, boundary, debug)
|
||||
print('citationstest: ' + str(fields))
|
||||
citations = []
|
||||
for ctr in range(0, 128):
|
||||
fieldName = 'newswire' + str(ctr)
|
||||
if not fields.get(fieldName):
|
||||
continue
|
||||
citations.append(fields[fieldName])
|
||||
|
||||
if citations:
|
||||
citationsStr = ''
|
||||
for citationDate in citations:
|
||||
citationsStr += citationDate + '\n'
|
||||
# save citations dates, so that they can be added when
|
||||
# reloading the newblog screen
|
||||
citationsFile = open(citationsFilename, "w+")
|
||||
if citationsFile:
|
||||
citationsFile.write(citationsStr)
|
||||
citationsFile.close()
|
||||
|
||||
# redirect back to the default timeline
|
||||
if callingDomain.endswith('.onion') and \
|
||||
onionDomain:
|
||||
actorStr = \
|
||||
'http://' + onionDomain + usersPath
|
||||
elif (callingDomain.endswith('.i2p') and
|
||||
i2pDomain):
|
||||
actorStr = \
|
||||
'http://' + i2pDomain + usersPath
|
||||
self._redirect_headers(actorStr + '/newblog',
|
||||
cookie, callingDomain)
|
||||
self.server.POSTbusy = False
|
||||
|
||||
def _newsPostEdit(self, callingDomain: str, cookie: str,
|
||||
authorized: bool, path: str,
|
||||
baseDir: str, httpPrefix: str,
|
||||
|
@ -8494,7 +8608,8 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
replyPageNumber,
|
||||
nickname, domain,
|
||||
domainFull,
|
||||
self.server.defaultTimeline).encode('utf-8')
|
||||
self.server.defaultTimeline,
|
||||
self.server.newswire).encode('utf-8')
|
||||
if not msg:
|
||||
print('Error replying to ' + inReplyToUrl)
|
||||
self._404()
|
||||
|
@ -10756,9 +10871,8 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
filename.endswith('.webp') or \
|
||||
filename.endswith('.avif') or \
|
||||
filename.endswith('.gif'):
|
||||
if self.server.debug:
|
||||
print('DEBUG: POST media removing metadata')
|
||||
postImageFilename = filename.replace('.temp', '')
|
||||
print('Removing metadata from ' + postImageFilename)
|
||||
removeMetaData(filename, postImageFilename)
|
||||
if os.path.isfile(postImageFilename):
|
||||
print('POST media saved to ' + postImageFilename)
|
||||
|
@ -10779,15 +10893,24 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
else:
|
||||
print('WARN: no text fields could be extracted from POST')
|
||||
|
||||
# process the received text fields from the POST
|
||||
if not fields.get('message') and \
|
||||
not fields.get('imageDescription'):
|
||||
return -1
|
||||
if fields.get('submitPost'):
|
||||
if fields['submitPost'] != 'Submit':
|
||||
# was the citations button pressed on the newblog screen?
|
||||
citationsButtonPress = False
|
||||
if postType == 'newblog' and fields.get('submitCitations'):
|
||||
if fields['submitCitations'] == \
|
||||
self.server.translate['Citations']:
|
||||
citationsButtonPress = True
|
||||
|
||||
if not citationsButtonPress:
|
||||
# process the received text fields from the POST
|
||||
if not fields.get('message') and \
|
||||
not fields.get('imageDescription'):
|
||||
return -1
|
||||
else:
|
||||
return 2
|
||||
if fields.get('submitPost'):
|
||||
if fields['submitPost'] != \
|
||||
self.server.translate['Submit']:
|
||||
return -1
|
||||
else:
|
||||
return 2
|
||||
|
||||
if not fields.get('imageDescription'):
|
||||
fields['imageDescription'] = None
|
||||
|
@ -10809,19 +10932,20 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
if not fields.get('location'):
|
||||
fields['location'] = None
|
||||
|
||||
# Store a file which contains the time in seconds
|
||||
# since epoch when an attempt to post something was made.
|
||||
# This is then used for active monthly users counts
|
||||
lastUsedFilename = \
|
||||
self.server.baseDir + '/accounts/' + \
|
||||
nickname + '@' + self.server.domain + '/.lastUsed'
|
||||
try:
|
||||
lastUsedFile = open(lastUsedFilename, 'w+')
|
||||
if lastUsedFile:
|
||||
lastUsedFile.write(str(int(time.time())))
|
||||
lastUsedFile.close()
|
||||
except BaseException:
|
||||
pass
|
||||
if not citationsButtonPress:
|
||||
# Store a file which contains the time in seconds
|
||||
# since epoch when an attempt to post something was made.
|
||||
# This is then used for active monthly users counts
|
||||
lastUsedFilename = \
|
||||
self.server.baseDir + '/accounts/' + \
|
||||
nickname + '@' + self.server.domain + '/.lastUsed'
|
||||
try:
|
||||
lastUsedFile = open(lastUsedFilename, 'w+')
|
||||
if lastUsedFile:
|
||||
lastUsedFile.write(str(int(time.time())))
|
||||
lastUsedFile.close()
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
mentionsStr = ''
|
||||
if fields.get('mentions'):
|
||||
|
@ -10866,6 +10990,31 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
else:
|
||||
return -1
|
||||
elif postType == 'newblog':
|
||||
# citations button on newblog screen
|
||||
if citationsButtonPress:
|
||||
messageJson = \
|
||||
htmlCitations(self.server.baseDir,
|
||||
nickname,
|
||||
self.server.domain,
|
||||
self.server.httpPrefix,
|
||||
self.server.defaultTimeline,
|
||||
self.server.translate,
|
||||
self.server.newswire,
|
||||
self.server.cssCache,
|
||||
fields['subject'],
|
||||
fields['message'],
|
||||
filename, attachmentMediaType,
|
||||
fields['imageDescription'])
|
||||
if messageJson:
|
||||
messageJson = messageJson.encode('utf-8')
|
||||
self._set_headers('text/html',
|
||||
len(messageJson),
|
||||
cookie, callingDomain)
|
||||
self._write(messageJson)
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
# submit button on newblog screen
|
||||
messageJson = \
|
||||
createBlogPost(self.server.baseDir, nickname,
|
||||
self.server.domain, self.server.port,
|
||||
|
@ -10876,8 +11025,10 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
fields['imageDescription'],
|
||||
self.server.useBlurHash,
|
||||
fields['replyTo'], fields['replyTo'],
|
||||
fields['subject'], fields['schedulePost'],
|
||||
fields['eventDate'], fields['eventTime'],
|
||||
fields['subject'],
|
||||
fields['schedulePost'],
|
||||
fields['eventDate'],
|
||||
fields['eventTime'],
|
||||
fields['location'])
|
||||
if messageJson:
|
||||
if fields['schedulePost']:
|
||||
|
@ -11637,6 +11788,17 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.server.defaultTimeline)
|
||||
return
|
||||
|
||||
if authorized and self.path.endswith('/citationsdata'):
|
||||
self._citationsUpdate(callingDomain, cookie, authorized, self.path,
|
||||
self.server.baseDir, self.server.httpPrefix,
|
||||
self.server.domain,
|
||||
self.server.domainFull,
|
||||
self.server.onionDomain,
|
||||
self.server.i2pDomain, self.server.debug,
|
||||
self.server.defaultTimeline,
|
||||
self.server.newswire)
|
||||
return
|
||||
|
||||
if authorized and self.path.endswith('/newseditdata'):
|
||||
self._newsPostEdit(callingDomain, cookie, authorized, self.path,
|
||||
self.server.baseDir, self.server.httpPrefix,
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
--title-text: #282c37;
|
||||
--title-background: #ccc;
|
||||
--focus-color: white;
|
||||
--calendar-horizontal-padding: 0;
|
||||
--font-size-calendar-header: 3rem;
|
||||
--font-size-calendar-day: 1rem;
|
||||
--font-size-calendar-cell: 4rem;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
@ -54,6 +58,7 @@ main {
|
|||
.calendar {
|
||||
table-display: fixed;
|
||||
width: 100%;
|
||||
padding: 0 var(--calendar-horizontal-padding);
|
||||
}
|
||||
|
||||
a:visited{
|
||||
|
@ -105,7 +110,7 @@ a:focus {
|
|||
background-color: var(--title-background);
|
||||
color: var(--title-text);
|
||||
display: inline-block;
|
||||
font-size: 3rem;
|
||||
font-size: var(--font-size-calendar-header);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.1em;
|
||||
padding: .5rem 2rem;
|
||||
|
@ -113,13 +118,13 @@ a:focus {
|
|||
}
|
||||
|
||||
.calendar__day__header {
|
||||
font-size: 1rem;
|
||||
font-size: var(--font-size-calendar-day);
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.calendar__day__cell {
|
||||
font-size: 4rem;
|
||||
font-size: var(--font-size-calendar-cell);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
--font-size-publish-button: 18px;
|
||||
--font-size-newswire: 18px;
|
||||
--font-size-newswire-mobile: 40px;
|
||||
--font-size-dropdown-header: 40px;
|
||||
--font-size: 30px;
|
||||
--font-size2: 24px;
|
||||
--font-size3: 38px;
|
||||
|
@ -82,10 +83,15 @@
|
|||
--column-left-width: 10vw;
|
||||
--column-center-width: 80vw;
|
||||
--column-right-width: 10vw;
|
||||
--column-left-header-style: uppercase;
|
||||
--column-left-header-background: #555;
|
||||
--column-left-header-color: #fff;
|
||||
--column-left-header-size: 20px;
|
||||
--column-left-header-size-mobile: 50px;
|
||||
--column-left-border-width: 0;
|
||||
--column-left-icons-margin: 0;
|
||||
--column-right-border-width: 0;
|
||||
--column-left-border-color: black;
|
||||
--column-left-icon-size: 20%;
|
||||
--column-left-icon-size-mobile: 10%;
|
||||
--column-left-image-width-mobile: 40vw;
|
||||
|
@ -105,6 +111,7 @@
|
|||
--tab-border-color: grey;
|
||||
--icon-brightness-change: 150%;
|
||||
--container-button-padding: 20px;
|
||||
--header-button-padding: 20px;
|
||||
--container-padding: 2%;
|
||||
--container-padding-bottom: 1%;
|
||||
--container-padding-bottom-mobile: 0%;
|
||||
|
@ -118,6 +125,11 @@
|
|||
--publish-button-vertical-offset: 10px;
|
||||
--banner-height: 15vh;
|
||||
--banner-height-mobile: 10vh;
|
||||
--post-separator-margin-top: 0;
|
||||
--post-separator-margin-bottom: 0;
|
||||
--post-separator-width: 95%;
|
||||
--post-separator-height: 1px;
|
||||
--header-vertical-offset: 0;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
@ -146,6 +158,27 @@ body, html {
|
|||
line-height: var(--line-spacing);
|
||||
}
|
||||
|
||||
.leftColIcons {
|
||||
width: 100%;
|
||||
background-color: var(--main-bg-color);
|
||||
float: right;
|
||||
display: block;
|
||||
padding-bottom: var(--column-left-icons-margin);
|
||||
}
|
||||
|
||||
.postSeparatorImage img {
|
||||
padding-top: var(--post-separator-margin-top);
|
||||
padding-bottom: var(--post-separator-margin-bottom);
|
||||
width: var(--post-separator-width);
|
||||
height: var(--post-separator-height);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.headericons {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 10px;
|
||||
margin: 1.5em 10px;
|
||||
|
@ -399,14 +432,6 @@ a:focus {
|
|||
margin: var(--vertical-between-posts);
|
||||
}
|
||||
|
||||
.containerHeader {
|
||||
border: var(--border-width-header) solid var(--border-color);
|
||||
background-color: var(--main-bg-color);
|
||||
border-radius: var(--timeline-border-radius);
|
||||
padding: var(--container-button-padding);
|
||||
margin: var(--vertical-between-posts-header);
|
||||
}
|
||||
|
||||
.media {
|
||||
width: 80%;
|
||||
border-radius: 5px;
|
||||
|
@ -670,23 +695,6 @@ input[type=submit]:hover {
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* The container <div> - needed to position the dropdown content */
|
||||
.dropdown {
|
||||
margin: 10px auto;
|
||||
padding: 0px 14px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdown img {
|
||||
opacity: 1.0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0px 16px;
|
||||
-ms-transform: translateY(-10%);
|
||||
transform: translateY(-10%);
|
||||
}
|
||||
|
||||
.timeline-avatar {
|
||||
margin: 10px auto;
|
||||
padding: 0px 0px;
|
||||
|
@ -711,39 +719,102 @@ input[type=submit]:hover {
|
|||
padding: 0px 30px;
|
||||
}
|
||||
|
||||
/* Dropdown Content (Hidden by Default) */
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: var(--dropdown-bg-color);
|
||||
min-width: 600px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
z-index: 1;
|
||||
/* new post dropdown */
|
||||
|
||||
.newPostDropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Links inside the dropdown */
|
||||
.dropdown-content a {
|
||||
background-color: var(--dropdown-bg-color);
|
||||
color: var(--dropdown-fg-color);
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
.newPostDropdown img {
|
||||
width: var(--font-size-dropdown-header);
|
||||
}
|
||||
|
||||
.dropdown-content img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0px 0px;
|
||||
.newPostDropdown > input[type="checkbox"] {
|
||||
position: absolute;
|
||||
left: -100vw;
|
||||
}
|
||||
|
||||
/* Change color of dropdown links on hover */
|
||||
.dropdown-content a:hover {
|
||||
color: var(--dropdown-fg-color-hover);
|
||||
background-color: var(--dropdown-bg-color-hover);
|
||||
.newPostDropdown > label,
|
||||
.newPostDropdown > a[role="button"] {
|
||||
display: inline-block;
|
||||
padding: 6px 0px;
|
||||
color: var(--dropdown-fg-color);
|
||||
font-size: var(--font-size-dropdown-header);
|
||||
line-height: 1.5em;
|
||||
text-decoration: none;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Show the dropdown menu on hover */
|
||||
.show {display: block;}
|
||||
.newPostDropdown > label:hover,
|
||||
.newPostDropdown > a[role="button"]:hover,
|
||||
.newPostDropdown > a[role="button"]:focus {
|
||||
border-color: var(--dropdown-fg-color-hover);
|
||||
}
|
||||
|
||||
.newPostDropdown > label:after,
|
||||
.newPostDropdown > a[role="button"]:after {
|
||||
content: "";
|
||||
font-family: 'Bedstead';
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.newPostDropdown > ul {
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
display: block;
|
||||
left: -100vw;
|
||||
top: calc(1.5em + 14px);
|
||||
border: 0;
|
||||
background: var(--dropdown-bg-color);
|
||||
padding: 6px 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.newPostDropdown > ul a {
|
||||
display: block;
|
||||
padding: 6px 15px;
|
||||
text-decoration: none;
|
||||
color: var(--dropdown-fg-color);
|
||||
}
|
||||
|
||||
.newPostDropdown > ul a:hover,
|
||||
.newPostDropdown > ul a:focus {
|
||||
color: var(--dropdown-fg-color-hover);
|
||||
background: var(--dropdown-bg-color-hover);
|
||||
}
|
||||
|
||||
.newPostDropdown > input[type="checkbox"]:checked ~ ul,
|
||||
.newPostDropdown > ul:target {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.newPostDropdown > [type="checkbox"]:checked + label:after,
|
||||
.newPostDropdown > ul:target ~ a:after {
|
||||
content: "↴";
|
||||
}
|
||||
|
||||
.newPostDropdown a.close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.newPostDropdown > ul:target ~ a.close {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-indent: -100vw;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.newPostDropdown + h2 {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.slider {
|
||||
-webkit-appearance: none;
|
||||
|
@ -808,8 +879,6 @@ div.gallery img {
|
|||
}
|
||||
|
||||
li { list-style:none;}
|
||||
.msgscope-collapse { position: relative; }
|
||||
.nav { width: 150px; }
|
||||
/***********BUTTON CODE ******************************************************/
|
||||
|
||||
a, button, input:focus, input[type='button'], input[type='reset'], input[type='submit'], textarea:focus, .button {
|
||||
|
@ -820,193 +889,10 @@ a, button, input:focus, input[type='button'], input[type='reset'], input[type='s
|
|||
transition: all 0.1s ease-in-out;
|
||||
text-decoration: none;
|
||||
}
|
||||
.button-msgScope {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
.button-msgScope button, .button-msgScope div.lined-thin {
|
||||
align-self: center;
|
||||
background: transparent;
|
||||
padding: 1rem 1rem;
|
||||
margin: 0 1rem;
|
||||
transition: all .5s ease;
|
||||
color: var(--dropdown-fg-color);
|
||||
font-size: 2rem;
|
||||
letter-spacing: 1px;
|
||||
outline: none;
|
||||
}
|
||||
.btn {
|
||||
margin: -3px 0 0 0;
|
||||
}
|
||||
|
||||
aside .toggle-msgScope input[type='checkbox'] {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.toggle-msgScope {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: margin 300ms cubic-bezier(0.17, 0.04, 0.03, 0.94);
|
||||
line-height: 2rem;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
.toggle-msgScope div[class*='toggle-inside'] {
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
}
|
||||
aside .toggle-msgScope input[type='checkbox'] {
|
||||
float:right;
|
||||
}
|
||||
aside .toggle-inside li {
|
||||
padding-left: 20px;
|
||||
width: 100%;
|
||||
margin-left: -15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.nav li:hover {
|
||||
color: var(--dropdown-fg-color-hover);
|
||||
background-color: var(--dropdown-bg-color-hover);
|
||||
}
|
||||
.nav .toggle-msgScope {
|
||||
overflow: visible;
|
||||
}
|
||||
#msgscope label div {
|
||||
-webkit-transition: all 0.1s ease-in-out;
|
||||
-moz-transition: all 0.1s ease-in-out;
|
||||
-ms-transition: all 0.1s ease-in-out;
|
||||
-o-transition: all 0.1s ease-in-out;
|
||||
transition: all 0.1s ease-in-out;
|
||||
margin: 0 auto;
|
||||
font-size: 1.5rem;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
background-color: var(--dropdown-bg-color);
|
||||
color: var(--dropdown-fg-color);
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
background-image: none;
|
||||
white-space: nowrap;
|
||||
font-size: 0px;
|
||||
line-height: 1.42857143;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
margin-top: 10px;
|
||||
}
|
||||
[id*='toggle'] .container, [class*='toggle'] .container {
|
||||
transition: margin 300ms cubic-bezier(0.17, 0.04, 0.03, 0.94);
|
||||
}
|
||||
[id*='toggle'] {
|
||||
visibility: hidden;
|
||||
appearance:none;
|
||||
cursor:pointer;
|
||||
left:-100%;
|
||||
top:-100%;
|
||||
}
|
||||
[id*='toggle'] + label {
|
||||
cursor:pointer;
|
||||
text-align: left;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
cursor: pointer;
|
||||
transition:all 500ms ease;
|
||||
}
|
||||
[id*='toggle'toggle'toggle'] + label div {
|
||||
transition:all 500ms ease;
|
||||
}
|
||||
[id*='toggle'toggle'toggle'] + label div:after {
|
||||
content:'\002b'; /* open */
|
||||
text-align: left;
|
||||
float: left;
|
||||
}
|
||||
[id*='toggle'toggle'toggle']:checked + label div:after {
|
||||
content:'\2212'; /* close */
|
||||
text-align: left;
|
||||
float: left;
|
||||
}
|
||||
#msgscope label div {
|
||||
-webkit-transition: all 0.1s ease-in-out;
|
||||
-moz-transition: all 0.1s ease-in-out;
|
||||
-ms-transition: all 0.1s ease-in-out;
|
||||
-o-transition: all 0.1s ease-in-out;
|
||||
transition: all 0.1s ease-in-out;
|
||||
margin: 0 auto;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
background-image: none;
|
||||
white-space: nowrap;
|
||||
font-size: 0px;
|
||||
line-height: 1.42857143;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
[id*='toggle'] .container, [class*='toggle'] .container {
|
||||
transition: margin 300ms cubic-bezier(0.17, 0.04, 0.03, 0.94);
|
||||
}
|
||||
[id*='toggle'] {
|
||||
visibility: hidden;
|
||||
appearance:none;
|
||||
cursor:pointer;
|
||||
left:-100%;
|
||||
top:-100%;
|
||||
}
|
||||
[id*='toggle'] + label {
|
||||
cursor:pointer;
|
||||
text-align: left;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
cursor: pointer;
|
||||
transition:all 500ms ease;
|
||||
}
|
||||
[id*='toggle'] + label div {
|
||||
transition:all 500ms ease;
|
||||
}
|
||||
[id*='toggle'] + label div:after {
|
||||
content:'\002b'; /* open */
|
||||
text-align: left;
|
||||
float: left;
|
||||
}
|
||||
[id*='toggle']:checked + label div:after {
|
||||
content:'\2212'; /* close */
|
||||
text-align: left;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.nav [id*='toggle'] + label div:after {
|
||||
content:' '; /* open */
|
||||
}
|
||||
.nav [id*='toggle']:checked + label div:after {
|
||||
content:' '; /* close */
|
||||
}
|
||||
|
||||
[id*='toggle']:checked ~ .container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[id*='toggle']:checked ~ .toggle-inside {
|
||||
display: block;
|
||||
}
|
||||
.toggle-msgScope div[class*='toggle-inside'] {
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.containerHeader {
|
||||
overflow: auto;
|
||||
}
|
||||
|
@ -1026,6 +912,14 @@ div.container {
|
|||
font-size: var(--font-size);
|
||||
line-height: var(--line-spacing);
|
||||
}
|
||||
.containerHeader {
|
||||
border: var(--border-width-header) solid var(--border-color);
|
||||
background-color: var(--main-bg-color);
|
||||
border-radius: var(--timeline-border-radius);
|
||||
padding: var(--header-button-padding);
|
||||
margin: var(--vertical-between-posts-header);
|
||||
transform: translateY(var(--header-vertical-offset));
|
||||
}
|
||||
.container {
|
||||
border: var(--border-width) solid var(--border-color);
|
||||
background-color: var(--main-bg-color);
|
||||
|
@ -1040,11 +934,13 @@ div.container {
|
|||
background-color: var(--column-left-header-background);
|
||||
color: var(--column-left-header-color);
|
||||
font-size: var(--column-left-header-size);
|
||||
text-transform: uppercase;
|
||||
text-transform: var(--column-left-header-style);
|
||||
padding: 4px;
|
||||
border: none;
|
||||
}
|
||||
.newswireItem {
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
font-size: var(--font-size-newswire);
|
||||
color: var(--column-right-fg-color);
|
||||
line-height: var(--line-spacing-newswire);
|
||||
|
@ -1102,6 +998,7 @@ div.container {
|
|||
width: var(--column-left-width);
|
||||
}
|
||||
.col-left {
|
||||
border: var(--column-left-border-width) solid var(--column-left-border-color);
|
||||
color: var(--column-left-fg-color);
|
||||
font-size: var(--font-size-links);
|
||||
float: left;
|
||||
|
@ -1109,7 +1006,6 @@ div.container {
|
|||
}
|
||||
.col-left img.leftColEdit {
|
||||
background: var(--column-left-color);
|
||||
margin: 40px 0;
|
||||
width: var(--column-left-icon-size);
|
||||
}
|
||||
.col-left img.leftColEditImage {
|
||||
|
@ -1142,6 +1038,7 @@ div.container {
|
|||
overflow: hidden;
|
||||
}
|
||||
.col-right {
|
||||
border: var(--column-right-border-width) solid var(--column-left-border-color);
|
||||
background-color: var(--column-left-color);
|
||||
color: var(--column-left-fg-color);
|
||||
font-size: var(--font-size-links);
|
||||
|
@ -1150,7 +1047,6 @@ div.container {
|
|||
}
|
||||
.col-right img.rightColEdit {
|
||||
background: var(--column-left-color);
|
||||
margin: 40px 0;
|
||||
width: var(--column-right-icon-size);
|
||||
}
|
||||
.col-right img.rightColEditImage {
|
||||
|
@ -1605,40 +1501,6 @@ div.container {
|
|||
border-radius: 1%;
|
||||
width: 15%;
|
||||
}
|
||||
#msgscope label img {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
padding: 0px 0px;
|
||||
}
|
||||
.toggle-msgScope img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0px 0px;
|
||||
}
|
||||
.dropdown-menutoggle {
|
||||
-webkit-margin-start: 0px;
|
||||
-webkit-margin-end: 0px;
|
||||
-webkit-padding-start: 40px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 21px;
|
||||
width: 300%;
|
||||
min-width: 100%;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 0 17px !important;
|
||||
margin: 2px 0 0 !important;
|
||||
font-size: var(--font-size2);
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
color: var(--dropdown-fg-color);
|
||||
background-color: var(--dropdown-bg-color);
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
input[type=checkbox]
|
||||
{
|
||||
-ms-transform: scale(2);
|
||||
|
@ -1695,6 +1557,14 @@ div.container {
|
|||
font-size: var(--font-size);
|
||||
line-height: var(--line-spacing);
|
||||
}
|
||||
.containerHeader {
|
||||
border: var(--border-width-header) solid var(--border-color);
|
||||
background-color: var(--main-bg-color);
|
||||
border-radius: var(--timeline-border-radius);
|
||||
padding: var(--header-button-padding);
|
||||
margin: var(--vertical-between-posts-header);
|
||||
transform: translateY(0%);
|
||||
}
|
||||
.container {
|
||||
border: var(--border-width) solid var(--border-color);
|
||||
background-color: var(--main-bg-color);
|
||||
|
@ -1709,7 +1579,7 @@ div.container {
|
|||
background-color: var(--column-left-header-background);
|
||||
color: var(--column-left-header-color);
|
||||
font-size: var(--column-left-header-size-mobile);
|
||||
text-transform: uppercase;
|
||||
text-transform: var(--column-left-header-style);
|
||||
padding: 4px;
|
||||
border: none;
|
||||
}
|
||||
|
@ -1740,6 +1610,8 @@ div.container {
|
|||
padding: 0 0;
|
||||
}
|
||||
.newswireItem {
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
font-size: var(--font-size-newswire-mobile);
|
||||
color: var(--column-right-fg-color);
|
||||
line-height: var(--line-spacing-newswire);
|
||||
|
@ -2262,41 +2134,6 @@ div.container {
|
|||
border-radius: 1%;
|
||||
width: 25%;
|
||||
}
|
||||
#msgscope label img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
padding: 0px 0px;
|
||||
}
|
||||
.toggle-msgScope img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: -15px 0px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
.dropdown-menutoggle {
|
||||
-webkit-margin-start: 0px;
|
||||
-webkit-margin-end: 0px;
|
||||
-webkit-padding-start: 40px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 21px;
|
||||
width: 460%;
|
||||
min-width: 100%;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 0 17px !important;
|
||||
margin: 2px 0 0 !important;
|
||||
font-size: var(--font-size3);
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
color: var(--dropdown-fg-color);
|
||||
background-color: var(--dropdown-bg-color);
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
input[type=checkbox]
|
||||
{
|
||||
-ms-transform: scale(4);
|
||||
|
|
11
follow.py
|
@ -205,10 +205,11 @@ def unfollowPerson(baseDir: str, nickname: str, domain: str,
|
|||
return
|
||||
with open(filename, "r") as f:
|
||||
lines = f.readlines()
|
||||
with open(filename, 'w+') as f:
|
||||
for line in lines:
|
||||
if line.strip("\n").strip("\r").lower() != handleToUnfollowLower:
|
||||
f.write(line)
|
||||
with open(filename, 'w+') as f:
|
||||
for line in lines:
|
||||
if line.strip("\n").strip("\r").lower() != \
|
||||
handleToUnfollowLower:
|
||||
f.write(line)
|
||||
|
||||
# write to an unfollowed file so that if a follow accept
|
||||
# later arrives then it can be ignored
|
||||
|
@ -216,7 +217,7 @@ def unfollowPerson(baseDir: str, nickname: str, domain: str,
|
|||
if os.path.isfile(unfollowedFilename):
|
||||
if handleToUnfollowLower not in \
|
||||
open(unfollowedFilename).read().lower():
|
||||
with open(filename, "a+") as f:
|
||||
with open(unfollowedFilename, "a+") as f:
|
||||
f.write(handleToUnfollow + '\n')
|
||||
else:
|
||||
with open(unfollowedFilename, "w+") as f:
|
||||
|
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1019 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 43 KiB |
|
@ -725,8 +725,9 @@ def runNewswireDaemon(baseDir: str, httpd,
|
|||
mergeWithPreviousNewswire(httpd.newswire, newNewswire)
|
||||
|
||||
httpd.newswire = newNewswire
|
||||
saveJson(httpd.newswire, newswireStateFilename)
|
||||
print('Newswire updated')
|
||||
if newNewswire:
|
||||
saveJson(httpd.newswire, newswireStateFilename)
|
||||
print('Newswire updated')
|
||||
|
||||
convertRSStoActivityPub(baseDir,
|
||||
httpPrefix, domain, port,
|
||||
|
|
25
posts.py
|
@ -1228,6 +1228,31 @@ def createBlogPost(baseDir: str,
|
|||
schedulePost,
|
||||
eventDate, eventTime, location)
|
||||
blog['object']['type'] = 'Article'
|
||||
|
||||
# append citations tags, stored in a file
|
||||
citationsFilename = \
|
||||
baseDir + '/accounts/' + \
|
||||
nickname + '@' + domain + '/.citations.txt'
|
||||
if os.path.isfile(citationsFilename):
|
||||
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
|
||||
# dateStr = sections[0]
|
||||
title = sections[1]
|
||||
link = sections[2]
|
||||
tagJson = {
|
||||
"type": "Article",
|
||||
"name": title,
|
||||
"url": link
|
||||
}
|
||||
blog['object']['tag'].append(tagJson)
|
||||
|
||||
return blog
|
||||
|
||||
|
||||
|
|
1
tests.py
|
@ -1678,7 +1678,6 @@ def testWebLinks():
|
|||
'some.incredibly.long.and.annoying.word.which.should.be.removed: ' + \
|
||||
'The remaining text'
|
||||
resultText = removeLongWords(exampleText, 40, [])
|
||||
print('resultText: ' + resultText)
|
||||
assert resultText == \
|
||||
'some.incredibly.long.and.annoying.word.w\n' + \
|
||||
'hich.should.be.removed: The remaining text'
|
||||
|
|
70
theme.py
|
@ -313,7 +313,8 @@ def setThemeDefault(baseDir: str):
|
|||
}
|
||||
themeParams = {
|
||||
"banner-height": "20vh",
|
||||
"banner-height-mobile": "10vh"
|
||||
"banner-height-mobile": "10vh",
|
||||
"search-banner-height-mobile": "15vh"
|
||||
}
|
||||
setThemeFromDict(baseDir, name, themeParams, bgParams)
|
||||
|
||||
|
@ -462,6 +463,11 @@ def setThemeNight(baseDir: str):
|
|||
fontStrItalic = \
|
||||
"url('./fonts/solidaric-italic.woff2') format('woff2')"
|
||||
themeParams = {
|
||||
"main-visited-color": "#0481f5",
|
||||
"post-separator-margin-top": "9%",
|
||||
"post-separator-margin-bottom": "9%",
|
||||
"post-separator-width": "80%",
|
||||
"post-separator-height": "10%",
|
||||
"column-left-header-background": "#07447c",
|
||||
"banner-height": "15vh",
|
||||
"banner-height-mobile": "10vh",
|
||||
|
@ -476,7 +482,7 @@ def setThemeNight(baseDir: str):
|
|||
"column-left-color": "#0f0d10",
|
||||
"text-entry-background": "#0f0d10",
|
||||
"link-bg-color": "#0f0d10",
|
||||
"main-link-color": "ff9900",
|
||||
"main-link-color": "#6481f5",
|
||||
"main-link-color-hover": "#d09338",
|
||||
"main-fg-color": "#0481f5",
|
||||
"column-left-fg-color": "#0481f5",
|
||||
|
@ -600,6 +606,7 @@ def setThemeHenge(baseDir: str):
|
|||
setRssIconAtTop(baseDir, True)
|
||||
setPublishButtonAtTop(baseDir, False)
|
||||
themeParams = {
|
||||
"banner-height": "25vh",
|
||||
"column-left-image-width-mobile": "40vw",
|
||||
"column-right-image-width-mobile": "40vw",
|
||||
"font-size-button-mobile": "26px",
|
||||
|
@ -1019,14 +1026,49 @@ def setThemeIndymediaModern(baseDir: str):
|
|||
fontStrItalic = \
|
||||
"url('./fonts/NimbusSanL-italic.otf') format('opentype')"
|
||||
themeParams = {
|
||||
"follow-text-size1": "14px",
|
||||
"follow-text-size2": "30px",
|
||||
"hashtag-size1": "20px",
|
||||
"hashtag-size2": "30px",
|
||||
"font-size-calendar-header": "2rem",
|
||||
"font-size-calendar-cell": "2rem",
|
||||
"calendar-horizontal-padding": "20%",
|
||||
"time-vertical-align": "10px",
|
||||
"header-vertical-offset": "-10%",
|
||||
"publish-button-vertical-offset": "0",
|
||||
"vertical-between-posts-header": "0 0",
|
||||
"header-button-padding": "0 0",
|
||||
"containericons-horizontal-spacing": "0%",
|
||||
"font-size-header": "14px",
|
||||
"font-size": "22px",
|
||||
"font-size2": "16px",
|
||||
"font-size3": "30px",
|
||||
"font-size4": "14px",
|
||||
"font-size5": "12px",
|
||||
"font-size-likes": "10px",
|
||||
"font-size-links": "12px",
|
||||
"font-size-newswire": "12px",
|
||||
"font-size-newswire-mobile": "30px",
|
||||
"font-size-dropdown-header": "30px",
|
||||
"post-separator-margin-top": "1%",
|
||||
"post-separator-margin-bottom": "1%",
|
||||
"post-separator-width": "95%",
|
||||
"post-separator-height": "1px",
|
||||
"column-left-border-width": "1px",
|
||||
"column-right-border-width": "0px",
|
||||
"column-left-border-color": "black",
|
||||
"column-left-header-color": "black",
|
||||
"column-left-header-background": "white",
|
||||
"column-left-header-style": "none",
|
||||
"search-banner-height": "15vh",
|
||||
"search-banner-height-mobile": "10vh",
|
||||
"publish-button-vertical-offset": "10px",
|
||||
"container-button-padding": "0px",
|
||||
"container-button-margin": "0px",
|
||||
"column-right-icon-size": "11%",
|
||||
"column-left-icon-size": "15%",
|
||||
"column-right-icon-size": "15%",
|
||||
"button-height-padding": "5px",
|
||||
"icon-brightness-change": "70%",
|
||||
"border-width": "0px",
|
||||
"border-width-header": "0px",
|
||||
"tab-border-width": "3px",
|
||||
"tab-border-color": "grey",
|
||||
|
@ -1034,8 +1076,8 @@ def setThemeIndymediaModern(baseDir: str):
|
|||
"login-button-color": "#25408f",
|
||||
"login-button-fg-color": "white",
|
||||
"column-left-width": "10vw",
|
||||
"column-center-width": "70vw",
|
||||
"column-right-width": "20vw",
|
||||
"column-center-width": "80vw",
|
||||
"column-right-width": "10vw",
|
||||
"column-right-fg-color": "#25408f",
|
||||
"column-right-fg-color-voted-on": "red",
|
||||
"newswire-item-moderated-color": "red",
|
||||
|
@ -1054,12 +1096,7 @@ def setThemeIndymediaModern(baseDir: str):
|
|||
"hashtag-background-color": "#b2b2b2",
|
||||
"focus-color": "grey",
|
||||
"font-size-button-mobile": "26px",
|
||||
"font-size-publish-button": "26px",
|
||||
"font-size": "32px",
|
||||
"font-size2": "26px",
|
||||
"font-size3": "40px",
|
||||
"font-size4": "24px",
|
||||
"font-size5": "22px",
|
||||
"font-size-publish-button": "14px",
|
||||
"rgba(0, 0, 0, 0.5)": "rgba(0, 0, 0, 0.0)",
|
||||
"column-left-color": "white",
|
||||
"main-bg-color": "white",
|
||||
|
@ -1108,7 +1145,7 @@ def setThemeIndymediaModern(baseDir: str):
|
|||
}
|
||||
setThemeFromDict(baseDir, name, themeParams, bgParams)
|
||||
setNewswirePublishAsIcon(baseDir, False)
|
||||
setFullWidthTimelineButtonHeader(baseDir, True)
|
||||
setFullWidthTimelineButtonHeader(baseDir, False)
|
||||
setIconsAsButtons(baseDir, True)
|
||||
setRssIconAtTop(baseDir, False)
|
||||
setPublishButtonAtTop(baseDir, True)
|
||||
|
@ -1117,8 +1154,15 @@ def setThemeIndymediaModern(baseDir: str):
|
|||
def setThemeSolidaric(baseDir: str):
|
||||
name = 'solidaric'
|
||||
themeParams = {
|
||||
"button-corner-radius": "5px",
|
||||
"column-left-icons-margin": "15px",
|
||||
"post-separator-width": "96.5%",
|
||||
"post-separator-height": "40px",
|
||||
"border-width-header": "0",
|
||||
"border-width": "0",
|
||||
"banner-height": "35vh",
|
||||
"banner-height-mobile": "15vh",
|
||||
"search-banner-height-mobile": "15vh",
|
||||
"time-vertical-align": "-4px",
|
||||
"time-vertical-align-mobile": "15px",
|
||||
"hashtag-background-color": "lightred",
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "إزالة مشاركات Twitter",
|
||||
"Sensitive": "حساس",
|
||||
"Word Replacements": "استبدال الكلمات",
|
||||
"Happening Today": "يحدث اليوم",
|
||||
"Happening This Week": "يحدث هذا الاسبوع",
|
||||
"Happening Today": "اليوم",
|
||||
"Happening This Week": "هكذا",
|
||||
"Blog": "مدونة",
|
||||
"Blogs": "المدونات",
|
||||
"Title": "عنوان",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "ميزات",
|
||||
"Article": "مقال إخباري",
|
||||
"Create an article": "قم بإنشاء مقال",
|
||||
"Settings": "إعدادات"
|
||||
"Settings": "إعدادات",
|
||||
"Citations": "اقتباسات",
|
||||
"Choose newswire items referenced in your article": "اختر العناصر الإخبارية المشار إليها في مقالتك"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Elimina les publicacions de Twitter",
|
||||
"Sensitive": "Sensible",
|
||||
"Word Replacements": "Substitucions de paraula",
|
||||
"Happening Today": "Passant avui",
|
||||
"Happening This Week": "Passa aquesta setmana",
|
||||
"Happening Today": "Avui",
|
||||
"Happening This Week": "Aviat",
|
||||
"Blog": "Bloc",
|
||||
"Blogs": "Blocs",
|
||||
"Title": "Títol",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Article",
|
||||
"Article": "Reportatge",
|
||||
"Create an article": "Creeu un article",
|
||||
"Settings": "Configuració"
|
||||
"Settings": "Configuració",
|
||||
"Citations": "Cites",
|
||||
"Choose newswire items referenced in your article": "Trieu articles de newswire als quals faci referència el vostre article"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Dileu postiadau Twitter",
|
||||
"Sensitive": "Sensitif",
|
||||
"Word Replacements": "Amnewidiadau Geiriau",
|
||||
"Happening Today": "Digwydd Heddiw",
|
||||
"Happening This Week": "Yn Digwydd Yr Wythnos Hon",
|
||||
"Happening Today": "Heddiw",
|
||||
"Happening This Week": "Yn fuan",
|
||||
"Blog": "Blog",
|
||||
"Blogs": "Blogs",
|
||||
"Title": "Teitl",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Nodweddion",
|
||||
"Article": "Erthygl",
|
||||
"Create an article": "Creu erthygl",
|
||||
"Settings": "Gosodiadau"
|
||||
"Settings": "Gosodiadau",
|
||||
"Citations": "Dyfyniadau",
|
||||
"Choose newswire items referenced in your article": "Dewiswch eitemau newyddion y cyfeirir atynt yn eich erthygl"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Entfernen Sie Twitter-Posts",
|
||||
"Sensitive": "Empfindlich",
|
||||
"Word Replacements": "Wortersetzungen",
|
||||
"Happening Today": "Heute passiert",
|
||||
"Happening This Week": "Diese Woche passiert",
|
||||
"Happening Today": "Heute",
|
||||
"Happening This Week": "Demnächst",
|
||||
"Blog": "Blog",
|
||||
"Blogs": "Blogs",
|
||||
"Title": "Titel",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Eigenschaften",
|
||||
"Article": "Artikel",
|
||||
"Create an article": "Erstellen Sie einen Artikel",
|
||||
"Settings": "Einstellungen"
|
||||
"Settings": "Einstellungen",
|
||||
"Citations": "Zitate",
|
||||
"Choose newswire items referenced in your article": "Wählen Sie Newswire-Artikel aus, auf die in Ihrem Artikel verwiesen wird"
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@
|
|||
"Sensitive": "Sensitive",
|
||||
"Word Replacements": "Word Replacements",
|
||||
"Happening Today": "Today",
|
||||
"Happening This Week": "This Week",
|
||||
"Happening This Week": "Soon",
|
||||
"Blog": "Blog",
|
||||
"Blogs": "Blogs",
|
||||
"Title": "Title",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Features",
|
||||
"Article": "Article",
|
||||
"Create an article": "Create an article",
|
||||
"Settings": "Settings"
|
||||
"Settings": "Settings",
|
||||
"Citations": "Citations",
|
||||
"Choose newswire items referenced in your article": "Choose newswire items referenced in your article"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Eliminar publicaciones de Twitter",
|
||||
"Sensitive": "Sensible",
|
||||
"Word Replacements": "Reemplazos de palabras",
|
||||
"Happening Today": "Sucediendo hoy",
|
||||
"Happening This Week": "Sucediendo esta semana",
|
||||
"Happening Today": "Hoy",
|
||||
"Happening This Week": "Pronto",
|
||||
"Blog": "Blog",
|
||||
"Blogs": "Blogs",
|
||||
"Title": "Título",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Caracteristicas",
|
||||
"Article": "Artículo",
|
||||
"Create an article": "Crea un articulo",
|
||||
"Settings": "Configuraciones"
|
||||
"Settings": "Configuraciones",
|
||||
"Citations": "Citas",
|
||||
"Choose newswire items referenced in your article": "Elija elementos de Newswire a los que se hace referencia en su artículo"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Supprimer les messages Twitter",
|
||||
"Sensitive": "Sensible",
|
||||
"Word Replacements": "Remplacements de mots",
|
||||
"Happening Today": "Se passe aujourd'hui",
|
||||
"Happening This Week": "Se passe cette semaine",
|
||||
"Happening Today": "Aujourd'hui",
|
||||
"Happening This Week": "Bientôt",
|
||||
"Blog": "Blog",
|
||||
"Blogs": "Blogs",
|
||||
"Title": "Titre",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Traits",
|
||||
"Article": "Article",
|
||||
"Create an article": "Créer un article",
|
||||
"Settings": "Réglages"
|
||||
"Settings": "Réglages",
|
||||
"Citations": "Citations",
|
||||
"Choose newswire items referenced in your article": "Choisissez les éléments de fil d'actualité référencés dans votre article"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Bain poist Twitter",
|
||||
"Sensitive": "Íogair",
|
||||
"Word Replacements": "Athchur Focal",
|
||||
"Happening Today": "Ag tarlú inniu",
|
||||
"Happening This Week": "Ag tarlú an tseachtain seo",
|
||||
"Happening Today": "Inniu",
|
||||
"Happening This Week": "Go gairid",
|
||||
"Blog": "Blag",
|
||||
"Blogs": "Blaganna",
|
||||
"Title": "Teideal",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Gnéithe",
|
||||
"Article": "Airteagal",
|
||||
"Create an article": "Cruthaigh alt",
|
||||
"Settings": "Socruithe"
|
||||
"Settings": "Socruithe",
|
||||
"Citations": "Citations",
|
||||
"Choose newswire items referenced in your article": "Roghnaigh míreanna sreanga nuachta dá dtagraítear i d’alt"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "ट्विटर पोस्ट हटाएं",
|
||||
"Sensitive": "संवेदनशील",
|
||||
"Word Replacements": "शब्द प्रतिस्थापन",
|
||||
"Happening Today": "आज हो रहा है",
|
||||
"Happening This Week": "इस सप्ताह हो रहा है",
|
||||
"Happening Today": "आज",
|
||||
"Happening This Week": "जल्द ही",
|
||||
"Blog": "ब्लॉग",
|
||||
"Blogs": "ब्लॉग",
|
||||
"Title": "शीर्षक",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "विशेषताएं",
|
||||
"Article": "लेख",
|
||||
"Create an article": "एक लेख बनाएँ",
|
||||
"Settings": "समायोजन"
|
||||
"Settings": "समायोजन",
|
||||
"Citations": "उद्धरण",
|
||||
"Choose newswire items referenced in your article": "अपने लेख में संदर्भित newswire आइटम चुनें"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Rimuovi i post di Twitter",
|
||||
"Sensitive": "Sensibile",
|
||||
"Word Replacements": "Sostituzioni di parole",
|
||||
"Happening Today": "Succede oggi",
|
||||
"Happening This Week": "Succede questa settimana",
|
||||
"Happening Today": "Oggi",
|
||||
"Happening This Week": "Presto",
|
||||
"Blog": "Blog",
|
||||
"Blogs": "Blog",
|
||||
"Title": "Titolo",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Caratteristiche",
|
||||
"Article": "Articolo",
|
||||
"Create an article": "Crea un articolo",
|
||||
"Settings": "impostazioni"
|
||||
"Settings": "impostazioni",
|
||||
"Citations": "Citazioni",
|
||||
"Choose newswire items referenced in your article": "Scegli gli articoli del newswire a cui fa riferimento il tuo articolo"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Twitterの投稿を削除する",
|
||||
"Sensitive": "敏感",
|
||||
"Word Replacements": "単語の置換",
|
||||
"Happening Today": "今日の出来事",
|
||||
"Happening This Week": "今週の出来事",
|
||||
"Happening Today": "今日",
|
||||
"Happening This Week": "すぐに",
|
||||
"Blog": "ブログ",
|
||||
"Blogs": "ブログ",
|
||||
"Title": "題名",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "特徴",
|
||||
"Article": "論文",
|
||||
"Create an article": "記事を作成する",
|
||||
"Settings": "設定"
|
||||
"Settings": "設定",
|
||||
"Citations": "引用",
|
||||
"Choose newswire items referenced in your article": "あなたの記事で参照されているニュースワイヤーアイテムを選択してください"
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@
|
|||
"Sensitive": "Sensitive",
|
||||
"Word Replacements": "Word Replacements",
|
||||
"Happening Today": "Happening Today",
|
||||
"Happening This Week": "Happening This Week",
|
||||
"Happening This Week": "Soon",
|
||||
"Blog": "Blog",
|
||||
"Blogs": "Blogs",
|
||||
"Title": "Title",
|
||||
|
@ -321,5 +321,7 @@
|
|||
"Features" : "Features",
|
||||
"Article": "Article",
|
||||
"Create an article": "Create an article",
|
||||
"Settings": "Settings"
|
||||
"Settings": "Settings",
|
||||
"Citations": "Citations",
|
||||
"Choose newswire items referenced in your article": "Choose newswire items referenced in your article"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Remover postagens do Twitter",
|
||||
"Sensitive": "Sensível",
|
||||
"Word Replacements": "Substituições do Word",
|
||||
"Happening Today": "Acontecendo hoje",
|
||||
"Happening This Week": "Acontecendo Esta Semana",
|
||||
"Happening Today": "Hoje",
|
||||
"Happening This Week": "Em breve",
|
||||
"Blog": "Blog",
|
||||
"Blogs": "Blogs",
|
||||
"Title": "Título",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "Recursos",
|
||||
"Article": "Artigo",
|
||||
"Create an article": "Crie um artigo",
|
||||
"Settings": "Definições"
|
||||
"Settings": "Definições",
|
||||
"Citations": "Citações",
|
||||
"Choose newswire items referenced in your article": "Escolha os itens de notícias mencionados em seu artigo"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "Удалить сообщения из Твиттера",
|
||||
"Sensitive": "чувствительный",
|
||||
"Word Replacements": "Замены слов",
|
||||
"Happening Today": "Происходит сегодня",
|
||||
"Happening This Week": "Происходит на этой неделе",
|
||||
"Happening Today": "Cегодня",
|
||||
"Happening This Week": "Скоро",
|
||||
"Blog": "Блог",
|
||||
"Blogs": "Блоги",
|
||||
"Title": "заглавие",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "особенности",
|
||||
"Article": "Статья",
|
||||
"Create an article": "Создать статью",
|
||||
"Settings": "Настройки"
|
||||
"Settings": "Настройки",
|
||||
"Citations": "Цитаты",
|
||||
"Choose newswire items referenced in your article": "Выберите элементы ленты новостей, на которые есть ссылки в вашей статье"
|
||||
}
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
"Remove Twitter posts": "删除Twitter帖子",
|
||||
"Sensitive": "敏感",
|
||||
"Word Replacements": "单词替换",
|
||||
"Happening Today": "今天发生",
|
||||
"Happening This Week": "本周发生",
|
||||
"Happening Today": "今天",
|
||||
"Happening This Week": "不久",
|
||||
"Blog": "博客",
|
||||
"Blogs": "网志",
|
||||
"Title": "标题",
|
||||
|
@ -325,5 +325,7 @@
|
|||
"Features" : "特征",
|
||||
"Article": "文章",
|
||||
"Create an article": "建立文章",
|
||||
"Settings": "设定值"
|
||||
"Settings": "设定值",
|
||||
"Citations": "引文",
|
||||
"Choose newswire items referenced in your article": "选择文章中引用的新闻专栏文章"
|
||||
}
|
||||
|
|
588
webinterface.py
|
@ -736,6 +736,7 @@ def htmlHashtagSearch(cssCache: {},
|
|||
return None
|
||||
|
||||
iconsDir = getIconsDir(baseDir)
|
||||
separatorStr = htmlPostSeparator(baseDir, None)
|
||||
|
||||
# check that the directory for the nickname exists
|
||||
if nickname:
|
||||
|
@ -829,7 +830,7 @@ def htmlHashtagSearch(cssCache: {},
|
|||
if nickname:
|
||||
showIndividualPostIcons = True
|
||||
allowDeletion = False
|
||||
hashtagSearchForm += \
|
||||
hashtagSearchForm += separatorStr + \
|
||||
individualPostAsHtml(True, recentPostsCache,
|
||||
maxRecentPosts,
|
||||
iconsDir, translate, None,
|
||||
|
@ -1165,6 +1166,7 @@ def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str,
|
|||
return historySearchForm
|
||||
|
||||
iconsDir = getIconsDir(baseDir)
|
||||
separatorStr = htmlPostSeparator(baseDir, None)
|
||||
|
||||
# ensure that the page number is in bounds
|
||||
if not pageNumber:
|
||||
|
@ -1191,7 +1193,7 @@ def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str,
|
|||
continue
|
||||
showIndividualPostIcons = True
|
||||
allowDeletion = False
|
||||
historySearchForm += \
|
||||
historySearchForm += separatorStr + \
|
||||
individualPostAsHtml(True, recentPostsCache,
|
||||
maxRecentPosts,
|
||||
iconsDir, translate, None,
|
||||
|
@ -1282,7 +1284,7 @@ def htmlEditLinks(cssCache: {}, translate: {}, baseDir: str, path: str,
|
|||
' <center>\n' + \
|
||||
' <input type="submit" name="submitLinks" value="' + \
|
||||
translate['Submit'] + '">\n' + \
|
||||
' <center>\n'
|
||||
' </center>\n'
|
||||
editLinksForm += \
|
||||
' </div>\n'
|
||||
|
||||
|
@ -1365,7 +1367,7 @@ def htmlEditNewswire(cssCache: {}, translate: {}, baseDir: str, path: str,
|
|||
' <center>\n' + \
|
||||
' <input type="submit" name="submitNewswire" value="' + \
|
||||
translate['Submit'] + '">\n' + \
|
||||
' <center>\n'
|
||||
' </center>\n'
|
||||
editNewswireForm += \
|
||||
' </div>\n'
|
||||
|
||||
|
@ -2413,6 +2415,111 @@ def htmlSuspended(cssCache: {}, baseDir: str) -> str:
|
|||
return suspendedForm
|
||||
|
||||
|
||||
def htmlNewPostDropDown(scopeIcon: str, scopeDescription: str,
|
||||
replyStr: str,
|
||||
translate: {},
|
||||
iconsDir: str,
|
||||
showPublicOnDropdown: bool,
|
||||
defaultTimeline: str,
|
||||
pathBase: str,
|
||||
dropdownNewPostSuffix: str,
|
||||
dropdownNewBlogSuffix: str,
|
||||
dropdownUnlistedSuffix: str,
|
||||
dropdownFollowersSuffix: str,
|
||||
dropdownDMSuffix: str,
|
||||
dropdownReminderSuffix: str,
|
||||
dropdownEventSuffix: str,
|
||||
dropdownReportSuffix: str) -> str:
|
||||
"""Returns the html for a drop down list of new post types
|
||||
"""
|
||||
dropDownContent = '<div class="newPostDropdown">\n'
|
||||
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="/' + \
|
||||
iconsDir + '/' + scopeIcon + '"/><b>' + \
|
||||
scopeDescription + '</b></label>\n'
|
||||
dropDownContent += ' <ul>\n'
|
||||
|
||||
if showPublicOnDropdown:
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + dropdownNewPostSuffix + \
|
||||
'"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_public.png"/><b>' + \
|
||||
translate['Public'] + '</b><br>' + \
|
||||
translate['Visible to anyone'] + '</a></li>\n'
|
||||
if defaultTimeline == 'tlnews':
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + dropdownNewBlogSuffix + \
|
||||
'"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_blog.png"/><b>' + \
|
||||
translate['Article'] + '</b><br>' + \
|
||||
translate['Create an article'] + '</a></li>\n'
|
||||
else:
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + dropdownNewBlogSuffix + \
|
||||
'"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/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="/' + \
|
||||
iconsDir + '/scope_unlisted.png"/><b>' + \
|
||||
translate['Unlisted'] + '</b><br>' + \
|
||||
translate['Not on public timeline'] + '</a></li>\n'
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + dropdownFollowersSuffix + \
|
||||
'"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_followers.png"/><b>' + \
|
||||
translate['Followers'] + '</b><br>' + \
|
||||
translate['Only to followers'] + '</a></li>\n'
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + dropdownDMSuffix + \
|
||||
'"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_dm.png"/><b>' + \
|
||||
translate['DM'] + '</b><br>' + \
|
||||
translate['Only to mentioned people'] + '</a></li>\n'
|
||||
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + dropdownReminderSuffix + \
|
||||
'"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_reminder.png"/><b>' + \
|
||||
translate['Reminder'] + '</b><br>' + \
|
||||
translate['Scheduled note to yourself'] + '</a></li>\n'
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + dropdownEventSuffix + \
|
||||
'"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_event.png"/><b>' + \
|
||||
translate['Event'] + '</b><br>' + \
|
||||
translate['Create an event'] + '</a></li>\n'
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + dropdownReportSuffix + \
|
||||
'"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_report.png"/><b>' + \
|
||||
translate['Report'] + '</b><br>' + \
|
||||
translate['Send to moderators'] + '</a></li>\n'
|
||||
|
||||
if not replyStr:
|
||||
dropDownContent += \
|
||||
'<li><a href="' + pathBase + \
|
||||
'/newshare"><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/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="/' + \
|
||||
iconsDir + '/scope_question.png"/><b>' + \
|
||||
translate['Question'] + '</b><br>' + \
|
||||
translate['Ask a question'] + '</a></li>\n'
|
||||
|
||||
dropDownContent += ' </ul>\n'
|
||||
dropDownContent += '</div>\n'
|
||||
return dropDownContent
|
||||
|
||||
|
||||
def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
||||
baseDir: str, httpPrefix: str,
|
||||
path: str, inReplyTo: str,
|
||||
|
@ -2420,7 +2527,7 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
|||
reportUrl: str, pageNumber: int,
|
||||
nickname: str, domain: str,
|
||||
domainFull: str,
|
||||
defaultTimeline: str) -> str:
|
||||
defaultTimeline: str, newswire: {}) -> str:
|
||||
"""New post screen
|
||||
"""
|
||||
iconsDir = getIconsDir(baseDir)
|
||||
|
@ -2643,6 +2750,33 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
|||
extraFields += '<input type="text" name="location">\n'
|
||||
extraFields += '</div>\n'
|
||||
|
||||
citationsStr = ''
|
||||
if endpoint == 'newblog':
|
||||
citationsFilename = \
|
||||
baseDir + '/accounts/' + \
|
||||
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 \
|
||||
|
@ -2791,21 +2925,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
|||
newPostForm += '<img loading="lazy" class="timeline-banner" src="' + \
|
||||
'/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
||||
|
||||
# only show the share option if this is not a reply
|
||||
shareOptionOnDropdown = ''
|
||||
questionOptionOnDropdown = ''
|
||||
if not replyStr:
|
||||
shareOptionOnDropdown = \
|
||||
' <a href="' + pathBase + \
|
||||
'/newshare"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_share.png"/><b>' + translate['Shares'] + \
|
||||
'</b><br>' + translate['Describe a shared item'] + '</li></a>\n'
|
||||
questionOptionOnDropdown = \
|
||||
' <a href="' + pathBase + \
|
||||
'/newquestion"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_question.png"/><b>' + translate['Question'] + \
|
||||
'</b><br>' + translate['Ask a question'] + '</li></a>\n'
|
||||
|
||||
mentionsStr = ''
|
||||
for m in mentions:
|
||||
mentionNickname = getNicknameFromActor(m)
|
||||
|
@ -2858,89 +2977,22 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
|||
|
||||
dropDownContent = ''
|
||||
if not reportUrl:
|
||||
dropDownContent += "<div class='msgscope-collapse collapse "
|
||||
dropDownContent += "right desktoponly' id='msgscope'>\n"
|
||||
dropDownContent += " <ul class='nav msgscope-nav msgscope-right'>\n"
|
||||
dropDownContent += " <li class=' ' style='position: relative;'>\n"
|
||||
dropDownContent += " <div class='toggle-msgScope button-msgScope'>\n"
|
||||
dropDownContent += " <input id='toggleMsgScope' "
|
||||
dropDownContent += "name='toggleMsgScope' type='checkbox'/>\n"
|
||||
dropDownContent += " <label for='toggleMsgScope'>\n"
|
||||
dropDownContent += " <div class='lined-thin'>\n"
|
||||
dropDownContent += ' <img loading="lazy" alt="" title="" src="/'
|
||||
dropDownContent += iconsDir + '/' + scopeIcon
|
||||
dropDownContent += '"/><b class="scope-desc">'
|
||||
dropDownContent += scopeDescription + '</b>\n'
|
||||
dropDownContent += " <span class='caret'/>\n"
|
||||
dropDownContent += " </div>\n"
|
||||
dropDownContent += " </label>\n"
|
||||
dropDownContent += " <div class='toggle-inside'>\n"
|
||||
dropDownContent += " <ul aria-labelledby='dropdownMsgScope' "
|
||||
dropDownContent += "class='dropdown-menutoggle'>\n"
|
||||
|
||||
if showPublicOnDropdown:
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownNewPostSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_public.png"/><b>' + \
|
||||
translate['Public'] + '</b><br>' + \
|
||||
translate['Visible to anyone'] + '</li></a>\n'
|
||||
if defaultTimeline == 'tlnews':
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownNewBlogSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_blog.png"/><b>' + \
|
||||
translate['Article'] + '</b><br>' + \
|
||||
translate['Create an article'] + '</li></a>\n'
|
||||
else:
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownNewBlogSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_blog.png"/><b>' + \
|
||||
translate['Blog'] + '</b><br>' + \
|
||||
translate['Publicly visible post'] + '</li></a>\n'
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownUnlistedSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir+'/scope_unlisted.png"/><b>' + \
|
||||
translate['Unlisted'] + '</b><br>' + \
|
||||
translate['Not on public timeline'] + '</li></a>\n'
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownFollowersSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_followers.png"/><b>' + \
|
||||
translate['Followers'] + '</b><br>' + \
|
||||
translate['Only to followers'] + '</li></a>\n'
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownDMSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_dm.png"/><b>' + translate['DM'] + \
|
||||
'</b><br>' + translate['Only to mentioned people'] + \
|
||||
'</li></a>\n'
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownReminderSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_reminder.png"/><b>' + translate['Reminder'] + \
|
||||
'</b><br>' + translate['Scheduled note to yourself'] + \
|
||||
'</li></a>\n'
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownEventSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + \
|
||||
iconsDir + '/scope_event.png"/><b>' + translate['Event'] + \
|
||||
'</b><br>' + translate['Create an event'] + \
|
||||
'</li></a>\n'
|
||||
dropDownContent += " " \
|
||||
'<a href="' + pathBase + dropdownReportSuffix + \
|
||||
'"><li><img loading="lazy" alt="" title="" src="/' + iconsDir + \
|
||||
'/scope_report.png"/><b>' + translate['Report'] + \
|
||||
'</b><br>' + translate['Send to moderators'] + '</li></a>\n'
|
||||
dropDownContent += questionOptionOnDropdown + shareOptionOnDropdown
|
||||
dropDownContent += ' </ul>\n'
|
||||
dropDownContent += ' </div>\n'
|
||||
dropDownContent += ' </div>\n'
|
||||
dropDownContent += ' </li>\n'
|
||||
dropDownContent += ' </ul>\n'
|
||||
dropDownContent += '</div>\n'
|
||||
dropDownContent = \
|
||||
htmlNewPostDropDown(scopeIcon, scopeDescription,
|
||||
replyStr,
|
||||
translate,
|
||||
iconsDir,
|
||||
showPublicOnDropdown,
|
||||
defaultTimeline,
|
||||
pathBase,
|
||||
dropdownNewPostSuffix,
|
||||
dropdownNewBlogSuffix,
|
||||
dropdownUnlistedSuffix,
|
||||
dropdownFollowersSuffix,
|
||||
dropdownDMSuffix,
|
||||
dropdownReminderSuffix,
|
||||
dropdownEventSuffix,
|
||||
dropdownReportSuffix)
|
||||
else:
|
||||
mentionsStr = 'Re: ' + reportUrl + '\n\n' + mentionsStr
|
||||
|
||||
|
@ -2966,13 +3018,22 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
|||
newPostForm += ' </div>\n'
|
||||
|
||||
newPostForm += ' <div class="containerSubmitNewPost"><center>\n'
|
||||
|
||||
# newPostForm += \
|
||||
# ' <a href="' + pathBase + \
|
||||
# '/inbox"><button class="cancelbtn">' + \
|
||||
# translate['Go Back'] + '</button></a>\n'
|
||||
|
||||
# for a new blog if newswire items exist then add a citations button
|
||||
if newswire and path.endswith('/newblog'):
|
||||
newPostForm += \
|
||||
' <input type="submit" name="submitCitations" value="' + \
|
||||
translate['Citations'] + '">\n'
|
||||
|
||||
newPostForm += \
|
||||
' <input type="submit" name="submitPost" value="' + \
|
||||
translate['Submit'] + '">\n'
|
||||
|
||||
newPostForm += ' </center></div>\n'
|
||||
|
||||
newPostForm += replyStr
|
||||
|
@ -3018,7 +3079,7 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
|||
newPostForm += \
|
||||
' <textarea id="message" name="message" style="height:' + \
|
||||
str(messageBoxHeight) + 'px"' + selectedStr + '></textarea>\n'
|
||||
newPostForm += extraFields+dateAndLocation
|
||||
newPostForm += extraFields + citationsStr + dateAndLocation
|
||||
if not mediaInstance or replyStr:
|
||||
newPostForm += newPostImageSection
|
||||
newPostForm += ' </div>\n'
|
||||
|
@ -3079,6 +3140,7 @@ def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int,
|
|||
These should only be public posts
|
||||
"""
|
||||
iconsDir = getIconsDir(baseDir)
|
||||
separatorStr = htmlPostSeparator(baseDir, None)
|
||||
profileStr = ''
|
||||
maxItems = 4
|
||||
ctr = 0
|
||||
|
@ -3111,7 +3173,7 @@ def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int,
|
|||
showPublishedDateOnly,
|
||||
False, False, False, True, False)
|
||||
if postStr:
|
||||
profileStr += postStr
|
||||
profileStr += separatorStr + postStr
|
||||
ctr += 1
|
||||
if ctr >= maxItems:
|
||||
break
|
||||
|
@ -3329,6 +3391,7 @@ def htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int,
|
|||
'" alt="' + translate['Page up'] + '"></a>\n' + \
|
||||
' </center>\n'
|
||||
|
||||
separatorStr = htmlPostSeparator(baseDir, None)
|
||||
for published, item in sharesJson.items():
|
||||
showContactButton = False
|
||||
if item['actor'] != actor:
|
||||
|
@ -3336,7 +3399,7 @@ def htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int,
|
|||
showRemoveButton = False
|
||||
if item['actor'] == actor:
|
||||
showRemoveButton = True
|
||||
timelineStr += \
|
||||
timelineStr += separatorStr + \
|
||||
htmlIndividualShare(actor, item, translate,
|
||||
showContactButton, showRemoveButton)
|
||||
|
||||
|
@ -5524,6 +5587,29 @@ def individualPostAsHtml(allowDownloads: bool,
|
|||
'<div class="gitpatch"><pre><code>' + contentStr + \
|
||||
'</code></pre></div>\n'
|
||||
|
||||
# show blog citations
|
||||
citationsStr = ''
|
||||
if boxName == 'tlblog':
|
||||
if postJsonObject['object'].get('tag'):
|
||||
for tagJson in postJsonObject['object']['tag']:
|
||||
if not isinstance(tagJson, dict):
|
||||
continue
|
||||
if not tagJson.get('type'):
|
||||
continue
|
||||
if tagJson['type'] != 'Article':
|
||||
continue
|
||||
if not tagJson.get('name'):
|
||||
continue
|
||||
if not tagJson.get('url'):
|
||||
continue
|
||||
citationsStr += \
|
||||
'<li><a href="' + tagJson['url'] + '">' + \
|
||||
'<cite>' + tagJson['name'] + '</cite></a></li>\n'
|
||||
if citationsStr:
|
||||
citationsStr = '<p><b>' + translate['Citations'] + \
|
||||
':</b></p>' + \
|
||||
'<ul>\n' + citationsStr + '</ul>\n'
|
||||
|
||||
postHtml = ''
|
||||
if boxName != 'tlmedia':
|
||||
postHtml = ' <div id="' + timelinePostBookmark + \
|
||||
|
@ -5532,7 +5618,7 @@ def individualPostAsHtml(allowDownloads: bool,
|
|||
postHtml += ' <div class="post-title">\n' + \
|
||||
' ' + titleStr + \
|
||||
replyAvatarImageInPost + ' </div>\n'
|
||||
postHtml += contentStr + footerStr + '\n'
|
||||
postHtml += contentStr + citationsStr + footerStr + '\n'
|
||||
postHtml += ' </div>\n'
|
||||
else:
|
||||
postHtml = galleryStr
|
||||
|
@ -5599,6 +5685,7 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
"""
|
||||
htmlStr = ''
|
||||
|
||||
separatorStr = htmlPostSeparator(baseDir, 'left')
|
||||
domain = domainFull
|
||||
if ':' in domain:
|
||||
domain = domain.split(':')
|
||||
|
@ -5648,6 +5735,7 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
if editImageClass == 'leftColEdit':
|
||||
htmlStr += '\n <center>\n'
|
||||
|
||||
htmlStr += ' <div class="leftColIcons">\n'
|
||||
if editor:
|
||||
# show the edit icon
|
||||
htmlStr += \
|
||||
|
@ -5676,6 +5764,7 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
'" src="/' + iconsDir + '/logorss.png" /></a>\n'
|
||||
if rssIconAtTop:
|
||||
htmlStr += rssIconStr
|
||||
htmlStr += ' </div>\n'
|
||||
|
||||
if editImageClass == 'leftColEdit':
|
||||
htmlStr += ' </center>\n'
|
||||
|
@ -5683,8 +5772,8 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
if (editor or rssIconAtTop) and not showHeaderImage:
|
||||
htmlStr += '</div><br>'
|
||||
|
||||
if showHeaderImage:
|
||||
htmlStr += '<br>'
|
||||
# if showHeaderImage:
|
||||
# htmlStr += '<br>'
|
||||
|
||||
linksFilename = baseDir + '/accounts/links.txt'
|
||||
linksFileContainsEntries = False
|
||||
|
@ -5725,6 +5814,7 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
else:
|
||||
if lineStr.startswith('#') or lineStr.startswith('*'):
|
||||
lineStr = lineStr[1:].strip()
|
||||
htmlStr += separatorStr
|
||||
htmlStr += \
|
||||
' <h3 class="linksHeader">' + \
|
||||
lineStr + '</h3>\n'
|
||||
|
@ -5752,10 +5842,11 @@ def votesIndicator(totalVotes: int, positiveVoting: bool) -> str:
|
|||
return totalVotesStr
|
||||
|
||||
|
||||
def htmlNewswire(newswire: {}, nickname: str, moderator: bool,
|
||||
def htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
|
||||
translate: {}, positiveVoting: bool, iconsDir: str) -> str:
|
||||
"""Converts a newswire dict into html
|
||||
"""
|
||||
separatorStr = htmlPostSeparator(baseDir, 'right')
|
||||
htmlStr = ''
|
||||
for dateStr, item in newswire.items():
|
||||
publishedDate = \
|
||||
|
@ -5765,6 +5856,7 @@ def htmlNewswire(newswire: {}, nickname: str, moderator: bool,
|
|||
dateStrLink = dateStr.replace('T', ' ')
|
||||
dateStrLink = dateStrLink.replace('Z', '')
|
||||
moderatedItem = item[5]
|
||||
htmlStr += separatorStr
|
||||
if moderatedItem and 'vote:' + nickname in item[2]:
|
||||
totalVotesStr = ''
|
||||
totalVotes = 0
|
||||
|
@ -5821,6 +5913,118 @@ def htmlNewswire(newswire: {}, nickname: str, moderator: bool,
|
|||
return htmlStr
|
||||
|
||||
|
||||
def htmlCitations(baseDir: str, nickname: str, domain: str,
|
||||
httpPrefix: str, defaultTimeline: str,
|
||||
translate: {}, newswire: {}, cssCache: {},
|
||||
blogTitle: str, blogContent: str,
|
||||
blogImageFilename: str,
|
||||
blogImageAttachmentMediaType: str,
|
||||
blogImageDescription: str) -> str:
|
||||
"""Show the citations screen when creating a blog
|
||||
"""
|
||||
htmlStr = ''
|
||||
|
||||
# create a list of dates for citations
|
||||
# these can then be used to re-select checkboxes later
|
||||
citationsFilename = \
|
||||
baseDir + '/accounts/' + \
|
||||
nickname + '@' + domain + '/.citations.txt'
|
||||
citationsSelected = []
|
||||
if os.path.isfile(citationsFilename):
|
||||
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
|
||||
dateStr = sections[0]
|
||||
citationsSelected.append(dateStr)
|
||||
|
||||
# the css filename
|
||||
cssFilename = baseDir + '/epicyon-profile.css'
|
||||
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||
cssFilename = baseDir + '/epicyon.css'
|
||||
|
||||
profileStyle = getCSS(baseDir, cssFilename, cssCache)
|
||||
if profileStyle:
|
||||
# replace any https within the css with whatever prefix is needed
|
||||
if httpPrefix != 'https':
|
||||
profileStyle = \
|
||||
profileStyle.replace('https://', httpPrefix + '://')
|
||||
|
||||
# iconsDir = getIconsDir(baseDir)
|
||||
|
||||
htmlStr = htmlHeader(cssFilename, profileStyle)
|
||||
|
||||
# top banner
|
||||
bannerFile, bannerFilename = getBannerFile(baseDir, nickname, domain)
|
||||
htmlStr += \
|
||||
'<a href="/users/' + nickname + '/newblog" title="' + \
|
||||
translate['Go Back'] + '" alt="' + \
|
||||
translate['Go Back'] + '">\n'
|
||||
htmlStr += '<img loading="lazy" class="timeline-banner" src="' + \
|
||||
'/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
||||
|
||||
htmlStr += \
|
||||
'<form enctype="multipart/form-data" method="POST" ' + \
|
||||
'accept-charset="UTF-8" action="/users/' + nickname + \
|
||||
'/citationsdata">\n'
|
||||
htmlStr += ' <center>\n'
|
||||
htmlStr += translate['Choose newswire items ' +
|
||||
'referenced in your article'] + '<br>'
|
||||
if blogTitle is None:
|
||||
blogTitle = ''
|
||||
htmlStr += \
|
||||
' <input type="hidden" name="blogTitle" value="' + \
|
||||
blogTitle + '">\n'
|
||||
if blogContent is None:
|
||||
blogContent = ''
|
||||
htmlStr += \
|
||||
' <input type="hidden" name="blogContent" value="' + \
|
||||
blogContent + '">\n'
|
||||
# submit button
|
||||
htmlStr += \
|
||||
' <input type="submit" name="submitCitations" value="' + \
|
||||
translate['Submit'] + '">\n'
|
||||
htmlStr += ' </center>\n'
|
||||
|
||||
citationsSeparator = '#####'
|
||||
|
||||
# list of newswire items
|
||||
if newswire:
|
||||
ctr = 0
|
||||
for dateStr, item in newswire.items():
|
||||
# should this checkbox be selected?
|
||||
selectedStr = ''
|
||||
if dateStr in citationsSelected:
|
||||
selectedStr = ' checked'
|
||||
|
||||
publishedDate = \
|
||||
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
|
||||
dateShown = publishedDate.strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
title = removeLongWords(item[0], 16, []).replace('\n', '<br>')
|
||||
link = item[1]
|
||||
|
||||
citationValue = \
|
||||
dateStr + citationsSeparator + \
|
||||
title + citationsSeparator + \
|
||||
link
|
||||
htmlStr += \
|
||||
'<input type="checkbox" name="newswire' + str(ctr) + \
|
||||
'" value="' + citationValue + '"' + selectedStr + '/>' + \
|
||||
'<a href="' + link + '"><cite>' + title + '</cite></a> '
|
||||
htmlStr += '<span class="newswireDate">' + \
|
||||
dateShown + '</span><br>\n'
|
||||
ctr += 1
|
||||
|
||||
htmlStr += '</form>\n'
|
||||
return htmlStr + htmlFooter()
|
||||
|
||||
|
||||
def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
|
||||
httpPrefix: str, translate: {},
|
||||
iconsDir: str, moderator: bool, editor: bool,
|
||||
|
@ -5968,7 +6172,7 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
|
||||
# show the newswire lines
|
||||
newswireContentStr = \
|
||||
htmlNewswire(newswire, nickname, moderator, translate,
|
||||
htmlNewswire(baseDir, newswire, nickname, moderator, translate,
|
||||
positiveVoting, iconsDir)
|
||||
htmlStr += newswireContentStr
|
||||
|
||||
|
@ -6348,6 +6552,54 @@ def headerButtonsTimeline(defaultTimeline: str,
|
|||
inboxButton + '"><span>' + translate['Inbox'] + \
|
||||
'</span></button></a>'
|
||||
|
||||
# show todays events buttons on the first inbox page
|
||||
happeningStr = ''
|
||||
if boxName == 'inbox' and pageNumber == 1:
|
||||
if todaysEventsCheck(baseDir, nickname, domain):
|
||||
now = datetime.now()
|
||||
|
||||
# happening today button
|
||||
if not iconsAsButtons:
|
||||
happeningStr += \
|
||||
'<a href="' + usersPath + '/calendar?year=' + \
|
||||
str(now.year) + '?month=' + str(now.month) + \
|
||||
'?day=' + str(now.day) + '">' + \
|
||||
'<button class="buttonevent">' + \
|
||||
translate['Happening Today'] + '</button></a>'
|
||||
else:
|
||||
happeningStr += \
|
||||
'<a href="' + usersPath + '/calendar?year=' + \
|
||||
str(now.year) + '?month=' + str(now.month) + \
|
||||
'?day=' + str(now.day) + '">' + \
|
||||
'<button class="button">' + \
|
||||
translate['Happening Today'] + '</button></a>'
|
||||
|
||||
# happening this week button
|
||||
if thisWeeksEventsCheck(baseDir, nickname, domain):
|
||||
if not iconsAsButtons:
|
||||
happeningStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/calendar"><button class="buttonevent">' + \
|
||||
translate['Happening This Week'] + '</button></a>'
|
||||
else:
|
||||
happeningStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/calendar"><button class="button">' + \
|
||||
translate['Happening This Week'] + '</button></a>'
|
||||
else:
|
||||
# happening this week button
|
||||
if thisWeeksEventsCheck(baseDir, nickname, domain):
|
||||
if not iconsAsButtons:
|
||||
happeningStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/calendar"><button class="buttonevent">' + \
|
||||
translate['Happening This Week'] + '</button></a>'
|
||||
else:
|
||||
happeningStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/calendar"><button class="button">' + \
|
||||
translate['Happening This Week'] + '</button></a>'
|
||||
|
||||
if not newsHeader:
|
||||
# button for the outbox
|
||||
tlStr += \
|
||||
|
@ -6359,66 +6611,20 @@ def headerButtonsTimeline(defaultTimeline: str,
|
|||
# add other buttons
|
||||
tlStr += \
|
||||
sharesButtonStr + bookmarksButtonStr + eventsButtonStr + \
|
||||
moderationButtonStr + newPostButtonStr
|
||||
|
||||
# show todays events buttons on the first inbox page
|
||||
if boxName == 'inbox' and pageNumber == 1:
|
||||
if todaysEventsCheck(baseDir, nickname, domain):
|
||||
now = datetime.now()
|
||||
|
||||
# happening today button
|
||||
if not iconsAsButtons:
|
||||
tlStr += \
|
||||
'<a href="' + usersPath + '/calendar?year=' + \
|
||||
str(now.year) + '?month=' + str(now.month) + \
|
||||
'?day=' + str(now.day) + '">' + \
|
||||
'<button class="buttonevent">' + \
|
||||
translate['Happening Today'] + '</button></a>'
|
||||
else:
|
||||
tlStr += \
|
||||
'<a href="' + usersPath + '/calendar?year=' + \
|
||||
str(now.year) + '?month=' + str(now.month) + \
|
||||
'?day=' + str(now.day) + '">' + \
|
||||
'<button class="button">' + \
|
||||
translate['Happening Today'] + '</button></a>'
|
||||
|
||||
# happening this week button
|
||||
if thisWeeksEventsCheck(baseDir, nickname, domain):
|
||||
if not iconsAsButtons:
|
||||
tlStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/calendar"><button class="buttonevent">' + \
|
||||
translate['Happening This Week'] + '</button></a>'
|
||||
else:
|
||||
tlStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/calendar"><button class="button">' + \
|
||||
translate['Happening This Week'] + '</button></a>'
|
||||
else:
|
||||
# happening this week button
|
||||
if thisWeeksEventsCheck(baseDir, nickname, domain):
|
||||
if not iconsAsButtons:
|
||||
tlStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/calendar"><button class="buttonevent">' + \
|
||||
translate['Happening This Week'] + '</button></a>'
|
||||
else:
|
||||
tlStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/calendar"><button class="button">' + \
|
||||
translate['Happening This Week'] + '</button></a>'
|
||||
moderationButtonStr + happeningStr + newPostButtonStr
|
||||
|
||||
if not newsHeader:
|
||||
if not iconsAsButtons:
|
||||
# the search button
|
||||
# the search icon
|
||||
tlStr += \
|
||||
' <a class="imageAnchor" href="' + usersPath + \
|
||||
'<a class="imageAnchor" href="' + usersPath + \
|
||||
'/search"><img loading="lazy" src="/' + \
|
||||
iconsDir + '/search.png" title="' + \
|
||||
translate['Search and follow'] + '" alt="| ' + \
|
||||
translate['Search and follow'] + \
|
||||
'" class="timelineicon"/></a>'
|
||||
else:
|
||||
# the search button
|
||||
tlStr += \
|
||||
'<a href="' + usersPath + \
|
||||
'/search"><button class="button">' + \
|
||||
|
@ -6501,6 +6707,8 @@ def headerButtonsTimeline(defaultTimeline: str,
|
|||
'/links.png" title="' + translate['Edit Links'] + \
|
||||
'" alt="| ' + translate['Edit Links'] + \
|
||||
'" class="timelineicon"/></a>'
|
||||
# end of headericons div
|
||||
tlStr += '</div>'
|
||||
else:
|
||||
# NOTE: deliberately no \n at end of line
|
||||
tlStr += \
|
||||
|
@ -6523,6 +6731,23 @@ def headerButtonsTimeline(defaultTimeline: str,
|
|||
return tlStr
|
||||
|
||||
|
||||
def htmlPostSeparator(baseDir: str, column: str) -> str:
|
||||
"""Returns the html for a timeline post separator image
|
||||
"""
|
||||
iconsDir = getIconsDir(baseDir)
|
||||
filename = 'separator.png'
|
||||
if column:
|
||||
filename = 'separator_' + column + '.png'
|
||||
separatorImageFilename = baseDir + '/img/' + iconsDir + '/' + filename
|
||||
separatorStr = ''
|
||||
if os.path.isfile(separatorImageFilename):
|
||||
separatorStr = \
|
||||
'<div class="postSeparatorImage"><center>' + \
|
||||
'<img src="/' + iconsDir + '/' + filename + '"/>' + \
|
||||
'</center></div>\n'
|
||||
return separatorStr
|
||||
|
||||
|
||||
def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
||||
recentPostsCache: {}, maxRecentPosts: int,
|
||||
translate: {}, pageNumber: int,
|
||||
|
@ -6598,6 +6823,10 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
# This changes depending upon theme
|
||||
iconsDir = getIconsDir(baseDir)
|
||||
|
||||
separatorStr = ''
|
||||
if boxName != 'tlmedia':
|
||||
separatorStr = htmlPostSeparator(baseDir, None)
|
||||
|
||||
# the css filename
|
||||
cssFilename = baseDir + '/epicyon-profile.css'
|
||||
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||
|
@ -6760,10 +6989,21 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
if timeDiff > 100:
|
||||
print('TIMELINE TIMING ' + boxName + ' 4 = ' + str(timeDiff))
|
||||
|
||||
# if this is a news instance and we are viewing the news timeline
|
||||
newsHeader = False
|
||||
if defaultTimeline == 'tlnews' and boxName == 'tlnews':
|
||||
newsHeader = True
|
||||
|
||||
newPostButtonStr = ''
|
||||
# start of headericons div
|
||||
if not newsHeader:
|
||||
if not iconsAsButtons:
|
||||
newPostButtonStr += '<div class="headericons">'
|
||||
|
||||
# what screen to go to when a new post is created
|
||||
if boxName == 'dm':
|
||||
if not iconsAsButtons:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a class="imageAnchor" href="' + usersPath + \
|
||||
'/newdm"><img loading="lazy" src="/' + \
|
||||
iconsDir + '/newpost.png" title="' + \
|
||||
|
@ -6771,13 +7011,13 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
'" alt="| ' + translate['Create a new DM'] + \
|
||||
'" class="timelineicon"/></a>\n'
|
||||
else:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a href="' + usersPath + '/newdm">' + \
|
||||
'<button class="button"><span>' + \
|
||||
translate['Post'] + ' </span></button></a>'
|
||||
elif boxName == 'tlblogs' or boxName == 'tlnews':
|
||||
if not iconsAsButtons:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a class="imageAnchor" href="' + usersPath + \
|
||||
'/newblog"><img loading="lazy" src="/' + \
|
||||
iconsDir + '/newpost.png" title="' + \
|
||||
|
@ -6785,13 +7025,13 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
translate['Create a new post'] + \
|
||||
'" class="timelineicon"/></a>\n'
|
||||
else:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a href="' + usersPath + '/newblog">' + \
|
||||
'<button class="button"><span>' + \
|
||||
translate['Post'] + '</span></button></a>'
|
||||
elif boxName == 'tlevents':
|
||||
if not iconsAsButtons:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a class="imageAnchor" href="' + usersPath + \
|
||||
'/newevent"><img loading="lazy" src="/' + \
|
||||
iconsDir + '/newpost.png" title="' + \
|
||||
|
@ -6799,14 +7039,14 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
translate['Create a new event'] + \
|
||||
'" class="timelineicon"/></a>\n'
|
||||
else:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a href="' + usersPath + '/newevent">' + \
|
||||
'<button class="button"><span>' + \
|
||||
translate['Post'] + '</span></button></a>'
|
||||
else:
|
||||
if not manuallyApproveFollowers:
|
||||
if not iconsAsButtons:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a class="imageAnchor" href="' + usersPath + \
|
||||
'/newpost"><img loading="lazy" src="/' + \
|
||||
iconsDir + '/newpost.png" title="' + \
|
||||
|
@ -6814,13 +7054,13 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
translate['Create a new post'] + \
|
||||
'" class="timelineicon"/></a>\n'
|
||||
else:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a href="' + usersPath + '/newpost">' + \
|
||||
'<button class="button"><span>' + \
|
||||
translate['Post'] + '</span></button></a>'
|
||||
else:
|
||||
if not iconsAsButtons:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a class="imageAnchor" href="' + usersPath + \
|
||||
'/newfollowers"><img loading="lazy" src="/' + \
|
||||
iconsDir + '/newpost.png" title="' + \
|
||||
|
@ -6828,10 +7068,11 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
'" alt="| ' + translate['Create a new post'] + \
|
||||
'" class="timelineicon"/></a>\n'
|
||||
else:
|
||||
newPostButtonStr = \
|
||||
newPostButtonStr += \
|
||||
'<a href="' + usersPath + '/newfollowers">' + \
|
||||
'<button class="button"><span>' + \
|
||||
translate['Post'] + '</span></button></a>'
|
||||
|
||||
# This creates a link to the profile page when viewed
|
||||
# in lynx, but should be invisible in a graphical web browser
|
||||
tlStr += \
|
||||
|
@ -7056,6 +7297,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
|
||||
if currTlStr:
|
||||
itemCtr += 1
|
||||
if separatorStr:
|
||||
tlStr += separatorStr
|
||||
tlStr += currTlStr
|
||||
if boxName == 'tlmedia':
|
||||
tlStr += '</div>\n'
|
||||
|
@ -8499,7 +8742,8 @@ def htmlCalendar(cssCache: {}, translate: {},
|
|||
' <img loading="lazy" alt="' + translate['Previous month'] + \
|
||||
'" title="' + translate['Previous month'] + '" src="/' + iconsDir + \
|
||||
'/prev.png" class="buttonprev"/></a>\n'
|
||||
calendarStr += ' <a href="' + calActor + '/inbox">'
|
||||
calendarStr += ' <a href="' + calActor + '/inbox" title="'
|
||||
calendarStr += translate['Switch to timeline view'] + '">'
|
||||
calendarStr += ' <h1>' + monthName + '</h1></a>\n'
|
||||
calendarStr += \
|
||||
' <a href="' + calActor + '/calendar?year=' + str(nextYear) + \
|
||||
|
|