diff --git a/blog.py b/blog.py index a1efa5e2b..4c3e83c70 100644 --- a/blog.py +++ b/blog.py @@ -233,7 +233,28 @@ def htmlBlogPostContent(authorized: bool, 'content') blogStr += '
' + contentStr + '\n' - blogStr += '
\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 += \ + '
  • ' + \ + '' + tagJson['name'] + '
  • \n' + if citationsStr: + citationsStr = '

    ' + translate['Citations'] + \ + ':

    ' + \ + '\n' + + blogStr += '
    \n' + citationsStr if not linkedAuthor: blogStr += '

    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, diff --git a/epicyon-calendar.css b/epicyon-calendar.css index a57effefc..8db0a6b54 100644 --- a/epicyon-calendar.css +++ b/epicyon-calendar.css @@ -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; } diff --git a/epicyon-profile.css b/epicyon-profile.css index ad284219a..10495edb3 100644 --- a/epicyon-profile.css +++ b/epicyon-profile.css @@ -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

    - 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); diff --git a/follow.py b/follow.py index 84c7823da..9f61636eb 100644 --- a/follow.py +++ b/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: diff --git a/img/icons/indymediamodern/separator.png b/img/icons/indymediamodern/separator.png new file mode 100644 index 000000000..f3b5f0799 Binary files /dev/null and b/img/icons/indymediamodern/separator.png differ diff --git a/img/icons/indymediamodern/separator_left.png b/img/icons/indymediamodern/separator_left.png new file mode 100644 index 000000000..f3b5f0799 Binary files /dev/null and b/img/icons/indymediamodern/separator_left.png differ diff --git a/img/icons/night/separator_right.png b/img/icons/night/separator_right.png new file mode 100644 index 000000000..a917fe973 Binary files /dev/null and b/img/icons/night/separator_right.png differ diff --git a/img/icons/solidaric/calendar.png b/img/icons/solidaric/calendar.png index 7d951c55e..6116aa7c6 100644 Binary files a/img/icons/solidaric/calendar.png and b/img/icons/solidaric/calendar.png differ diff --git a/img/icons/solidaric/calendar_notify.png b/img/icons/solidaric/calendar_notify.png index c364c3109..1f0b12012 100644 Binary files a/img/icons/solidaric/calendar_notify.png and b/img/icons/solidaric/calendar_notify.png differ diff --git a/img/icons/solidaric/dm.png b/img/icons/solidaric/dm.png index 369fffccd..13e1bae38 100644 Binary files a/img/icons/solidaric/dm.png and b/img/icons/solidaric/dm.png differ diff --git a/img/icons/solidaric/edit.png b/img/icons/solidaric/edit.png index d29dd278f..5972682b9 100644 Binary files a/img/icons/solidaric/edit.png and b/img/icons/solidaric/edit.png differ diff --git a/img/icons/solidaric/edit_notify.png b/img/icons/solidaric/edit_notify.png index f41f31d9c..ba8ef51af 100644 Binary files a/img/icons/solidaric/edit_notify.png and b/img/icons/solidaric/edit_notify.png differ diff --git a/img/icons/solidaric/newpost.png b/img/icons/solidaric/newpost.png index 1a4febb3c..728f8316d 100644 Binary files a/img/icons/solidaric/newpost.png and b/img/icons/solidaric/newpost.png differ diff --git a/img/icons/solidaric/newswire.png b/img/icons/solidaric/newswire.png index 5732a2001..8f3acbf18 100644 Binary files a/img/icons/solidaric/newswire.png and b/img/icons/solidaric/newswire.png differ diff --git a/img/icons/solidaric/prev.png b/img/icons/solidaric/prev.png index cbbbc2417..b8d6a40f8 100644 Binary files a/img/icons/solidaric/prev.png and b/img/icons/solidaric/prev.png differ diff --git a/img/icons/solidaric/publish.png b/img/icons/solidaric/publish.png index 9d64f4b7b..a9b1a53b1 100644 Binary files a/img/icons/solidaric/publish.png and b/img/icons/solidaric/publish.png differ diff --git a/img/icons/solidaric/scope_blog.png b/img/icons/solidaric/scope_blog.png index 9d64f4b7b..d2c60ddea 100644 Binary files a/img/icons/solidaric/scope_blog.png and b/img/icons/solidaric/scope_blog.png differ diff --git a/img/icons/solidaric/scope_dm.png b/img/icons/solidaric/scope_dm.png index 571e4042b..728f8316d 100644 Binary files a/img/icons/solidaric/scope_dm.png and b/img/icons/solidaric/scope_dm.png differ diff --git a/img/icons/solidaric/scope_event.png b/img/icons/solidaric/scope_event.png index 7d951c55e..6116aa7c6 100644 Binary files a/img/icons/solidaric/scope_event.png and b/img/icons/solidaric/scope_event.png differ diff --git a/img/icons/solidaric/search.png b/img/icons/solidaric/search.png index 18b7b05da..48d2d3e6d 100644 Binary files a/img/icons/solidaric/search.png and b/img/icons/solidaric/search.png differ diff --git a/img/icons/solidaric/separator.png b/img/icons/solidaric/separator.png new file mode 100644 index 000000000..8a2fbedae Binary files /dev/null and b/img/icons/solidaric/separator.png differ diff --git a/img/left_col_image_indymediamodern.png b/img/left_col_image_indymediamodern.png deleted file mode 100644 index d663454b0..000000000 Binary files a/img/left_col_image_indymediamodern.png and /dev/null differ diff --git a/img/right_col_image_indymediamodern.png b/img/right_col_image_indymediamodern.png deleted file mode 100644 index 9f3bf7525..000000000 Binary files a/img/right_col_image_indymediamodern.png and /dev/null differ diff --git a/img/right_col_image_solidaric.png b/img/right_col_image_solidaric.png index 2658ce7a8..34ddf0fe5 100644 Binary files a/img/right_col_image_solidaric.png and b/img/right_col_image_solidaric.png differ diff --git a/newsdaemon.py b/newsdaemon.py index c0a6e8120..695eaef71 100644 --- a/newsdaemon.py +++ b/newsdaemon.py @@ -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, diff --git a/posts.py b/posts.py index ed93f04e9..ec59bc6db 100644 --- a/posts.py +++ b/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 diff --git a/tests.py b/tests.py index c4cf32433..8a75dbedc 100644 --- a/tests.py +++ b/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' diff --git a/theme.py b/theme.py index dec93871f..6fc2c02cd 100644 --- a/theme.py +++ b/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", diff --git a/translations/ar.json b/translations/ar.json index d0c16cab4..1118a56b1 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -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": "اختر العناصر الإخبارية المشار إليها في مقالتك" } diff --git a/translations/ca.json b/translations/ca.json index 7bb30de20..797ad5469 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -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" } diff --git a/translations/cy.json b/translations/cy.json index d3c964222..d5cdb532d 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -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" } diff --git a/translations/de.json b/translations/de.json index e2e373907..b8eb64df8 100644 --- a/translations/de.json +++ b/translations/de.json @@ -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" } diff --git a/translations/en.json b/translations/en.json index c3d6d80ea..0b7b9b3c3 100644 --- a/translations/en.json +++ b/translations/en.json @@ -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" } diff --git a/translations/es.json b/translations/es.json index 547e1fb11..dac48137c 100644 --- a/translations/es.json +++ b/translations/es.json @@ -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" } diff --git a/translations/fr.json b/translations/fr.json index 4d03a9ef9..e1edeefb3 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -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" } diff --git a/translations/ga.json b/translations/ga.json index 1f683ad04..ba28b61e6 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -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" } diff --git a/translations/hi.json b/translations/hi.json index c181d5b72..11daf17df 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -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 आइटम चुनें" } diff --git a/translations/it.json b/translations/it.json index 0a968746b..2470fc5d9 100644 --- a/translations/it.json +++ b/translations/it.json @@ -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" } diff --git a/translations/ja.json b/translations/ja.json index 6db9ca798..d6b71d6ca 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -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": "あなたの記事で参照されているニュースワイヤーアイテムを選択してください" } diff --git a/translations/oc.json b/translations/oc.json index f8de1d224..efda869e1 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -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" } diff --git a/translations/pt.json b/translations/pt.json index 87519778c..316fdcf85 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -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" } diff --git a/translations/ru.json b/translations/ru.json index faeceb3a9..885238420 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -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": "Выберите элементы ленты новостей, на которые есть ссылки в вашей статье" } diff --git a/translations/zh.json b/translations/zh.json index 8ed04b18f..f7ba586ec 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -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": "选择文章中引用的新闻专栏文章" } diff --git a/webinterface.py b/webinterface.py index 9509450b0..a2933611d 100644 --- a/webinterface.py +++ b/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, '
    \n' + \ ' \n' + \ - '
    \n' + '
    \n' editLinksForm += \ '
    \n' @@ -1365,7 +1367,7 @@ def htmlEditNewswire(cssCache: {}, translate: {}, baseDir: str, path: str, '
    \n' + \ ' \n' + \ - '
    \n' + '
    \n' editNewswireForm += \ ' \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 = '
    \n' + dropDownContent += ' \n' + dropDownContent += ' \n' + dropDownContent += ' \n' + dropDownContent += '
    \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 += '\n' extraFields += '\n' + citationsStr = '' + if endpoint == 'newblog': + citationsFilename = \ + baseDir + '/accounts/' + \ + nickname + '@' + domain + '/.citations.txt' + if os.path.isfile(citationsFilename): + citationsStr = '
    \n' + citationsStr += '

    \n' + citationsStr += ' \n' + citationsStr += '
    \n' + dateAndLocation = '' if endpoint != 'newshare' and \ endpoint != 'newreport' and \ @@ -2791,21 +2925,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, newPostForm += '\n' - # only show the share option if this is not a reply - shareOptionOnDropdown = '' - questionOptionOnDropdown = '' - if not replyStr: - shareOptionOnDropdown = \ - '
  • ' + translate['Shares'] + \ - '
    ' + translate['Describe a shared item'] + '
  • \n' - questionOptionOnDropdown = \ - '
  • ' + translate['Question'] + \ - '
    ' + translate['Ask a question'] + '
  • \n' - mentionsStr = '' for m in mentions: mentionNickname = getNicknameFromActor(m) @@ -2858,89 +2977,22 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, dropDownContent = '' if not reportUrl: - dropDownContent += "\n' - dropDownContent += ' \n' - dropDownContent += ' \n' - dropDownContent += '\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 += ' \n' newPostForm += '
    \n' + # newPostForm += \ # ' \n' + + # for a new blog if newswire items exist then add a citations button + if newswire and path.endswith('/newblog'): + newPostForm += \ + ' \n' + newPostForm += \ ' \n' + newPostForm += '
    \n' newPostForm += replyStr @@ -3018,7 +3079,7 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, newPostForm += \ ' \n' - newPostForm += extraFields+dateAndLocation + newPostForm += extraFields + citationsStr + dateAndLocation if not mediaInstance or replyStr: newPostForm += newPostImageSection newPostForm += ' \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'] + '">\n' + \ '
    \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, '
    ' + contentStr + \
                     '
    \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 += \ + '
  • ' + \ + '' + tagJson['name'] + '
  • \n' + if citationsStr: + citationsStr = '

    ' + translate['Citations'] + \ + ':

    ' + \ + '\n' + postHtml = '' if boxName != 'tlmedia': postHtml = '
    \n' + \ ' ' + titleStr + \ replyAvatarImageInPost + '
    \n' - postHtml += contentStr + footerStr + '\n' + postHtml += contentStr + citationsStr + footerStr + '\n' postHtml += ' \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
    \n' + htmlStr += '
    \n' if editor: # show the edit icon htmlStr += \ @@ -5676,6 +5764,7 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, '" src="/' + iconsDir + '/logorss.png" />\n' if rssIconAtTop: htmlStr += rssIconStr + htmlStr += '
    \n' if editImageClass == 'leftColEdit': htmlStr += '
    \n' @@ -5683,8 +5772,8 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, if (editor or rssIconAtTop) and not showHeaderImage: htmlStr += '
    ' - if showHeaderImage: - htmlStr += '
    ' + # if showHeaderImage: + # htmlStr += '
    ' 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 += \ '

    ' + \ lineStr + '

    \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 += \ + '\n' + htmlStr += '\n' + + htmlStr += \ + '
    \n' + htmlStr += '
    \n' + htmlStr += translate['Choose newswire items ' + + 'referenced in your article'] + '
    ' + if blogTitle is None: + blogTitle = '' + htmlStr += \ + ' \n' + if blogContent is None: + blogContent = '' + htmlStr += \ + ' \n' + # submit button + htmlStr += \ + ' \n' + htmlStr += '
    \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', '
    ') + link = item[1] + + citationValue = \ + dateStr + citationsSeparator + \ + title + citationsSeparator + \ + link + htmlStr += \ + '' + \ + '' + title + ' ' + htmlStr += '' + \ + dateShown + '
    \n' + ctr += 1 + + htmlStr += '
    \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 + '">' + translate['Inbox'] + \ '' + # 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 += \ + '' + \ + '' + else: + happeningStr += \ + '' + \ + '' + + # happening this week button + if thisWeeksEventsCheck(baseDir, nickname, domain): + if not iconsAsButtons: + happeningStr += \ + '' + else: + happeningStr += \ + '' + else: + # happening this week button + if thisWeeksEventsCheck(baseDir, nickname, domain): + if not iconsAsButtons: + happeningStr += \ + '' + else: + happeningStr += \ + '' + 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 += \ - '' + \ - '' - else: - tlStr += \ - '' + \ - '' - - # happening this week button - if thisWeeksEventsCheck(baseDir, nickname, domain): - if not iconsAsButtons: - tlStr += \ - '' - else: - tlStr += \ - '' - else: - # happening this week button - if thisWeeksEventsCheck(baseDir, nickname, domain): - if not iconsAsButtons: - tlStr += \ - '' - else: - tlStr += \ - '' + moderationButtonStr + happeningStr + newPostButtonStr if not newsHeader: if not iconsAsButtons: - # the search button + # the search icon tlStr += \ - ' | ' + \
                 translate['Search and follow'] + \
                 '' else: + # the search button tlStr += \ '' elif boxName == 'tlblogs' or boxName == 'tlnews': if not iconsAsButtons: - newPostButtonStr = \ + newPostButtonStr += \ '\n' else: - newPostButtonStr = \ + newPostButtonStr += \ '' + \ '' elif boxName == 'tlevents': if not iconsAsButtons: - newPostButtonStr = \ + newPostButtonStr += \ '\n' else: - newPostButtonStr = \ + newPostButtonStr += \ '' + \ '' else: if not manuallyApproveFollowers: if not iconsAsButtons: - newPostButtonStr = \ + newPostButtonStr += \ '\n' else: - newPostButtonStr = \ + newPostButtonStr += \ '' + \ '' else: if not iconsAsButtons: - newPostButtonStr = \ + newPostButtonStr += \ '| ' + translate['Create a new post'] + \
                     '\n' else: - newPostButtonStr = \ + newPostButtonStr += \ '' + \ '' + # 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 += '\n' @@ -8499,7 +8742,8 @@ def htmlCalendar(cssCache: {}, translate: {}, ' ' + translate['Previous month'] + \
         '\n' - calendarStr += ' ' + calendarStr += ' ' calendarStr += '

    ' + monthName + '

    \n' calendarStr += \ '