Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon into main
|
|
@ -1,7 +1,9 @@
|
||||||
@charset "UTF-8";
|
@charset "UTF-8";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
--header-bg-color: #282c37;
|
||||||
--main-bg-color: #282c37;
|
--main-bg-color: #282c37;
|
||||||
|
--post-bg-color: #282c37;
|
||||||
--column-left-color: #282c37;
|
--column-left-color: #282c37;
|
||||||
--link-bg-color: #282c37;
|
--link-bg-color: #282c37;
|
||||||
--dropdown-fg-color: #dddddd;
|
--dropdown-fg-color: #dddddd;
|
||||||
|
|
@ -46,6 +48,8 @@
|
||||||
--time-vertical-align: 4px;
|
--time-vertical-align: 4px;
|
||||||
--time-vertical-align-mobile: 25px;
|
--time-vertical-align-mobile: 25px;
|
||||||
--publish-button-text: #FFFFFF;
|
--publish-button-text: #FFFFFF;
|
||||||
|
--button-margin: 5px;
|
||||||
|
--button-left-margin: none;
|
||||||
--button-text: #FFFFFF;
|
--button-text: #FFFFFF;
|
||||||
--button-selected-text: #FFFFFF;
|
--button-selected-text: #FFFFFF;
|
||||||
--publish-button-background: #999;
|
--publish-button-background: #999;
|
||||||
|
|
@ -62,7 +66,7 @@
|
||||||
--button-height: 10px;
|
--button-height: 10px;
|
||||||
--button-height-padding-mobile: 20px;
|
--button-height-padding-mobile: 20px;
|
||||||
--button-height-padding: 10px;
|
--button-height-padding: 10px;
|
||||||
--image-corners: 10%,
|
--image-corners: 10%;
|
||||||
--gallery-border: #ccc;
|
--gallery-border: #ccc;
|
||||||
--gallery-hover: #777;
|
--gallery-hover: #777;
|
||||||
--gallery-text-color: #ccc;
|
--gallery-text-color: #ccc;
|
||||||
|
|
@ -70,6 +74,7 @@
|
||||||
--gallery-font-size-mobile: 35px;
|
--gallery-font-size-mobile: 35px;
|
||||||
--button-corner-radius: 15px;
|
--button-corner-radius: 15px;
|
||||||
--timeline-border-radius: 30px;
|
--timeline-border-radius: 30px;
|
||||||
|
--timeline-posts-background-color: #282c37;
|
||||||
--icons-side: right;
|
--icons-side: right;
|
||||||
--title-color: #999;
|
--title-color: #999;
|
||||||
--focus-color: white;
|
--focus-color: white;
|
||||||
|
|
@ -83,6 +88,8 @@
|
||||||
--column-left-width: 10vw;
|
--column-left-width: 10vw;
|
||||||
--column-center-width: 80vw;
|
--column-center-width: 80vw;
|
||||||
--column-right-width: 10vw;
|
--column-right-width: 10vw;
|
||||||
|
--column-left-mobile-margin: 2%;
|
||||||
|
--column-left-top-margin: 0;
|
||||||
--column-left-header-style: uppercase;
|
--column-left-header-style: uppercase;
|
||||||
--column-left-header-background: #555;
|
--column-left-header-background: #555;
|
||||||
--column-left-header-color: #fff;
|
--column-left-header-color: #fff;
|
||||||
|
|
@ -129,13 +136,16 @@
|
||||||
--post-separator-margin-top: 0;
|
--post-separator-margin-top: 0;
|
||||||
--post-separator-margin-bottom: 0;
|
--post-separator-margin-bottom: 0;
|
||||||
--post-separator-width: 95%;
|
--post-separator-width: 95%;
|
||||||
|
--separator-width-left: 95%;
|
||||||
|
--separator-width-right: 95%;
|
||||||
--post-separator-height: 1px;
|
--post-separator-height: 1px;
|
||||||
--header-vertical-offset: 0;
|
--header-vertical-offset: 0;
|
||||||
--profile-background-height: 25vw;
|
--profile-background-height: 25vw;
|
||||||
--profile-text-align: left;
|
--profile-text-align: left;
|
||||||
--verticals-width: 0;
|
--verticals-width: 0;
|
||||||
--italic-font-style: italic;
|
--italic-font-style: italic;
|
||||||
--header-font: 'Bedstead';
|
--header-font: 'Arial, Helvetica, sans-serif';
|
||||||
|
--button-bottom-margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
|
@ -166,13 +176,14 @@ body, html {
|
||||||
|
|
||||||
.leftColIcons {
|
.leftColIcons {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--main-bg-color);
|
background-color: var(--column-left-color);
|
||||||
float: right;
|
float: right;
|
||||||
display: block;
|
display: block;
|
||||||
padding-bottom: var(--column-left-icons-margin);
|
padding-bottom: var(--column-left-icons-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.postSeparatorImage img {
|
.postSeparatorImage img {
|
||||||
|
background-color: var(--post-bg-color);
|
||||||
padding-top: var(--post-separator-margin-top);
|
padding-top: var(--post-separator-margin-top);
|
||||||
padding-bottom: var(--post-separator-margin-bottom);
|
padding-bottom: var(--post-separator-margin-bottom);
|
||||||
width: var(--post-separator-width);
|
width: var(--post-separator-width);
|
||||||
|
|
@ -180,6 +191,24 @@ body, html {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.postSeparatorImageLeft img {
|
||||||
|
background-color: var(--column-left-color);
|
||||||
|
padding-top: var(--post-separator-margin-top);
|
||||||
|
padding-bottom: var(--post-separator-margin-bottom);
|
||||||
|
width: var(--separator-width-left);
|
||||||
|
height: var(--post-separator-height);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.postSeparatorImageRight img {
|
||||||
|
background-color: var(--column-left-color);
|
||||||
|
padding-top: var(--post-separator-margin-top);
|
||||||
|
padding-bottom: var(--post-separator-margin-bottom);
|
||||||
|
width: var(--separator-width-right);
|
||||||
|
height: var(--post-separator-height);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.headericons {
|
.headericons {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
@ -217,6 +246,10 @@ blockquote p {
|
||||||
margin: -4px 5px;
|
margin: -4px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.imageAnchor {
|
||||||
|
font-family: var(--header-font);
|
||||||
|
}
|
||||||
|
|
||||||
.imageAnchor:focus img{
|
.imageAnchor:focus img{
|
||||||
border: 2px solid var(--focus-color);
|
border: 2px solid var(--focus-color);
|
||||||
}
|
}
|
||||||
|
|
@ -378,6 +411,10 @@ a:focus {
|
||||||
width: 10%;
|
width: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timeline-posts {
|
||||||
|
background-color: var(--timeline-posts-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
.container img.timelineicon:hover {
|
.container img.timelineicon:hover {
|
||||||
filter: brightness(var(--icon-brightness-change));
|
filter: brightness(var(--icon-brightness-change));
|
||||||
}
|
}
|
||||||
|
|
@ -530,6 +567,10 @@ a:focus {
|
||||||
background: var(--link-bg-color);
|
background: var(--link-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.announceOrReply {
|
||||||
|
font-family: var(--header-font);
|
||||||
|
}
|
||||||
|
|
||||||
.container img.announceOrReply {
|
.container img.announceOrReply {
|
||||||
float: none;
|
float: none;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
|
|
@ -932,15 +973,16 @@ div.container {
|
||||||
}
|
}
|
||||||
.containerHeader {
|
.containerHeader {
|
||||||
border: var(--border-width-header) solid var(--border-color);
|
border: var(--border-width-header) solid var(--border-color);
|
||||||
background-color: var(--main-bg-color);
|
background-color: var(--header-bg-color);
|
||||||
border-radius: var(--timeline-border-radius);
|
border-radius: var(--timeline-border-radius);
|
||||||
padding: var(--header-button-padding);
|
padding: var(--header-button-padding);
|
||||||
margin: var(--vertical-between-posts-header);
|
margin: var(--vertical-between-posts-header);
|
||||||
transform: translateY(var(--header-vertical-offset));
|
transform: translateY(var(--header-vertical-offset));
|
||||||
|
margin-bottom: var(--button-bottom-margin);
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
border: var(--border-width) solid var(--border-color);
|
border: var(--border-width) solid var(--border-color);
|
||||||
background-color: var(--main-bg-color);
|
background-color: var(--post-bg-color);
|
||||||
border-radius: var(--timeline-border-radius);
|
border-radius: var(--timeline-border-radius);
|
||||||
padding-left: var(--container-padding);
|
padding-left: var(--container-padding);
|
||||||
padding-right: var(--container-padding);
|
padding-right: var(--container-padding);
|
||||||
|
|
@ -954,7 +996,6 @@ div.container {
|
||||||
font-family: var(--header-font);
|
font-family: var(--header-font);
|
||||||
font-size: var(--column-left-header-size);
|
font-size: var(--column-left-header-size);
|
||||||
text-transform: var(--column-left-header-style);
|
text-transform: var(--column-left-header-style);
|
||||||
padding: 4px;
|
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
.newswireItem {
|
.newswireItem {
|
||||||
|
|
@ -999,13 +1040,14 @@ div.container {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.timeline-banner {
|
.timeline-banner {
|
||||||
|
vertical-align: top;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 98vw;
|
width: 100%;
|
||||||
max-height: var(--banner-height);
|
max-height: var(--banner-height);
|
||||||
}
|
}
|
||||||
.timeline {
|
.timeline {
|
||||||
border: 0;
|
border: 0;
|
||||||
width: 98vw;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.col-left a:link {
|
.col-left a:link {
|
||||||
background: var(--column-left-color);
|
background: var(--column-left-color);
|
||||||
|
|
@ -1018,6 +1060,7 @@ div.container {
|
||||||
width: var(--column-left-width);
|
width: var(--column-left-width);
|
||||||
}
|
}
|
||||||
.col-left {
|
.col-left {
|
||||||
|
margin-top: var(--column-left-top-margin);
|
||||||
border: var(--column-left-border-width) solid var(--column-left-border-color);
|
border: var(--column-left-border-width) solid var(--column-left-border-color);
|
||||||
color: var(--column-left-fg-color);
|
color: var(--column-left-fg-color);
|
||||||
font-size: var(--font-size-links);
|
font-size: var(--font-size-links);
|
||||||
|
|
@ -1252,7 +1295,7 @@ div.container {
|
||||||
font-family: var(--header-font);
|
font-family: var(--header-font);
|
||||||
padding: var(--button-height-padding);
|
padding: var(--button-height-padding);
|
||||||
width: 10%;
|
width: 10%;
|
||||||
margin: 5px;
|
margin: var(--button-margin);
|
||||||
min-width: var(--button-width-chars);
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -1260,6 +1303,8 @@ div.container {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
|
margin-bottom: var(--button-bottom-margin);
|
||||||
|
margin-left: var(--button-left-margin);
|
||||||
}
|
}
|
||||||
.buttonDesktop {
|
.buttonDesktop {
|
||||||
border-radius: var(--button-corner-radius);
|
border-radius: var(--button-corner-radius);
|
||||||
|
|
@ -1278,6 +1323,8 @@ div.container {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
|
margin-bottom: var(--button-bottom-margin);
|
||||||
|
margin-left: var(--button-left-margin);
|
||||||
}
|
}
|
||||||
.publishbtn {
|
.publishbtn {
|
||||||
border-radius: var(--button-corner-radius);
|
border-radius: var(--button-corner-radius);
|
||||||
|
|
@ -1322,7 +1369,7 @@ div.container {
|
||||||
font-family: var(--header-font);
|
font-family: var(--header-font);
|
||||||
padding: var(--button-height-padding);
|
padding: var(--button-height-padding);
|
||||||
width: 10%;
|
width: 10%;
|
||||||
margin: 5px;
|
margin: var(--button-margin);
|
||||||
min-width: var(--button-width-chars);
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -1330,6 +1377,8 @@ div.container {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
|
margin-bottom: var(--button-bottom-margin);
|
||||||
|
margin-left: var(--button-left-margin);
|
||||||
}
|
}
|
||||||
.buttonselectedhighlighted {
|
.buttonselectedhighlighted {
|
||||||
border-radius: var(--button-corner-radius);
|
border-radius: var(--button-corner-radius);
|
||||||
|
|
@ -1381,6 +1430,7 @@ div.container {
|
||||||
}
|
}
|
||||||
.pageicon {
|
.pageicon {
|
||||||
width: 4%;
|
width: 4%;
|
||||||
|
background-color: var(--post-bg-color);
|
||||||
}
|
}
|
||||||
.time-right {
|
.time-right {
|
||||||
float: var(--icons-side);
|
float: var(--icons-side);
|
||||||
|
|
@ -1564,15 +1614,16 @@ div.container {
|
||||||
}
|
}
|
||||||
.containerHeader {
|
.containerHeader {
|
||||||
border: var(--border-width-header) solid var(--border-color);
|
border: var(--border-width-header) solid var(--border-color);
|
||||||
background-color: var(--main-bg-color);
|
background-color: var(--header-bg-color);
|
||||||
border-radius: var(--timeline-border-radius);
|
border-radius: var(--timeline-border-radius);
|
||||||
padding: var(--header-button-padding);
|
padding: var(--header-button-padding);
|
||||||
margin: var(--vertical-between-posts-header);
|
margin: var(--vertical-between-posts-header);
|
||||||
transform: translateY(0%);
|
transform: translateY(0%);
|
||||||
|
margin-bottom: var(--button-bottom-margin);
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
border: var(--border-width) solid var(--border-color);
|
border: var(--border-width) solid var(--border-color);
|
||||||
background-color: var(--main-bg-color);
|
background-color: var(--post-bg-color);
|
||||||
border-radius: var(--timeline-border-radius);
|
border-radius: var(--timeline-border-radius);
|
||||||
padding-left: var(--container-padding);
|
padding-left: var(--container-padding);
|
||||||
padding-right: var(--container-padding);
|
padding-right: var(--container-padding);
|
||||||
|
|
@ -1586,7 +1637,6 @@ div.container {
|
||||||
font-family: var(--header-font);
|
font-family: var(--header-font);
|
||||||
font-size: var(--column-left-header-size-mobile);
|
font-size: var(--column-left-header-size-mobile);
|
||||||
text-transform: var(--column-left-header-style);
|
text-transform: var(--column-left-header-style);
|
||||||
padding: 4px;
|
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
.leftColEditImage {
|
.leftColEditImage {
|
||||||
|
|
@ -1657,6 +1707,7 @@ div.container {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
.timeline-banner {
|
.timeline-banner {
|
||||||
|
vertical-align: top;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 98vw;
|
width: 98vw;
|
||||||
max-height: var(--banner-height-mobile);
|
max-height: var(--banner-height-mobile);
|
||||||
|
|
@ -1671,6 +1722,9 @@ div.container {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0%;
|
width: 0%;
|
||||||
}
|
}
|
||||||
|
.col-left-mobile {
|
||||||
|
margin-left: var(--column-left-mobile-margin);
|
||||||
|
}
|
||||||
.col-left {
|
.col-left {
|
||||||
float: left;
|
float: left;
|
||||||
width: 0%;
|
width: 0%;
|
||||||
|
|
@ -1679,6 +1733,10 @@ div.container {
|
||||||
.col-center {
|
.col-center {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
.col-right-mobile {
|
||||||
|
margin-left: var(--column-left-mobile-margin);
|
||||||
|
margin-right: var(--column-left-mobile-margin);
|
||||||
|
}
|
||||||
.col-right {
|
.col-right {
|
||||||
float: right;
|
float: right;
|
||||||
width: 0%;
|
width: 0%;
|
||||||
|
|
@ -1848,11 +1906,12 @@ div.container {
|
||||||
min-width: var(--button-width-chars);
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 5px;
|
margin: var(--button-margin);
|
||||||
border-top: var(--tab-border-width) solid var(--tab-border-color);
|
border-top: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
|
margin-bottom: var(--button-bottom-margin);
|
||||||
}
|
}
|
||||||
.frontPageMobileButtons{
|
.frontPageMobileButtons{
|
||||||
display: block;
|
display: block;
|
||||||
|
|
@ -1881,11 +1940,12 @@ div.container {
|
||||||
min-width: var(--button-width-chars);
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 5px;
|
margin: var(--button-margin);
|
||||||
border-top: var(--tab-border-width) solid var(--tab-border-color);
|
border-top: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
|
margin-bottom: var(--button-bottom-margin);
|
||||||
}
|
}
|
||||||
.buttonDesktop {
|
.buttonDesktop {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
@ -1910,7 +1970,7 @@ div.container {
|
||||||
min-width: var(--button-width-chars);
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 15px;
|
margin: 15px 0px;
|
||||||
}
|
}
|
||||||
.buttonhighlighted {
|
.buttonhighlighted {
|
||||||
border-radius: var(--button-corner-radius);
|
border-radius: var(--button-corner-radius);
|
||||||
|
|
@ -1940,11 +2000,12 @@ div.container {
|
||||||
min-width: var(--button-width-chars);
|
min-width: var(--button-width-chars);
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 5px;
|
margin: var(--button-margin);
|
||||||
border-top: var(--tab-border-width) solid var(--tab-border-color);
|
border-top: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||||
|
margin-bottom: var(--button-bottom-margin);
|
||||||
}
|
}
|
||||||
.buttonselectedhighlighted {
|
.buttonselectedhighlighted {
|
||||||
border-radius: var(--button-corner-radius);
|
border-radius: var(--button-corner-radius);
|
||||||
|
|
@ -1996,6 +2057,7 @@ div.container {
|
||||||
}
|
}
|
||||||
.pageicon {
|
.pageicon {
|
||||||
width: 14%;
|
width: 14%;
|
||||||
|
background-color: var(--post-bg-color);
|
||||||
}
|
}
|
||||||
.time-right {
|
.time-right {
|
||||||
float: var(--icons-side);
|
float: var(--icons-side);
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@
|
||||||
--focus-color: white;
|
--focus-color: white;
|
||||||
--search-banner-height: 30vh;
|
--search-banner-height: 30vh;
|
||||||
--search-banner-height-mobile: 20vh;
|
--search-banner-height-mobile: 20vh;
|
||||||
|
--title-color: #999;
|
||||||
|
--header-font: 'Arial, Helvetica, sans-serif';
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
|
@ -62,6 +64,11 @@ body, html {
|
||||||
min-width: 600px;
|
min-width: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--header-font);
|
||||||
|
color: var(--title-color);
|
||||||
|
}
|
||||||
|
|
||||||
a, u {
|
a, u {
|
||||||
color: var(--main-fg-color);
|
color: var(--main-fg-color);
|
||||||
}
|
}
|
||||||
|
|
@ -90,6 +97,13 @@ a:focus {
|
||||||
border: 2px solid var(--focus-color);
|
border: 2px solid var(--focus-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.domainHistogramLeft {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.domainHistogramRight {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
.follow {
|
.follow {
|
||||||
background-image: url("follow-background.jpg");
|
background-image: url("follow-background.jpg");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
|
@ -192,6 +206,10 @@ input[type=text] {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 400px) {
|
@media screen and (min-width: 400px) {
|
||||||
|
.domainHistogram {
|
||||||
|
border: 0;
|
||||||
|
font-size: var(--hashtag-size1);
|
||||||
|
}
|
||||||
.timeline-banner {
|
.timeline-banner {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 98vw;
|
width: 98vw;
|
||||||
|
|
@ -244,6 +262,10 @@ input[type=text] {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1000px) {
|
@media screen and (max-width: 1000px) {
|
||||||
|
.domainHistogram {
|
||||||
|
border: 0;
|
||||||
|
font-size: var(--hashtag-size2);
|
||||||
|
}
|
||||||
.timeline-banner {
|
.timeline-banner {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 98vw;
|
width: 98vw;
|
||||||
|
|
|
||||||
|
|
@ -4474,6 +4474,8 @@ class JsonLdError(Exception):
|
||||||
self.causeTrace = traceback.extract_tb(*sys.exc_info()[2:])
|
self.causeTrace = traceback.extract_tb(*sys.exc_info()[2:])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
if not hasattr(self, 'message'):
|
||||||
|
return 'Unknown exception'
|
||||||
rval = repr(self.message)
|
rval = repr(self.message)
|
||||||
rval += '\nType: ' + self.type
|
rval += '\nType: ' + self.type
|
||||||
if self.code:
|
if self.code:
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@
|
||||||
"gallery-font-size": "35px",
|
"gallery-font-size": "35px",
|
||||||
"gallery-font-size-mobile": "55px",
|
"gallery-font-size-mobile": "55px",
|
||||||
"main-bg-color": "#002365",
|
"main-bg-color": "#002365",
|
||||||
|
"post-bg-color": "#002365",
|
||||||
|
"timeline-posts-background-color": "#002365",
|
||||||
|
"header-bg-color": "#002365",
|
||||||
"column-left-color": "#002365",
|
"column-left-color": "#002365",
|
||||||
"text-entry-background": "#002365",
|
"text-entry-background": "#002365",
|
||||||
"link-bg-color": "#002365",
|
"link-bg-color": "#002365",
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
"publish-button-at-top": "False",
|
"publish-button-at-top": "False",
|
||||||
"focus-color": "green",
|
"focus-color": "green",
|
||||||
"main-bg-color": "black",
|
"main-bg-color": "black",
|
||||||
|
"post-bg-color": "black",
|
||||||
|
"timeline-posts-background-color": "black",
|
||||||
|
"header-bg-color": "black",
|
||||||
"column-left-color": "black",
|
"column-left-color": "black",
|
||||||
"link-bg-color": "black",
|
"link-bg-color": "black",
|
||||||
"main-bg-color-dm": "#0b0a0a",
|
"main-bg-color-dm": "#0b0a0a",
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@
|
||||||
"font-size4": "24px",
|
"font-size4": "24px",
|
||||||
"font-size5": "22px",
|
"font-size5": "22px",
|
||||||
"main-bg-color": "#383335",
|
"main-bg-color": "#383335",
|
||||||
|
"post-bg-color": "#383335",
|
||||||
|
"timeline-posts-background-color": "#383335",
|
||||||
|
"header-bg-color": "#383335",
|
||||||
"column-left-color": "#383335",
|
"column-left-color": "#383335",
|
||||||
"text-entry-background": "#383335",
|
"text-entry-background": "#383335",
|
||||||
"link-bg-color": "#383335",
|
"link-bg-color": "#383335",
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@
|
||||||
"font-size4": "24px",
|
"font-size4": "24px",
|
||||||
"font-size5": "22px",
|
"font-size5": "22px",
|
||||||
"main-bg-color": "black",
|
"main-bg-color": "black",
|
||||||
|
"post-bg-color": "black",
|
||||||
|
"timeline-posts-background-color": "black",
|
||||||
|
"header-bg-color": "black",
|
||||||
"column-left-header-color": "#fff",
|
"column-left-header-color": "#fff",
|
||||||
"column-left-header-background": "#555",
|
"column-left-header-background": "#555",
|
||||||
"column-left-header-size": "20px",
|
"column-left-header-size": "20px",
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"verticals-width": "3px",
|
"button-bottom-margin": "0",
|
||||||
|
"header-vertical-offset": "10px",
|
||||||
|
"header-bg-color": "#efefef",
|
||||||
|
"verticals-width": "0px",
|
||||||
"newswire-publish-icon": "False",
|
"newswire-publish-icon": "False",
|
||||||
|
"line-spacing-newswire": "150%",
|
||||||
"full-width-timeline-buttons": "False",
|
"full-width-timeline-buttons": "False",
|
||||||
"icons-as-buttons": "True",
|
"icons-as-buttons": "True",
|
||||||
"rss-icon-at-top": "False",
|
"rss-icon-at-top": "False",
|
||||||
|
|
@ -13,8 +17,8 @@
|
||||||
"font-size-calendar-cell": "2rem",
|
"font-size-calendar-cell": "2rem",
|
||||||
"calendar-horizontal-padding": "20%",
|
"calendar-horizontal-padding": "20%",
|
||||||
"time-vertical-align": "10px",
|
"time-vertical-align": "10px",
|
||||||
"header-vertical-offset": "-10%",
|
"publish-button-vertical-offset": "15px",
|
||||||
"publish-button-vertical-offset": "0",
|
"vertical-between-posts": "0",
|
||||||
"vertical-between-posts-header": "0 0",
|
"vertical-between-posts-header": "0 0",
|
||||||
"header-button-padding": "0 0",
|
"header-button-padding": "0 0",
|
||||||
"containericons-horizontal-spacing": "0%",
|
"containericons-horizontal-spacing": "0%",
|
||||||
|
|
@ -31,44 +35,49 @@
|
||||||
"font-size-dropdown-header": "30px",
|
"font-size-dropdown-header": "30px",
|
||||||
"post-separator-margin-top": "1%",
|
"post-separator-margin-top": "1%",
|
||||||
"post-separator-margin-bottom": "1%",
|
"post-separator-margin-bottom": "1%",
|
||||||
"post-separator-width": "95%",
|
"post-separator-width": "96%",
|
||||||
|
"separator-width-left": "98%",
|
||||||
|
"separator-width-right": "99%",
|
||||||
"post-separator-height": "1px",
|
"post-separator-height": "1px",
|
||||||
|
"column-left-top-margin": "10px",
|
||||||
"column-left-border-width": "0px",
|
"column-left-border-width": "0px",
|
||||||
"column-right-border-width": "0px",
|
"column-right-border-width": "0px",
|
||||||
|
"column-left-mobile-margin": "2%",
|
||||||
"column-left-border-color": "black",
|
"column-left-border-color": "black",
|
||||||
"column-left-header-color": "black",
|
"column-left-header-color": "black",
|
||||||
"column-left-header-background": "white",
|
"column-left-header-background": "#efefef",
|
||||||
"column-left-header-style": "none",
|
"column-left-header-style": "none",
|
||||||
"search-banner-height": "15vh",
|
"search-banner-height": "15vh",
|
||||||
"search-banner-height-mobile": "10vh",
|
"search-banner-height-mobile": "10vh",
|
||||||
"container-button-padding": "0px",
|
"container-button-padding": "0px",
|
||||||
"container-button-margin": "0px",
|
"container-button-margin": "0px",
|
||||||
"column-left-icon-size": "15%",
|
"column-left-icon-size": "15%",
|
||||||
"column-right-icon-size": "15%",
|
"column-right-icon-size": "8%",
|
||||||
|
"button-margin": "2px",
|
||||||
"button-height-padding": "5px",
|
"button-height-padding": "5px",
|
||||||
"icon-brightness-change": "70%",
|
"icon-brightness-change": "70%",
|
||||||
"border-width": "0px",
|
"border-width": "0px",
|
||||||
"border-width-header": "0px",
|
"border-width-header": "0px",
|
||||||
"tab-border-width": "3px",
|
"tab-border-width": "0px",
|
||||||
"tab-border-color": "grey",
|
"tab-border-color": "grey",
|
||||||
"button-corner-radius": "0px",
|
"button-corner-radius": "0px",
|
||||||
"login-button-color": "#25408f",
|
"login-button-color": "#25408f",
|
||||||
"login-button-fg-color": "white",
|
"login-button-fg-color": "white",
|
||||||
"column-left-width": "10vw",
|
"column-left-width": "10vw",
|
||||||
"column-center-width": "80vw",
|
"column-center-width": "70vw",
|
||||||
"column-right-width": "10vw",
|
"column-right-width": "20vw",
|
||||||
"column-right-fg-color": "#25408f",
|
"column-right-fg-color": "#25408f",
|
||||||
"column-right-fg-color-voted-on": "red",
|
"column-right-fg-color-voted-on": "red",
|
||||||
"newswire-item-moderated-color": "red",
|
"newswire-item-moderated-color": "red",
|
||||||
"newswire-date-moderated-color": "red",
|
"newswire-date-moderated-color": "red",
|
||||||
"newswire-date-color": "grey",
|
"newswire-date-color": "grey",
|
||||||
"timeline-border-radius": "0px",
|
"timeline-border-radius": "0px",
|
||||||
"button-background": "#767674",
|
"button-background": "#dedede",
|
||||||
"button-background-hover": "#555",
|
"button-background-hover": "white",
|
||||||
"button-text-hover": "white",
|
"button-text-hover": "black",
|
||||||
"button-selected": "white",
|
"button-selected": "white",
|
||||||
"button-selected-text": "black",
|
"button-selected-text": "black",
|
||||||
"button-text": "white",
|
"button-text": "black",
|
||||||
"hashtag-fg-color": "white",
|
"hashtag-fg-color": "white",
|
||||||
"publish-button-background": "#25408f",
|
"publish-button-background": "#25408f",
|
||||||
"publish-button-text": "white",
|
"publish-button-text": "white",
|
||||||
|
|
@ -77,10 +86,12 @@
|
||||||
"font-size-button-mobile": "26px",
|
"font-size-button-mobile": "26px",
|
||||||
"font-size-publish-button": "14px",
|
"font-size-publish-button": "14px",
|
||||||
"rgba(0, 0, 0, 0.5)": "rgba(0, 0, 0, 0.0)",
|
"rgba(0, 0, 0, 0.5)": "rgba(0, 0, 0, 0.0)",
|
||||||
"column-left-color": "white",
|
"column-left-color": "#efefef",
|
||||||
"main-bg-color": "white",
|
"main-bg-color": "#efefef",
|
||||||
|
"post-bg-color": "white",
|
||||||
|
"timeline-posts-background-color": "white",
|
||||||
"main-bg-color-dm": "white",
|
"main-bg-color-dm": "white",
|
||||||
"link-bg-color": "white",
|
"link-bg-color": "transparent",
|
||||||
"main-bg-color-reply": "white",
|
"main-bg-color-reply": "white",
|
||||||
"main-bg-color-report": "white",
|
"main-bg-color-report": "white",
|
||||||
"main-header-color-roles": "#ebebf0",
|
"main-header-color-roles": "#ebebf0",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@
|
||||||
"column-left-header-background": "#9fb42b",
|
"column-left-header-background": "#9fb42b",
|
||||||
"column-left-header-color": "#33390d",
|
"column-left-header-color": "#33390d",
|
||||||
"main-bg-color": "#9fb42b",
|
"main-bg-color": "#9fb42b",
|
||||||
|
"post-bg-color": "#9fb42b",
|
||||||
|
"timeline-posts-background-color": "#9fb42b",
|
||||||
|
"header-bg-color": "#9fb42b",
|
||||||
"column-left-color": "#33390d",
|
"column-left-color": "#33390d",
|
||||||
"column-left-fg-color": "#9fb42b",
|
"column-left-fg-color": "#9fb42b",
|
||||||
"link-bg-color": "#33390d",
|
"link-bg-color": "#33390d",
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@
|
||||||
"rgba(0, 0, 0, 0.5)": "rgba(0, 0, 0, 0.0)",
|
"rgba(0, 0, 0, 0.5)": "rgba(0, 0, 0, 0.0)",
|
||||||
"column-left-color": "#e6ebf0",
|
"column-left-color": "#e6ebf0",
|
||||||
"main-bg-color": "#e6ebf0",
|
"main-bg-color": "#e6ebf0",
|
||||||
|
"post-bg-color": "#e6ebf0",
|
||||||
|
"timeline-posts-background-color": "#e6ebf0",
|
||||||
|
"header-bg-color": "#e6ebf0",
|
||||||
"main-bg-color-dm": "#e3dbf0",
|
"main-bg-color-dm": "#e3dbf0",
|
||||||
"link-bg-color": "#e6ebf0",
|
"link-bg-color": "#e6ebf0",
|
||||||
"main-bg-color-reply": "#e0dbf0",
|
"main-bg-color-reply": "#e0dbf0",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@
|
||||||
"font-size4": "24px",
|
"font-size4": "24px",
|
||||||
"font-size5": "22px",
|
"font-size5": "22px",
|
||||||
"main-bg-color": "#0f0d10",
|
"main-bg-color": "#0f0d10",
|
||||||
|
"post-bg-color": "#0f0d10",
|
||||||
|
"timeline-posts-background-color": "#0f0d10",
|
||||||
|
"header-bg-color": "#0f0d10",
|
||||||
"column-left-color": "#0f0d10",
|
"column-left-color": "#0f0d10",
|
||||||
"text-entry-background": "#0f0d10",
|
"text-entry-background": "#0f0d10",
|
||||||
"link-bg-color": "#0f0d10",
|
"link-bg-color": "#0f0d10",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@
|
||||||
"font-size4": "24px",
|
"font-size4": "24px",
|
||||||
"font-size5": "22px",
|
"font-size5": "22px",
|
||||||
"main-bg-color": "#1f152d",
|
"main-bg-color": "#1f152d",
|
||||||
|
"post-bg-color": "#1f152d",
|
||||||
|
"timeline-posts-background-color": "#1f152d",
|
||||||
|
"header-bg-color": "#1f152d",
|
||||||
"column-left-color": "#1f152d",
|
"column-left-color": "#1f152d",
|
||||||
"link-bg-color": "#1f152d",
|
"link-bg-color": "#1f152d",
|
||||||
"main-bg-color-reply": "#1a142d",
|
"main-bg-color-reply": "#1a142d",
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.5 KiB |
|
|
@ -41,6 +41,9 @@
|
||||||
"font-size5": "12px",
|
"font-size5": "12px",
|
||||||
"font-size-likes": "10px",
|
"font-size-likes": "10px",
|
||||||
"main-bg-color": "#100e23",
|
"main-bg-color": "#100e23",
|
||||||
|
"post-bg-color": "#100e23",
|
||||||
|
"timeline-posts-background-color": "#100e23",
|
||||||
|
"header-bg-color": "#100e23",
|
||||||
"column-left-color": "#0f0d10",
|
"column-left-color": "#0f0d10",
|
||||||
"text-entry-background": "#0f0d10",
|
"text-entry-background": "#0f0d10",
|
||||||
"link-bg-color": "#0f0d10",
|
"link-bg-color": "#0f0d10",
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,12 @@
|
||||||
"font-size5": "22px",
|
"font-size5": "22px",
|
||||||
"rgba(0, 0, 0, 0.5)": "rgba(0, 0, 0, 0.0)",
|
"rgba(0, 0, 0, 0.5)": "rgba(0, 0, 0, 0.0)",
|
||||||
"main-bg-color": "white",
|
"main-bg-color": "white",
|
||||||
|
"post-bg-color": "white",
|
||||||
|
"timeline-posts-background-color": "white",
|
||||||
|
"header-bg-color": "#ddd",
|
||||||
"column-left-color": "white",
|
"column-left-color": "white",
|
||||||
"main-bg-color-dm": "white",
|
"main-bg-color-dm": "white",
|
||||||
"link-bg-color": "white",
|
"link-bg-color": "transparent",
|
||||||
"main-bg-color-reply": "white",
|
"main-bg-color-reply": "white",
|
||||||
"main-bg-color-report": "white",
|
"main-bg-color-report": "white",
|
||||||
"main-header-color-roles": "#ebebf0",
|
"main-header-color-roles": "#ebebf0",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@
|
||||||
"font-size4": "24px",
|
"font-size4": "24px",
|
||||||
"font-size5": "22px",
|
"font-size5": "22px",
|
||||||
"main-bg-color": "#0f0d10",
|
"main-bg-color": "#0f0d10",
|
||||||
|
"post-bg-color": "#0f0d10",
|
||||||
|
"timeline-posts-background-color": "#0f0d10",
|
||||||
|
"header-bg-color": "#0f0d10",
|
||||||
"column-left-color": "#0f0d10",
|
"column-left-color": "#0f0d10",
|
||||||
"text-entry-background": "#0f0d10",
|
"text-entry-background": "#0f0d10",
|
||||||
"link-bg-color": "#0f0d10",
|
"link-bg-color": "#0f0d10",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@
|
||||||
"banner-height-mobile": "10vh",
|
"banner-height-mobile": "10vh",
|
||||||
"newswire-date-color": "yellow",
|
"newswire-date-color": "yellow",
|
||||||
"main-bg-color": "#5c4e41",
|
"main-bg-color": "#5c4e41",
|
||||||
|
"post-bg-color": "#5c4e41",
|
||||||
|
"timeline-posts-background-color": "#5c4e41",
|
||||||
|
"header-bg-color": "#5c4e41",
|
||||||
"column-left-color": "#5c4e41",
|
"column-left-color": "#5c4e41",
|
||||||
"text-entry-background": "#5c4e41",
|
"text-entry-background": "#5c4e41",
|
||||||
"link-bg-color": "#5c4e41",
|
"link-bg-color": "#5c4e41",
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "اختر العناصر الإخبارية المشار إليها في مقالتك",
|
"Choose newswire items referenced in your article": "اختر العناصر الإخبارية المشار إليها في مقالتك",
|
||||||
"RSS feed for your blog": "تغذية RSS لمدونتك",
|
"RSS feed for your blog": "تغذية RSS لمدونتك",
|
||||||
"Create a new shared item": "إنشاء عنصر مشترك جديد",
|
"Create a new shared item": "إنشاء عنصر مشترك جديد",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "أصول الهاشتاق"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Trieu articles de newswire als quals faci referència el vostre article",
|
"Choose newswire items referenced in your article": "Trieu articles de newswire als quals faci referència el vostre article",
|
||||||
"RSS feed for your blog": "Feed RSS del vostre bloc",
|
"RSS feed for your blog": "Feed RSS del vostre bloc",
|
||||||
"Create a new shared item": "Creeu un element compartit nou",
|
"Create a new shared item": "Creeu un element compartit nou",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Orígens de hashtag"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Dewiswch eitemau newyddion y cyfeirir atynt yn eich erthygl",
|
"Choose newswire items referenced in your article": "Dewiswch eitemau newyddion y cyfeirir atynt yn eich erthygl",
|
||||||
"RSS feed for your blog": "Porthiant RSS ar gyfer eich blog",
|
"RSS feed for your blog": "Porthiant RSS ar gyfer eich blog",
|
||||||
"Create a new shared item": "Creu eitem newydd a rennir",
|
"Create a new shared item": "Creu eitem newydd a rennir",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Gwreiddiau Hashtag"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Wählen Sie Newswire-Artikel aus, auf die in Ihrem Artikel verwiesen wird",
|
"Choose newswire items referenced in your article": "Wählen Sie Newswire-Artikel aus, auf die in Ihrem Artikel verwiesen wird",
|
||||||
"RSS feed for your blog": "RSS-Feed für Ihr Blog",
|
"RSS feed for your blog": "RSS-Feed für Ihr Blog",
|
||||||
"Create a new shared item": "Erstellen Sie ein neues freigegebenes Element",
|
"Create a new shared item": "Erstellen Sie ein neues freigegebenes Element",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Hashtag-Ursprünge"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Choose newswire items referenced in your article",
|
"Choose newswire items referenced in your article": "Choose newswire items referenced in your article",
|
||||||
"RSS feed for your blog": "RSS feed for your blog",
|
"RSS feed for your blog": "RSS feed for your blog",
|
||||||
"Create a new shared item": "Create a new shared item",
|
"Create a new shared item": "Create a new shared item",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Hashtag origins"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Elija elementos de Newswire a los que se hace referencia en su artículo",
|
"Choose newswire items referenced in your article": "Elija elementos de Newswire a los que se hace referencia en su artículo",
|
||||||
"RSS feed for your blog": "Fuente RSS para tu blog",
|
"RSS feed for your blog": "Fuente RSS para tu blog",
|
||||||
"Create a new shared item": "Crea un nuevo elemento compartido",
|
"Create a new shared item": "Crea un nuevo elemento compartido",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Orígenes del hashtag"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Choisissez les éléments de fil d'actualité référencés dans votre article",
|
"Choose newswire items referenced in your article": "Choisissez les éléments de fil d'actualité référencés dans votre article",
|
||||||
"RSS feed for your blog": "Flux RSS pour votre blog",
|
"RSS feed for your blog": "Flux RSS pour votre blog",
|
||||||
"Create a new shared item": "Créer un nouvel élément partagé",
|
"Create a new shared item": "Créer un nouvel élément partagé",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Origines des hashtags"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Roghnaigh míreanna sreanga nuachta dá dtagraítear i d’alt",
|
"Choose newswire items referenced in your article": "Roghnaigh míreanna sreanga nuachta dá dtagraítear i d’alt",
|
||||||
"RSS feed for your blog": "Fotha RSS do do bhlag",
|
"RSS feed for your blog": "Fotha RSS do do bhlag",
|
||||||
"Create a new shared item": "Cruthaigh mír nua roinnte",
|
"Create a new shared item": "Cruthaigh mír nua roinnte",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Bunús Hashtag"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "अपने लेख में संदर्भित newswire आइटम चुनें",
|
"Choose newswire items referenced in your article": "अपने लेख में संदर्भित newswire आइटम चुनें",
|
||||||
"RSS feed for your blog": "RSS आपके ब्लॉग के लिए फ़ीड करता है",
|
"RSS feed for your blog": "RSS आपके ब्लॉग के लिए फ़ीड करता है",
|
||||||
"Create a new shared item": "एक नया साझा आइटम बनाएं",
|
"Create a new shared item": "एक नया साझा आइटम बनाएं",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "हैशटैग की उत्पत्ति"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Scegli gli articoli del newswire a cui fa riferimento il tuo articolo",
|
"Choose newswire items referenced in your article": "Scegli gli articoli del newswire a cui fa riferimento il tuo articolo",
|
||||||
"RSS feed for your blog": "Feed RSS per il tuo blog",
|
"RSS feed for your blog": "Feed RSS per il tuo blog",
|
||||||
"Create a new shared item": "Crea un nuovo elemento condiviso",
|
"Create a new shared item": "Crea un nuovo elemento condiviso",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Origini hashtag"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "あなたの記事で参照されているニュースワイヤーアイテムを選択してください",
|
"Choose newswire items referenced in your article": "あなたの記事で参照されているニュースワイヤーアイテムを選択してください",
|
||||||
"RSS feed for your blog": "ブログのRSSフィード",
|
"RSS feed for your blog": "ブログのRSSフィード",
|
||||||
"Create a new shared item": "新しい共有アイテムを作成する",
|
"Create a new shared item": "新しい共有アイテムを作成する",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "ハッシュタグの起源"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -325,5 +325,6 @@
|
||||||
"Choose newswire items referenced in your article": "Choose newswire items referenced in your article",
|
"Choose newswire items referenced in your article": "Choose newswire items referenced in your article",
|
||||||
"RSS feed for your blog": "RSS feed for your blog",
|
"RSS feed for your blog": "RSS feed for your blog",
|
||||||
"Create a new shared item": "Create a new shared item",
|
"Create a new shared item": "Create a new shared item",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Hashtag origins"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Escolha os itens de notícias mencionados em seu artigo",
|
"Choose newswire items referenced in your article": "Escolha os itens de notícias mencionados em seu artigo",
|
||||||
"RSS feed for your blog": "Feed RSS para o seu blog",
|
"RSS feed for your blog": "Feed RSS para o seu blog",
|
||||||
"Create a new shared item": "Crie um novo item compartilhado",
|
"Create a new shared item": "Crie um novo item compartilhado",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Origens de hashtag"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "Выберите элементы ленты новостей, на которые есть ссылки в вашей статье",
|
"Choose newswire items referenced in your article": "Выберите элементы ленты новостей, на которые есть ссылки в вашей статье",
|
||||||
"RSS feed for your blog": "RSS-канал для вашего блога",
|
"RSS feed for your blog": "RSS-канал для вашего блога",
|
||||||
"Create a new shared item": "Создать новый общий элемент",
|
"Create a new shared item": "Создать новый общий элемент",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "Происхождение хэштегов"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,5 +329,6 @@
|
||||||
"Choose newswire items referenced in your article": "选择文章中引用的新闻专栏文章",
|
"Choose newswire items referenced in your article": "选择文章中引用的新闻专栏文章",
|
||||||
"RSS feed for your blog": "您博客的RSS供稿",
|
"RSS feed for your blog": "您博客的RSS供稿",
|
||||||
"Create a new shared item": "创建一个新的共享项目",
|
"Create a new shared item": "创建一个新的共享项目",
|
||||||
"Rc3": "Rc3"
|
"Rc3": "Rc3",
|
||||||
|
"Hashtag origins": "标签起源"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,7 @@ def htmlLinksMobile(cssCache: {}, baseDir: str,
|
||||||
'<img loading="lazy" class="timeline-banner" ' + \
|
'<img loading="lazy" class="timeline-banner" ' + \
|
||||||
'src="/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
'src="/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
||||||
|
|
||||||
|
htmlStr += '<div class="col-left-mobile">\n'
|
||||||
htmlStr += '<center>' + \
|
htmlStr += '<center>' + \
|
||||||
headerButtonsFrontScreen(translate, nickname,
|
headerButtonsFrontScreen(translate, nickname,
|
||||||
'links', authorized,
|
'links', authorized,
|
||||||
|
|
@ -237,6 +238,10 @@ def htmlLinksMobile(cssCache: {}, baseDir: str,
|
||||||
iconsPath, editor,
|
iconsPath, editor,
|
||||||
False, timelinePath,
|
False, timelinePath,
|
||||||
rssIconAtTop, False, False)
|
rssIconAtTop, False, False)
|
||||||
|
|
||||||
|
# end of col-left-mobile
|
||||||
|
htmlStr += '</div>\n'
|
||||||
|
|
||||||
htmlStr += '</div>\n' + htmlFooter()
|
htmlStr += '</div>\n' + htmlFooter()
|
||||||
return htmlStr
|
return htmlStr
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from content import removeLongWords
|
from content import removeLongWords
|
||||||
|
from utils import removeHtml
|
||||||
from utils import locatePost
|
from utils import locatePost
|
||||||
from utils import loadJson
|
from utils import loadJson
|
||||||
from utils import getConfigParam
|
from utils import getConfigParam
|
||||||
|
|
@ -205,6 +206,14 @@ def htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
|
||||||
separatorStr = htmlPostSeparator(baseDir, 'right')
|
separatorStr = htmlPostSeparator(baseDir, 'right')
|
||||||
htmlStr = ''
|
htmlStr = ''
|
||||||
for dateStr, item in newswire.items():
|
for dateStr, item in newswire.items():
|
||||||
|
item[0] = removeHtml(item[0]).strip()
|
||||||
|
if not item[0]:
|
||||||
|
continue
|
||||||
|
# remove any CDATA
|
||||||
|
if 'CDATA[' in item[0]:
|
||||||
|
item[0] = item[0].split('CDATA[')[1]
|
||||||
|
if ']' in item[0]:
|
||||||
|
item[0] = item[0].split(']')[0]
|
||||||
publishedDate = \
|
publishedDate = \
|
||||||
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
|
datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
|
||||||
dateShown = publishedDate.strftime("%Y-%m-%d %H:%M")
|
dateShown = publishedDate.strftime("%Y-%m-%d %H:%M")
|
||||||
|
|
@ -346,6 +355,14 @@ def htmlCitations(baseDir: str, nickname: str, domain: str,
|
||||||
if newswire:
|
if newswire:
|
||||||
ctr = 0
|
ctr = 0
|
||||||
for dateStr, item in newswire.items():
|
for dateStr, item in newswire.items():
|
||||||
|
item[0] = removeHtml(item[0]).strip()
|
||||||
|
if not item[0]:
|
||||||
|
continue
|
||||||
|
# remove any CDATA
|
||||||
|
if 'CDATA[' in item[0]:
|
||||||
|
item[0] = item[0].split('CDATA[')[1]
|
||||||
|
if ']' in item[0]:
|
||||||
|
item[0] = item[0].split(']')[0]
|
||||||
# should this checkbox be selected?
|
# should this checkbox be selected?
|
||||||
selectedStr = ''
|
selectedStr = ''
|
||||||
if dateStr in citationsSelected:
|
if dateStr in citationsSelected:
|
||||||
|
|
@ -416,6 +433,8 @@ def htmlNewswireMobile(cssCache: {}, baseDir: str, nickname: str,
|
||||||
'<img loading="lazy" class="timeline-banner" ' + \
|
'<img loading="lazy" class="timeline-banner" ' + \
|
||||||
'src="/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
'src="/users/' + nickname + '/' + bannerFile + '" /></a>\n'
|
||||||
|
|
||||||
|
htmlStr += '<div class="col-right-mobile">\n'
|
||||||
|
|
||||||
htmlStr += '<center>' + \
|
htmlStr += '<center>' + \
|
||||||
headerButtonsFrontScreen(translate, nickname,
|
headerButtonsFrontScreen(translate, nickname,
|
||||||
'newswire', authorized,
|
'newswire', authorized,
|
||||||
|
|
@ -428,6 +447,9 @@ def htmlNewswireMobile(cssCache: {}, baseDir: str, nickname: str,
|
||||||
False, timelinePath, showPublishButton,
|
False, timelinePath, showPublishButton,
|
||||||
showPublishAsIcon, rssIconAtTop, False,
|
showPublishAsIcon, rssIconAtTop, False,
|
||||||
authorized, False)
|
authorized, False)
|
||||||
|
# end of col-right-mobile
|
||||||
|
htmlStr += '</div\n>'
|
||||||
|
|
||||||
htmlStr += htmlFooter()
|
htmlStr += htmlFooter()
|
||||||
return htmlStr
|
return htmlStr
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
__filename__ = "webapp_hashtagswarm.py"
|
||||||
|
__author__ = "Bob Mottram"
|
||||||
|
__license__ = "AGPL3+"
|
||||||
|
__version__ = "1.1.0"
|
||||||
|
__maintainer__ = "Bob Mottram"
|
||||||
|
__email__ = "bob@freedombone.net"
|
||||||
|
__status__ = "Production"
|
||||||
|
|
||||||
|
import os
|
||||||
|
from blocking import isBlockedHashtag
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def getHashtagDomainMax(domainHistogram: {}) -> str:
|
||||||
|
"""Returns the domain with the maximum number of hashtags
|
||||||
|
"""
|
||||||
|
maxCount = 1
|
||||||
|
maxDomain = None
|
||||||
|
for domain, count in domainHistogram.items():
|
||||||
|
if count > maxCount:
|
||||||
|
maxDomain = domain
|
||||||
|
maxCount = count
|
||||||
|
return maxDomain
|
||||||
|
|
||||||
|
|
||||||
|
def getHashtagDomainHistogram(domainHistogram: {}, translate: {}) -> str:
|
||||||
|
"""Returns the html for a histogram of domains
|
||||||
|
from which hashtags are coming
|
||||||
|
"""
|
||||||
|
totalCount = 0
|
||||||
|
for domain, count in domainHistogram.items():
|
||||||
|
totalCount += count
|
||||||
|
if totalCount == 0:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
htmlStr = ''
|
||||||
|
histogramHeaderStr = '<br><br><center>\n'
|
||||||
|
histogramHeaderStr += ' <h1>' + translate['Hashtag origins'] + '</h1>\n'
|
||||||
|
histogramHeaderStr += ' <table class="domainHistogram">\n'
|
||||||
|
histogramHeaderStr += ' <colgroup>\n'
|
||||||
|
histogramHeaderStr += ' <col span="1" class="domainHistogramLeft">\n'
|
||||||
|
histogramHeaderStr += ' <col span="1" class="domainHistogramRight">\n'
|
||||||
|
histogramHeaderStr += ' </colgroup>\n'
|
||||||
|
histogramHeaderStr += ' <tbody>\n'
|
||||||
|
histogramHeaderStr += ' <tr>\n'
|
||||||
|
|
||||||
|
leftColStr = ''
|
||||||
|
rightColStr = ''
|
||||||
|
|
||||||
|
for i in range(len(domainHistogram)):
|
||||||
|
domain = getHashtagDomainMax(domainHistogram)
|
||||||
|
if not domain:
|
||||||
|
break
|
||||||
|
percent = int(domainHistogram[domain] * 100 / totalCount)
|
||||||
|
if histogramHeaderStr:
|
||||||
|
htmlStr += histogramHeaderStr
|
||||||
|
histogramHeaderStr = None
|
||||||
|
leftColStr += str(percent) + '%<br>'
|
||||||
|
rightColStr += domain + '<br>'
|
||||||
|
del domainHistogram[domain]
|
||||||
|
|
||||||
|
if htmlStr:
|
||||||
|
htmlStr += ' <td>' + leftColStr + '</td>\n'
|
||||||
|
htmlStr += ' <td>' + rightColStr + '</td>\n'
|
||||||
|
htmlStr += ' </tr>\n'
|
||||||
|
htmlStr += ' </tbody>\n'
|
||||||
|
htmlStr += ' </table>\n'
|
||||||
|
htmlStr += '</center>\n'
|
||||||
|
|
||||||
|
return htmlStr
|
||||||
|
|
||||||
|
|
||||||
|
def htmlHashTagSwarm(baseDir: str, actor: str, translate: {}) -> str:
|
||||||
|
"""Returns a tag swarm of today's hashtags
|
||||||
|
"""
|
||||||
|
currTime = datetime.utcnow()
|
||||||
|
daysSinceEpoch = (currTime - datetime(1970, 1, 1)).days
|
||||||
|
daysSinceEpochStr = str(daysSinceEpoch) + ' '
|
||||||
|
tagSwarm = []
|
||||||
|
domainHistogram = {}
|
||||||
|
|
||||||
|
for subdir, dirs, files in os.walk(baseDir + '/tags'):
|
||||||
|
for f in files:
|
||||||
|
tagsFilename = os.path.join(baseDir + '/tags', f)
|
||||||
|
if not os.path.isfile(tagsFilename):
|
||||||
|
continue
|
||||||
|
# get last modified datetime
|
||||||
|
modTimesinceEpoc = os.path.getmtime(tagsFilename)
|
||||||
|
lastModifiedDate = datetime.fromtimestamp(modTimesinceEpoc)
|
||||||
|
fileDaysSinceEpoch = (lastModifiedDate - datetime(1970, 1, 1)).days
|
||||||
|
# check if the file was last modified today
|
||||||
|
if fileDaysSinceEpoch != daysSinceEpoch:
|
||||||
|
continue
|
||||||
|
|
||||||
|
hashTagName = f.split('.')[0]
|
||||||
|
if isBlockedHashtag(baseDir, hashTagName):
|
||||||
|
continue
|
||||||
|
if daysSinceEpochStr not in open(tagsFilename).read():
|
||||||
|
continue
|
||||||
|
with open(tagsFilename, 'r') as tagsFile:
|
||||||
|
while True:
|
||||||
|
line = tagsFile.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
elif ' ' not in line:
|
||||||
|
break
|
||||||
|
sections = line.split(' ')
|
||||||
|
if len(sections) != 3:
|
||||||
|
break
|
||||||
|
postDaysSinceEpochStr = sections[0]
|
||||||
|
if not postDaysSinceEpochStr.isdigit():
|
||||||
|
break
|
||||||
|
postDaysSinceEpoch = int(postDaysSinceEpochStr)
|
||||||
|
if postDaysSinceEpoch < daysSinceEpoch - 1:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
postUrl = sections[2]
|
||||||
|
if '##' not in postUrl:
|
||||||
|
break
|
||||||
|
postDomain = postUrl.split('##')[1]
|
||||||
|
if '#' in postDomain:
|
||||||
|
postDomain = postDomain.split('#')[0]
|
||||||
|
if domainHistogram.get(postDomain):
|
||||||
|
domainHistogram[postDomain] = \
|
||||||
|
domainHistogram[postDomain] + 1
|
||||||
|
else:
|
||||||
|
domainHistogram[postDomain] = 1
|
||||||
|
tagSwarm.append(hashTagName)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not tagSwarm:
|
||||||
|
return ''
|
||||||
|
tagSwarm.sort()
|
||||||
|
tagSwarmStr = ''
|
||||||
|
ctr = 0
|
||||||
|
for tagName in tagSwarm:
|
||||||
|
tagSwarmStr += \
|
||||||
|
'<a href="' + actor + '/tags/' + tagName + \
|
||||||
|
'" class="hashtagswarm">' + tagName + '</a>\n'
|
||||||
|
ctr += 1
|
||||||
|
tagSwarmHtml = tagSwarmStr.strip() + '\n'
|
||||||
|
tagSwarmHtml += getHashtagDomainHistogram(domainHistogram, translate)
|
||||||
|
return tagSwarmHtml
|
||||||
|
|
@ -0,0 +1,330 @@
|
||||||
|
__filename__ = "webapp_headerbuttons.py"
|
||||||
|
__author__ = "Bob Mottram"
|
||||||
|
__license__ = "AGPL3+"
|
||||||
|
__version__ = "1.1.0"
|
||||||
|
__maintainer__ = "Bob Mottram"
|
||||||
|
__email__ = "bob@freedombone.net"
|
||||||
|
__status__ = "Production"
|
||||||
|
|
||||||
|
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from happening import todaysEventsCheck
|
||||||
|
from happening import thisWeeksEventsCheck
|
||||||
|
from webapp_utils import htmlHighlightLabel
|
||||||
|
|
||||||
|
|
||||||
|
def headerButtonsTimeline(defaultTimeline: str,
|
||||||
|
boxName: str,
|
||||||
|
pageNumber: int,
|
||||||
|
translate: {},
|
||||||
|
usersPath: str,
|
||||||
|
mediaButton: str,
|
||||||
|
blogsButton: str,
|
||||||
|
newsButton: str,
|
||||||
|
inboxButton: str,
|
||||||
|
dmButton: str,
|
||||||
|
newDM: str,
|
||||||
|
repliesButton: str,
|
||||||
|
newReply: str,
|
||||||
|
minimal: bool,
|
||||||
|
sentButton: str,
|
||||||
|
sharesButtonStr: str,
|
||||||
|
bookmarksButtonStr: str,
|
||||||
|
eventsButtonStr: str,
|
||||||
|
moderationButtonStr: str,
|
||||||
|
newPostButtonStr: str,
|
||||||
|
baseDir: str,
|
||||||
|
nickname: str, domain: str,
|
||||||
|
iconsPath: str,
|
||||||
|
timelineStartTime,
|
||||||
|
newCalendarEvent: bool,
|
||||||
|
calendarPath: str,
|
||||||
|
calendarImage: str,
|
||||||
|
followApprovals: str,
|
||||||
|
iconsAsButtons: bool) -> str:
|
||||||
|
"""Returns the header at the top of the timeline, containing
|
||||||
|
buttons for inbox, outbox, search, calendar, etc
|
||||||
|
"""
|
||||||
|
# start of the button header with inbox, outbox, etc
|
||||||
|
tlStr = '<div class="containerHeader">\n'
|
||||||
|
# first button
|
||||||
|
if defaultTimeline == 'tlmedia':
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/tlmedia"><button class="' + \
|
||||||
|
mediaButton + '"><span>' + translate['Media'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
elif defaultTimeline == 'tlblogs':
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/tlblogs"><button class="' + \
|
||||||
|
blogsButton + '"><span>' + translate['Blogs'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
elif defaultTimeline == 'tlnews':
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/tlnews"><button class="' + \
|
||||||
|
newsButton + '"><span>' + translate['Features'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
else:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/inbox"><button class="' + \
|
||||||
|
inboxButton + '"><span>' + \
|
||||||
|
translate['Inbox'] + '</span></button></a>'
|
||||||
|
|
||||||
|
# if this is a news instance and we are viewing the news timeline
|
||||||
|
newsHeader = False
|
||||||
|
if defaultTimeline == 'tlnews' and boxName == 'tlnews':
|
||||||
|
newsHeader = True
|
||||||
|
|
||||||
|
if not newsHeader:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/dm"><button class="' + dmButton + \
|
||||||
|
'"><span>' + htmlHighlightLabel(translate['DM'], newDM) + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + '/tlreplies"><button class="' + \
|
||||||
|
repliesButton + '"><span>' + \
|
||||||
|
htmlHighlightLabel(translate['Replies'], newReply) + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
# typically the media button
|
||||||
|
if defaultTimeline != 'tlmedia':
|
||||||
|
if not minimal and not newsHeader:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/tlmedia"><button class="' + \
|
||||||
|
mediaButton + '"><span>' + translate['Media'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
else:
|
||||||
|
if not minimal:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/inbox"><button class="' + \
|
||||||
|
inboxButton+'"><span>' + translate['Inbox'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
isFeaturesTimeline = \
|
||||||
|
defaultTimeline == 'tlnews' and boxName == 'tlnews'
|
||||||
|
|
||||||
|
if not isFeaturesTimeline:
|
||||||
|
# typically the blogs button
|
||||||
|
# but may change if this is a blogging oriented instance
|
||||||
|
if defaultTimeline != 'tlblogs':
|
||||||
|
if not minimal and not isFeaturesTimeline:
|
||||||
|
titleStr = translate['Blogs']
|
||||||
|
if defaultTimeline == 'tlnews':
|
||||||
|
titleStr = translate['Article']
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/tlblogs"><button class="' + \
|
||||||
|
blogsButton + '"><span>' + titleStr + \
|
||||||
|
'</span></button></a>'
|
||||||
|
else:
|
||||||
|
if not minimal:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/inbox"><button class="' + \
|
||||||
|
inboxButton + '"><span>' + translate['Inbox'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
# typically the news button
|
||||||
|
# but may change if this is a news oriented instance
|
||||||
|
if defaultTimeline != 'tlnews':
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/tlnews"><button class="' + \
|
||||||
|
newsButton + '"><span>' + translate['News'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
else:
|
||||||
|
if not newsHeader:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/inbox"><button class="' + \
|
||||||
|
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 += \
|
||||||
|
'<a href="' + usersPath + \
|
||||||
|
'/outbox"><button class="' + \
|
||||||
|
sentButton + '"><span>' + translate['Outbox'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
# add other buttons
|
||||||
|
tlStr += \
|
||||||
|
sharesButtonStr + bookmarksButtonStr + eventsButtonStr + \
|
||||||
|
moderationButtonStr + happeningStr + newPostButtonStr
|
||||||
|
|
||||||
|
if not newsHeader:
|
||||||
|
if not iconsAsButtons:
|
||||||
|
# the search icon
|
||||||
|
tlStr += \
|
||||||
|
'<a class="imageAnchor" href="' + usersPath + \
|
||||||
|
'/search"><img loading="lazy" src="/' + \
|
||||||
|
iconsPath + '/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">' + \
|
||||||
|
'<span>' + translate['Search'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
# benchmark 5
|
||||||
|
timeDiff = int((time.time() - timelineStartTime) * 1000)
|
||||||
|
if timeDiff > 100:
|
||||||
|
print('TIMELINE TIMING ' + boxName + ' 5 = ' + str(timeDiff))
|
||||||
|
|
||||||
|
# the calendar button
|
||||||
|
if not isFeaturesTimeline:
|
||||||
|
calendarAltText = translate['Calendar']
|
||||||
|
if newCalendarEvent:
|
||||||
|
# indicate that the calendar icon is highlighted
|
||||||
|
calendarAltText = '*' + calendarAltText + '*'
|
||||||
|
if not iconsAsButtons:
|
||||||
|
tlStr += \
|
||||||
|
' <a class="imageAnchor" href="' + \
|
||||||
|
usersPath + calendarPath + \
|
||||||
|
'"><img loading="lazy" src="/' + iconsPath + '/' + \
|
||||||
|
calendarImage + '" title="' + translate['Calendar'] + \
|
||||||
|
'" alt="| ' + calendarAltText + \
|
||||||
|
'" class="timelineicon"/></a>\n'
|
||||||
|
else:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + calendarPath + \
|
||||||
|
'"><button class="button">' + \
|
||||||
|
'<span>' + translate['Calendar'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
if not newsHeader:
|
||||||
|
# the show/hide button, for a simpler header appearance
|
||||||
|
if not iconsAsButtons:
|
||||||
|
tlStr += \
|
||||||
|
' <a class="imageAnchor" href="' + \
|
||||||
|
usersPath + '/minimal' + \
|
||||||
|
'"><img loading="lazy" src="/' + iconsPath + \
|
||||||
|
'/showhide.png" title="' + translate['Show/Hide Buttons'] + \
|
||||||
|
'" alt="| ' + translate['Show/Hide Buttons'] + \
|
||||||
|
'" class="timelineicon"/></a>\n'
|
||||||
|
else:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + '/minimal' + \
|
||||||
|
'"><button class="button">' + \
|
||||||
|
'<span>' + translate['Show/Hide Buttons'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
if newsHeader:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + '/inbox">' + \
|
||||||
|
'<button class="button">' + \
|
||||||
|
'<span>' + translate['User'] + '</span></button></a>'
|
||||||
|
|
||||||
|
# the newswire button to show right column links
|
||||||
|
if not iconsAsButtons:
|
||||||
|
tlStr += \
|
||||||
|
'<a class="imageAnchorMobile" href="' + \
|
||||||
|
usersPath + '/newswiremobile">' + \
|
||||||
|
'<img loading="lazy" src="/' + iconsPath + \
|
||||||
|
'/newswire.png" title="' + translate['News'] + \
|
||||||
|
'" alt="| ' + translate['News'] + \
|
||||||
|
'" class="timelineicon"/></a>'
|
||||||
|
else:
|
||||||
|
# NOTE: deliberately no \n at end of line
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + \
|
||||||
|
usersPath + '/newswiremobile' + \
|
||||||
|
'"><button class="buttonMobile">' + \
|
||||||
|
'<span>' + translate['Newswire'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
# the links button to show left column links
|
||||||
|
if not iconsAsButtons:
|
||||||
|
tlStr += \
|
||||||
|
'<a class="imageAnchorMobile" href="' + \
|
||||||
|
usersPath + '/linksmobile">' + \
|
||||||
|
'<img loading="lazy" src="/' + iconsPath + \
|
||||||
|
'/links.png" title="' + translate['Edit Links'] + \
|
||||||
|
'" alt="| ' + translate['Edit Links'] + \
|
||||||
|
'" class="timelineicon"/></a>'
|
||||||
|
else:
|
||||||
|
# NOTE: deliberately no \n at end of line
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + \
|
||||||
|
usersPath + '/linksmobile' + \
|
||||||
|
'"><button class="buttonMobile">' + \
|
||||||
|
'<span>' + translate['Links'] + \
|
||||||
|
'</span></button></a>'
|
||||||
|
|
||||||
|
if newsHeader:
|
||||||
|
tlStr += \
|
||||||
|
'<a href="' + usersPath + '/editprofile">' + \
|
||||||
|
'<button class="buttonDesktop">' + \
|
||||||
|
'<span>' + translate['Settings'] + '</span></button></a>'
|
||||||
|
|
||||||
|
if not newsHeader:
|
||||||
|
tlStr += followApprovals
|
||||||
|
|
||||||
|
if not iconsAsButtons:
|
||||||
|
# end of headericons div
|
||||||
|
tlStr += '</div>'
|
||||||
|
|
||||||
|
# end of the button header with inbox, outbox, etc
|
||||||
|
tlStr += ' </div>\n'
|
||||||
|
return tlStr
|
||||||
|
|
@ -758,7 +758,8 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
iconsPath + '/repeat_inactive.png" ' + \
|
iconsPath + '/repeat_inactive.png" ' + \
|
||||||
'class="announceOrReply"/>\n' + \
|
'class="announceOrReply"/>\n' + \
|
||||||
' <a href="' + \
|
' <a href="' + \
|
||||||
postJsonObject['object']['id'] + '">' + \
|
postJsonObject['object']['id'] + '" ' + \
|
||||||
|
'class="announceOrReply">' + \
|
||||||
announceDisplayName + '</a>\n'
|
announceDisplayName + '</a>\n'
|
||||||
# show avatar of person replied to
|
# show avatar of person replied to
|
||||||
announceActor = \
|
announceActor = \
|
||||||
|
|
@ -804,7 +805,8 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
'/repeat_inactive.png" ' + \
|
'/repeat_inactive.png" ' + \
|
||||||
'class="announceOrReply"/>\n' + \
|
'class="announceOrReply"/>\n' + \
|
||||||
' <a href="' + \
|
' <a href="' + \
|
||||||
postJsonObject['object']['id'] + '">@' + \
|
postJsonObject['object']['id'] + '" ' + \
|
||||||
|
'class="announceOrReply">@' + \
|
||||||
announceNickname + '@' + \
|
announceNickname + '@' + \
|
||||||
announceDomain + '</a>\n'
|
announceDomain + '</a>\n'
|
||||||
else:
|
else:
|
||||||
|
|
@ -816,7 +818,7 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
'class="announceOrReply"/>\n' + \
|
'class="announceOrReply"/>\n' + \
|
||||||
' <a href="' + \
|
' <a href="' + \
|
||||||
postJsonObject['object']['id'] + \
|
postJsonObject['object']['id'] + \
|
||||||
'">@unattributed</a>\n'
|
'" class="announceOrReply">@unattributed</a>\n'
|
||||||
else:
|
else:
|
||||||
titleStr += \
|
titleStr += \
|
||||||
' ' + \
|
' ' + \
|
||||||
|
|
@ -826,7 +828,8 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
'/repeat_inactive.png" ' + \
|
'/repeat_inactive.png" ' + \
|
||||||
'class="announceOrReply"/>\n' + \
|
'class="announceOrReply"/>\n' + \
|
||||||
' <a href="' + \
|
' <a href="' + \
|
||||||
postJsonObject['object']['id'] + '">@unattributed</a>\n'
|
postJsonObject['object']['id'] + '" ' + \
|
||||||
|
'class="announceOrReply">@unattributed</a>\n'
|
||||||
else:
|
else:
|
||||||
if postJsonObject['object'].get('inReplyTo'):
|
if postJsonObject['object'].get('inReplyTo'):
|
||||||
containerClassIcons = 'containericons darker'
|
containerClassIcons = 'containericons darker'
|
||||||
|
|
@ -892,7 +895,8 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
'class="announceOrReply"/>\n' + \
|
'class="announceOrReply"/>\n' + \
|
||||||
' ' + \
|
' ' + \
|
||||||
'<a href="' + inReplyTo + \
|
'<a href="' + inReplyTo + \
|
||||||
'">' + replyDisplayName + '</a>\n'
|
'" class="announceOrReply">' + \
|
||||||
|
replyDisplayName + '</a>\n'
|
||||||
|
|
||||||
# benchmark 13.7
|
# benchmark 13.7
|
||||||
if not allowDownloads:
|
if not allowDownloads:
|
||||||
|
|
@ -953,7 +957,8 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
iconsPath + '/reply.png" ' + \
|
iconsPath + '/reply.png" ' + \
|
||||||
'class="announceOrReply"/>\n' + \
|
'class="announceOrReply"/>\n' + \
|
||||||
' <a href="' + \
|
' <a href="' + \
|
||||||
inReplyTo + '">@' + \
|
inReplyTo + '" ' + \
|
||||||
|
'class="announceOrReply">@' + \
|
||||||
replyNickname + '@' + \
|
replyNickname + '@' + \
|
||||||
replyDomain + '</a>\n'
|
replyDomain + '</a>\n'
|
||||||
else:
|
else:
|
||||||
|
|
@ -967,7 +972,7 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
'/reply.png" class="announceOrReply"/>\n' + \
|
'/reply.png" class="announceOrReply"/>\n' + \
|
||||||
' <a href="' + \
|
' <a href="' + \
|
||||||
postJsonObject['object']['inReplyTo'] + \
|
postJsonObject['object']['inReplyTo'] + \
|
||||||
'">@unknown</a>\n'
|
'" class="announceOrReply">@unknown</a>\n'
|
||||||
else:
|
else:
|
||||||
postDomain = \
|
postDomain = \
|
||||||
postJsonObject['object']['inReplyTo']
|
postJsonObject['object']['inReplyTo']
|
||||||
|
|
@ -986,7 +991,8 @@ def individualPostAsHtml(allowDownloads: bool,
|
||||||
'class="announceOrReply"/>\n' + \
|
'class="announceOrReply"/>\n' + \
|
||||||
' <a href="' + \
|
' <a href="' + \
|
||||||
postJsonObject['object']['inReplyTo'] + \
|
postJsonObject['object']['inReplyTo'] + \
|
||||||
'">' + postDomain + '</a>\n'
|
'" class="announceOrReply">' + \
|
||||||
|
postDomain + '</a>\n'
|
||||||
|
|
||||||
# benchmark 14
|
# benchmark 14
|
||||||
if not allowDownloads:
|
if not allowDownloads:
|
||||||
|
|
|
||||||
|
|
@ -614,9 +614,6 @@ def htmlProfile(rssIconAtTop: bool,
|
||||||
if isSystemAccount(nickname):
|
if isSystemAccount(nickname):
|
||||||
bannerFile, bannerFilename = \
|
bannerFile, bannerFilename = \
|
||||||
getBannerFile(baseDir, nickname, domain)
|
getBannerFile(baseDir, nickname, domain)
|
||||||
# profileStyle = \
|
|
||||||
# profileStyle.replace('banner.png',
|
|
||||||
# '/users/' + nickname + '/' + bannerFile)
|
|
||||||
|
|
||||||
licenseStr = \
|
licenseStr = \
|
||||||
'<a href="https://gitlab.com/bashrc2/epicyon">' + \
|
'<a href="https://gitlab.com/bashrc2/epicyon">' + \
|
||||||
|
|
@ -733,7 +730,7 @@ def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
showPublishedDateOnly,
|
showPublishedDateOnly,
|
||||||
False, False, False, True, False)
|
False, False, False, True, False)
|
||||||
if postStr:
|
if postStr:
|
||||||
profileStr += separatorStr + postStr
|
profileStr += postStr + separatorStr
|
||||||
ctr += 1
|
ctr += 1
|
||||||
if ctr >= maxItems:
|
if ctr >= maxItems:
|
||||||
break
|
break
|
||||||
|
|
|
||||||
493
webapp_search.py
|
|
@ -28,7 +28,7 @@ from webapp_utils import htmlFooter
|
||||||
from webapp_utils import getSearchBannerFile
|
from webapp_utils import getSearchBannerFile
|
||||||
from webapp_utils import htmlPostSeparator
|
from webapp_utils import htmlPostSeparator
|
||||||
from webapp_post import individualPostAsHtml
|
from webapp_post import individualPostAsHtml
|
||||||
from blocking import isBlockedHashtag
|
from webapp_hashtagswarm import htmlHashTagSwarm
|
||||||
|
|
||||||
|
|
||||||
def htmlSearchEmoji(cssCache: {}, translate: {},
|
def htmlSearchEmoji(cssCache: {}, translate: {},
|
||||||
|
|
@ -372,7 +372,7 @@ def htmlSearch(cssCache: {}, translate: {},
|
||||||
'name="submitSearch">' + translate['Submit'] + '</button>\n'
|
'name="submitSearch">' + translate['Submit'] + '</button>\n'
|
||||||
followStr += ' </form>\n'
|
followStr += ' </form>\n'
|
||||||
followStr += ' <p class="hashtagswarm">' + \
|
followStr += ' <p class="hashtagswarm">' + \
|
||||||
htmlHashTagSwarm(baseDir, actor) + '</p>\n'
|
htmlHashTagSwarm(baseDir, actor, translate) + '</p>\n'
|
||||||
followStr += ' </center>\n'
|
followStr += ' </center>\n'
|
||||||
followStr += ' </div>\n'
|
followStr += ' </div>\n'
|
||||||
followStr += '</div>\n'
|
followStr += '</div>\n'
|
||||||
|
|
@ -380,79 +380,221 @@ def htmlSearch(cssCache: {}, translate: {},
|
||||||
return followStr
|
return followStr
|
||||||
|
|
||||||
|
|
||||||
def htmlHashTagSwarm(baseDir: str, actor: str) -> str:
|
def htmlSkillsSearch(cssCache: {}, translate: {}, baseDir: str,
|
||||||
"""Returns a tag swarm of today's hashtags
|
httpPrefix: str,
|
||||||
|
skillsearch: str, instanceOnly: bool,
|
||||||
|
postsPerPage: int) -> str:
|
||||||
|
"""Show a page containing search results for a skill
|
||||||
"""
|
"""
|
||||||
currTime = datetime.utcnow()
|
if skillsearch.startswith('*'):
|
||||||
daysSinceEpoch = (currTime - datetime(1970, 1, 1)).days
|
skillsearch = skillsearch[1:].strip()
|
||||||
daysSinceEpochStr = str(daysSinceEpoch) + ' '
|
|
||||||
tagSwarm = []
|
|
||||||
|
|
||||||
for subdir, dirs, files in os.walk(baseDir + '/tags'):
|
skillsearch = skillsearch.lower().strip('\n').strip('\r')
|
||||||
|
|
||||||
|
results = []
|
||||||
|
# search instance accounts
|
||||||
|
for subdir, dirs, files in os.walk(baseDir + '/accounts/'):
|
||||||
for f in files:
|
for f in files:
|
||||||
tagsFilename = os.path.join(baseDir + '/tags', f)
|
if not f.endswith('.json'):
|
||||||
if not os.path.isfile(tagsFilename):
|
|
||||||
continue
|
continue
|
||||||
# get last modified datetime
|
if '@' not in f:
|
||||||
modTimesinceEpoc = os.path.getmtime(tagsFilename)
|
|
||||||
lastModifiedDate = datetime.fromtimestamp(modTimesinceEpoc)
|
|
||||||
fileDaysSinceEpoch = (lastModifiedDate - datetime(1970, 1, 1)).days
|
|
||||||
# check if the file was last modified today
|
|
||||||
if fileDaysSinceEpoch != daysSinceEpoch:
|
|
||||||
continue
|
continue
|
||||||
|
if f.startswith('inbox@'):
|
||||||
|
continue
|
||||||
|
actorFilename = os.path.join(subdir, f)
|
||||||
|
actorJson = loadJson(actorFilename)
|
||||||
|
if actorJson:
|
||||||
|
if actorJson.get('id') and \
|
||||||
|
actorJson.get('skills') and \
|
||||||
|
actorJson.get('name') and \
|
||||||
|
actorJson.get('icon'):
|
||||||
|
actor = actorJson['id']
|
||||||
|
for skillName, skillLevel in actorJson['skills'].items():
|
||||||
|
skillName = skillName.lower()
|
||||||
|
if not (skillName in skillsearch or
|
||||||
|
skillsearch in skillName):
|
||||||
|
continue
|
||||||
|
skillLevelStr = str(skillLevel)
|
||||||
|
if skillLevel < 100:
|
||||||
|
skillLevelStr = '0' + skillLevelStr
|
||||||
|
if skillLevel < 10:
|
||||||
|
skillLevelStr = '0' + skillLevelStr
|
||||||
|
indexStr = \
|
||||||
|
skillLevelStr + ';' + actor + ';' + \
|
||||||
|
actorJson['name'] + \
|
||||||
|
';' + actorJson['icon']['url']
|
||||||
|
if indexStr not in results:
|
||||||
|
results.append(indexStr)
|
||||||
|
if not instanceOnly:
|
||||||
|
# search actor cache
|
||||||
|
for subdir, dirs, files in os.walk(baseDir + '/cache/actors/'):
|
||||||
|
for f in files:
|
||||||
|
if not f.endswith('.json'):
|
||||||
|
continue
|
||||||
|
if '@' not in f:
|
||||||
|
continue
|
||||||
|
if f.startswith('inbox@'):
|
||||||
|
continue
|
||||||
|
actorFilename = os.path.join(subdir, f)
|
||||||
|
cachedActorJson = loadJson(actorFilename)
|
||||||
|
if cachedActorJson:
|
||||||
|
if cachedActorJson.get('actor'):
|
||||||
|
actorJson = cachedActorJson['actor']
|
||||||
|
if actorJson.get('id') and \
|
||||||
|
actorJson.get('skills') and \
|
||||||
|
actorJson.get('name') and \
|
||||||
|
actorJson.get('icon'):
|
||||||
|
actor = actorJson['id']
|
||||||
|
for skillName, skillLevel in \
|
||||||
|
actorJson['skills'].items():
|
||||||
|
skillName = skillName.lower()
|
||||||
|
if not (skillName in skillsearch or
|
||||||
|
skillsearch in skillName):
|
||||||
|
continue
|
||||||
|
skillLevelStr = str(skillLevel)
|
||||||
|
if skillLevel < 100:
|
||||||
|
skillLevelStr = '0' + skillLevelStr
|
||||||
|
if skillLevel < 10:
|
||||||
|
skillLevelStr = '0' + skillLevelStr
|
||||||
|
indexStr = \
|
||||||
|
skillLevelStr + ';' + actor + ';' + \
|
||||||
|
actorJson['name'] + \
|
||||||
|
';' + actorJson['icon']['url']
|
||||||
|
if indexStr not in results:
|
||||||
|
results.append(indexStr)
|
||||||
|
|
||||||
hashTagName = f.split('.')[0]
|
results.sort(reverse=True)
|
||||||
if isBlockedHashtag(baseDir, hashTagName):
|
|
||||||
continue
|
|
||||||
if daysSinceEpochStr not in open(tagsFilename).read():
|
|
||||||
continue
|
|
||||||
with open(tagsFilename, 'r') as tagsFile:
|
|
||||||
line = tagsFile.readline()
|
|
||||||
lineCtr = 1
|
|
||||||
tagCtr = 0
|
|
||||||
maxLineCtr = 1
|
|
||||||
while line:
|
|
||||||
if ' ' not in line:
|
|
||||||
line = tagsFile.readline()
|
|
||||||
lineCtr += 1
|
|
||||||
# don't read too many lines
|
|
||||||
if lineCtr >= maxLineCtr:
|
|
||||||
break
|
|
||||||
continue
|
|
||||||
postDaysSinceEpochStr = line.split(' ')[0]
|
|
||||||
if not postDaysSinceEpochStr.isdigit():
|
|
||||||
line = tagsFile.readline()
|
|
||||||
lineCtr += 1
|
|
||||||
# don't read too many lines
|
|
||||||
if lineCtr >= maxLineCtr:
|
|
||||||
break
|
|
||||||
continue
|
|
||||||
postDaysSinceEpoch = int(postDaysSinceEpochStr)
|
|
||||||
if postDaysSinceEpoch < daysSinceEpoch:
|
|
||||||
break
|
|
||||||
if postDaysSinceEpoch == daysSinceEpoch:
|
|
||||||
if tagCtr == 0:
|
|
||||||
tagSwarm.append(hashTagName)
|
|
||||||
tagCtr += 1
|
|
||||||
|
|
||||||
line = tagsFile.readline()
|
cssFilename = baseDir + '/epicyon-profile.css'
|
||||||
lineCtr += 1
|
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||||
# don't read too many lines
|
cssFilename = baseDir + '/epicyon.css'
|
||||||
if lineCtr >= maxLineCtr:
|
|
||||||
break
|
|
||||||
|
|
||||||
if not tagSwarm:
|
skillSearchForm = htmlHeaderWithExternalStyle(cssFilename)
|
||||||
return ''
|
skillSearchForm += \
|
||||||
tagSwarm.sort()
|
'<center><h1>' + translate['Skills search'] + ': ' + \
|
||||||
tagSwarmStr = ''
|
skillsearch + '</h1></center>'
|
||||||
ctr = 0
|
|
||||||
for tagName in tagSwarm:
|
if len(results) == 0:
|
||||||
tagSwarmStr += \
|
skillSearchForm += \
|
||||||
'<a href="' + actor + '/tags/' + tagName + \
|
'<center><h5>' + translate['No results'] + \
|
||||||
'" class="hashtagswarm">' + tagName + '</a>\n'
|
'</h5></center>'
|
||||||
ctr += 1
|
else:
|
||||||
tagSwarmHtml = tagSwarmStr.strip() + '\n'
|
skillSearchForm += '<center>'
|
||||||
return tagSwarmHtml
|
ctr = 0
|
||||||
|
for skillMatch in results:
|
||||||
|
skillMatchFields = skillMatch.split(';')
|
||||||
|
if len(skillMatchFields) != 4:
|
||||||
|
continue
|
||||||
|
actor = skillMatchFields[1]
|
||||||
|
actorName = skillMatchFields[2]
|
||||||
|
avatarUrl = skillMatchFields[3]
|
||||||
|
skillSearchForm += \
|
||||||
|
'<div class="search-result""><a href="' + \
|
||||||
|
actor + '/skills">'
|
||||||
|
skillSearchForm += \
|
||||||
|
'<img loading="lazy" src="' + avatarUrl + \
|
||||||
|
'"/><span class="search-result-text">' + actorName + \
|
||||||
|
'</span></a></div>'
|
||||||
|
ctr += 1
|
||||||
|
if ctr >= postsPerPage:
|
||||||
|
break
|
||||||
|
skillSearchForm += '</center>'
|
||||||
|
skillSearchForm += htmlFooter()
|
||||||
|
return skillSearchForm
|
||||||
|
|
||||||
|
|
||||||
|
def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str,
|
||||||
|
httpPrefix: str,
|
||||||
|
nickname: str, domain: str,
|
||||||
|
historysearch: str,
|
||||||
|
postsPerPage: int, pageNumber: int,
|
||||||
|
projectVersion: str,
|
||||||
|
recentPostsCache: {},
|
||||||
|
maxRecentPosts: int,
|
||||||
|
session,
|
||||||
|
wfRequest,
|
||||||
|
personCache: {},
|
||||||
|
port: int,
|
||||||
|
YTReplacementDomain: str,
|
||||||
|
showPublishedDateOnly: bool) -> str:
|
||||||
|
"""Show a page containing search results for your post history
|
||||||
|
"""
|
||||||
|
if historysearch.startswith('!'):
|
||||||
|
historysearch = historysearch[1:].strip()
|
||||||
|
|
||||||
|
historysearch = historysearch.lower().strip('\n').strip('\r')
|
||||||
|
|
||||||
|
boxFilenames = \
|
||||||
|
searchBoxPosts(baseDir, nickname, domain,
|
||||||
|
historysearch, postsPerPage)
|
||||||
|
|
||||||
|
cssFilename = baseDir + '/epicyon-profile.css'
|
||||||
|
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||||
|
cssFilename = baseDir + '/epicyon.css'
|
||||||
|
|
||||||
|
historySearchForm = \
|
||||||
|
htmlHeaderWithExternalStyle(cssFilename)
|
||||||
|
|
||||||
|
# add the page title
|
||||||
|
historySearchForm += \
|
||||||
|
'<center><h1>' + translate['Your Posts'] + '</h1></center>'
|
||||||
|
|
||||||
|
if len(boxFilenames) == 0:
|
||||||
|
historySearchForm += \
|
||||||
|
'<center><h5>' + translate['No results'] + \
|
||||||
|
'</h5></center>'
|
||||||
|
return historySearchForm
|
||||||
|
|
||||||
|
iconsPath = getIconsWebPath(baseDir)
|
||||||
|
separatorStr = htmlPostSeparator(baseDir, None)
|
||||||
|
|
||||||
|
# ensure that the page number is in bounds
|
||||||
|
if not pageNumber:
|
||||||
|
pageNumber = 1
|
||||||
|
elif pageNumber < 1:
|
||||||
|
pageNumber = 1
|
||||||
|
|
||||||
|
# get the start end end within the index file
|
||||||
|
startIndex = int((pageNumber - 1) * postsPerPage)
|
||||||
|
endIndex = startIndex + postsPerPage
|
||||||
|
noOfBoxFilenames = len(boxFilenames)
|
||||||
|
if endIndex >= noOfBoxFilenames and noOfBoxFilenames > 0:
|
||||||
|
endIndex = noOfBoxFilenames - 1
|
||||||
|
|
||||||
|
index = startIndex
|
||||||
|
while index <= endIndex:
|
||||||
|
postFilename = boxFilenames[index]
|
||||||
|
if not postFilename:
|
||||||
|
index += 1
|
||||||
|
continue
|
||||||
|
postJsonObject = loadJson(postFilename)
|
||||||
|
if not postJsonObject:
|
||||||
|
index += 1
|
||||||
|
continue
|
||||||
|
showIndividualPostIcons = True
|
||||||
|
allowDeletion = False
|
||||||
|
postStr = \
|
||||||
|
individualPostAsHtml(True, recentPostsCache,
|
||||||
|
maxRecentPosts,
|
||||||
|
iconsPath, translate, None,
|
||||||
|
baseDir, session, wfRequest,
|
||||||
|
personCache,
|
||||||
|
nickname, domain, port,
|
||||||
|
postJsonObject,
|
||||||
|
None, True, allowDeletion,
|
||||||
|
httpPrefix, projectVersion,
|
||||||
|
'search',
|
||||||
|
YTReplacementDomain,
|
||||||
|
showPublishedDateOnly,
|
||||||
|
showIndividualPostIcons,
|
||||||
|
showIndividualPostIcons,
|
||||||
|
False, False, False)
|
||||||
|
if postStr:
|
||||||
|
historySearchForm += separatorStr + postStr
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
historySearchForm += htmlFooter()
|
||||||
|
return historySearchForm
|
||||||
|
|
||||||
|
|
||||||
def htmlHashtagSearch(cssCache: {},
|
def htmlHashtagSearch(cssCache: {},
|
||||||
|
|
@ -711,220 +853,3 @@ def rssHashtagSearch(nickname: str, domain: str, port: int,
|
||||||
break
|
break
|
||||||
|
|
||||||
return hashtagFeed + rss2TagFooter()
|
return hashtagFeed + rss2TagFooter()
|
||||||
|
|
||||||
|
|
||||||
def htmlSkillsSearch(cssCache: {}, translate: {}, baseDir: str,
|
|
||||||
httpPrefix: str,
|
|
||||||
skillsearch: str, instanceOnly: bool,
|
|
||||||
postsPerPage: int) -> str:
|
|
||||||
"""Show a page containing search results for a skill
|
|
||||||
"""
|
|
||||||
if skillsearch.startswith('*'):
|
|
||||||
skillsearch = skillsearch[1:].strip()
|
|
||||||
|
|
||||||
skillsearch = skillsearch.lower().strip('\n').strip('\r')
|
|
||||||
|
|
||||||
results = []
|
|
||||||
# search instance accounts
|
|
||||||
for subdir, dirs, files in os.walk(baseDir + '/accounts/'):
|
|
||||||
for f in files:
|
|
||||||
if not f.endswith('.json'):
|
|
||||||
continue
|
|
||||||
if '@' not in f:
|
|
||||||
continue
|
|
||||||
if f.startswith('inbox@'):
|
|
||||||
continue
|
|
||||||
actorFilename = os.path.join(subdir, f)
|
|
||||||
actorJson = loadJson(actorFilename)
|
|
||||||
if actorJson:
|
|
||||||
if actorJson.get('id') and \
|
|
||||||
actorJson.get('skills') and \
|
|
||||||
actorJson.get('name') and \
|
|
||||||
actorJson.get('icon'):
|
|
||||||
actor = actorJson['id']
|
|
||||||
for skillName, skillLevel in actorJson['skills'].items():
|
|
||||||
skillName = skillName.lower()
|
|
||||||
if not (skillName in skillsearch or
|
|
||||||
skillsearch in skillName):
|
|
||||||
continue
|
|
||||||
skillLevelStr = str(skillLevel)
|
|
||||||
if skillLevel < 100:
|
|
||||||
skillLevelStr = '0' + skillLevelStr
|
|
||||||
if skillLevel < 10:
|
|
||||||
skillLevelStr = '0' + skillLevelStr
|
|
||||||
indexStr = \
|
|
||||||
skillLevelStr + ';' + actor + ';' + \
|
|
||||||
actorJson['name'] + \
|
|
||||||
';' + actorJson['icon']['url']
|
|
||||||
if indexStr not in results:
|
|
||||||
results.append(indexStr)
|
|
||||||
if not instanceOnly:
|
|
||||||
# search actor cache
|
|
||||||
for subdir, dirs, files in os.walk(baseDir + '/cache/actors/'):
|
|
||||||
for f in files:
|
|
||||||
if not f.endswith('.json'):
|
|
||||||
continue
|
|
||||||
if '@' not in f:
|
|
||||||
continue
|
|
||||||
if f.startswith('inbox@'):
|
|
||||||
continue
|
|
||||||
actorFilename = os.path.join(subdir, f)
|
|
||||||
cachedActorJson = loadJson(actorFilename)
|
|
||||||
if cachedActorJson:
|
|
||||||
if cachedActorJson.get('actor'):
|
|
||||||
actorJson = cachedActorJson['actor']
|
|
||||||
if actorJson.get('id') and \
|
|
||||||
actorJson.get('skills') and \
|
|
||||||
actorJson.get('name') and \
|
|
||||||
actorJson.get('icon'):
|
|
||||||
actor = actorJson['id']
|
|
||||||
for skillName, skillLevel in \
|
|
||||||
actorJson['skills'].items():
|
|
||||||
skillName = skillName.lower()
|
|
||||||
if not (skillName in skillsearch or
|
|
||||||
skillsearch in skillName):
|
|
||||||
continue
|
|
||||||
skillLevelStr = str(skillLevel)
|
|
||||||
if skillLevel < 100:
|
|
||||||
skillLevelStr = '0' + skillLevelStr
|
|
||||||
if skillLevel < 10:
|
|
||||||
skillLevelStr = '0' + skillLevelStr
|
|
||||||
indexStr = \
|
|
||||||
skillLevelStr + ';' + actor + ';' + \
|
|
||||||
actorJson['name'] + \
|
|
||||||
';' + actorJson['icon']['url']
|
|
||||||
if indexStr not in results:
|
|
||||||
results.append(indexStr)
|
|
||||||
|
|
||||||
results.sort(reverse=True)
|
|
||||||
|
|
||||||
cssFilename = baseDir + '/epicyon-profile.css'
|
|
||||||
if os.path.isfile(baseDir + '/epicyon.css'):
|
|
||||||
cssFilename = baseDir + '/epicyon.css'
|
|
||||||
|
|
||||||
skillSearchForm = htmlHeaderWithExternalStyle(cssFilename)
|
|
||||||
skillSearchForm += \
|
|
||||||
'<center><h1>' + translate['Skills search'] + ': ' + \
|
|
||||||
skillsearch + '</h1></center>'
|
|
||||||
|
|
||||||
if len(results) == 0:
|
|
||||||
skillSearchForm += \
|
|
||||||
'<center><h5>' + translate['No results'] + \
|
|
||||||
'</h5></center>'
|
|
||||||
else:
|
|
||||||
skillSearchForm += '<center>'
|
|
||||||
ctr = 0
|
|
||||||
for skillMatch in results:
|
|
||||||
skillMatchFields = skillMatch.split(';')
|
|
||||||
if len(skillMatchFields) != 4:
|
|
||||||
continue
|
|
||||||
actor = skillMatchFields[1]
|
|
||||||
actorName = skillMatchFields[2]
|
|
||||||
avatarUrl = skillMatchFields[3]
|
|
||||||
skillSearchForm += \
|
|
||||||
'<div class="search-result""><a href="' + \
|
|
||||||
actor + '/skills">'
|
|
||||||
skillSearchForm += \
|
|
||||||
'<img loading="lazy" src="' + avatarUrl + \
|
|
||||||
'"/><span class="search-result-text">' + actorName + \
|
|
||||||
'</span></a></div>'
|
|
||||||
ctr += 1
|
|
||||||
if ctr >= postsPerPage:
|
|
||||||
break
|
|
||||||
skillSearchForm += '</center>'
|
|
||||||
skillSearchForm += htmlFooter()
|
|
||||||
return skillSearchForm
|
|
||||||
|
|
||||||
|
|
||||||
def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str,
|
|
||||||
httpPrefix: str,
|
|
||||||
nickname: str, domain: str,
|
|
||||||
historysearch: str,
|
|
||||||
postsPerPage: int, pageNumber: int,
|
|
||||||
projectVersion: str,
|
|
||||||
recentPostsCache: {},
|
|
||||||
maxRecentPosts: int,
|
|
||||||
session,
|
|
||||||
wfRequest,
|
|
||||||
personCache: {},
|
|
||||||
port: int,
|
|
||||||
YTReplacementDomain: str,
|
|
||||||
showPublishedDateOnly: bool) -> str:
|
|
||||||
"""Show a page containing search results for your post history
|
|
||||||
"""
|
|
||||||
if historysearch.startswith('!'):
|
|
||||||
historysearch = historysearch[1:].strip()
|
|
||||||
|
|
||||||
historysearch = historysearch.lower().strip('\n').strip('\r')
|
|
||||||
|
|
||||||
boxFilenames = \
|
|
||||||
searchBoxPosts(baseDir, nickname, domain,
|
|
||||||
historysearch, postsPerPage)
|
|
||||||
|
|
||||||
cssFilename = baseDir + '/epicyon-profile.css'
|
|
||||||
if os.path.isfile(baseDir + '/epicyon.css'):
|
|
||||||
cssFilename = baseDir + '/epicyon.css'
|
|
||||||
|
|
||||||
historySearchForm = \
|
|
||||||
htmlHeaderWithExternalStyle(cssFilename)
|
|
||||||
|
|
||||||
# add the page title
|
|
||||||
historySearchForm += \
|
|
||||||
'<center><h1>' + translate['Your Posts'] + '</h1></center>'
|
|
||||||
|
|
||||||
if len(boxFilenames) == 0:
|
|
||||||
historySearchForm += \
|
|
||||||
'<center><h5>' + translate['No results'] + \
|
|
||||||
'</h5></center>'
|
|
||||||
return historySearchForm
|
|
||||||
|
|
||||||
iconsPath = getIconsWebPath(baseDir)
|
|
||||||
separatorStr = htmlPostSeparator(baseDir, None)
|
|
||||||
|
|
||||||
# ensure that the page number is in bounds
|
|
||||||
if not pageNumber:
|
|
||||||
pageNumber = 1
|
|
||||||
elif pageNumber < 1:
|
|
||||||
pageNumber = 1
|
|
||||||
|
|
||||||
# get the start end end within the index file
|
|
||||||
startIndex = int((pageNumber - 1) * postsPerPage)
|
|
||||||
endIndex = startIndex + postsPerPage
|
|
||||||
noOfBoxFilenames = len(boxFilenames)
|
|
||||||
if endIndex >= noOfBoxFilenames and noOfBoxFilenames > 0:
|
|
||||||
endIndex = noOfBoxFilenames - 1
|
|
||||||
|
|
||||||
index = startIndex
|
|
||||||
while index <= endIndex:
|
|
||||||
postFilename = boxFilenames[index]
|
|
||||||
if not postFilename:
|
|
||||||
index += 1
|
|
||||||
continue
|
|
||||||
postJsonObject = loadJson(postFilename)
|
|
||||||
if not postJsonObject:
|
|
||||||
index += 1
|
|
||||||
continue
|
|
||||||
showIndividualPostIcons = True
|
|
||||||
allowDeletion = False
|
|
||||||
postStr = \
|
|
||||||
individualPostAsHtml(True, recentPostsCache,
|
|
||||||
maxRecentPosts,
|
|
||||||
iconsPath, translate, None,
|
|
||||||
baseDir, session, wfRequest,
|
|
||||||
personCache,
|
|
||||||
nickname, domain, port,
|
|
||||||
postJsonObject,
|
|
||||||
None, True, allowDeletion,
|
|
||||||
httpPrefix, projectVersion,
|
|
||||||
'search',
|
|
||||||
YTReplacementDomain,
|
|
||||||
showPublishedDateOnly,
|
|
||||||
showIndividualPostIcons,
|
|
||||||
showIndividualPostIcons,
|
|
||||||
False, False, False)
|
|
||||||
if postStr:
|
|
||||||
historySearchForm += separatorStr + postStr
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
historySearchForm += htmlFooter()
|
|
||||||
return historySearchForm
|
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,22 @@ __email__ = "bob@freedombone.net"
|
||||||
__status__ = "Production"
|
__status__ = "Production"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
|
||||||
import time
|
import time
|
||||||
from utils import removeIdEnding
|
from utils import removeIdEnding
|
||||||
from follow import followerApprovalActive
|
from follow import followerApprovalActive
|
||||||
from person import isPersonSnoozed
|
from person import isPersonSnoozed
|
||||||
from happening import todaysEventsCheck
|
|
||||||
from happening import thisWeeksEventsCheck
|
|
||||||
from webapp_utils import getIconsWebPath
|
from webapp_utils import getIconsWebPath
|
||||||
from webapp_utils import htmlPostSeparator
|
from webapp_utils import htmlPostSeparator
|
||||||
from webapp_utils import getBannerFile
|
from webapp_utils import getBannerFile
|
||||||
from webapp_utils import htmlHeaderWithExternalStyle
|
from webapp_utils import htmlHeaderWithExternalStyle
|
||||||
from webapp_utils import htmlFooter
|
from webapp_utils import htmlFooter
|
||||||
from webapp_utils import sharesTimelineJson
|
from webapp_utils import sharesTimelineJson
|
||||||
|
from webapp_utils import htmlHighlightLabel
|
||||||
from webapp_post import preparePostFromHtmlCache
|
from webapp_post import preparePostFromHtmlCache
|
||||||
from webapp_post import individualPostAsHtml
|
from webapp_post import individualPostAsHtml
|
||||||
from webapp_column_left import getLeftColumnContent
|
from webapp_column_left import getLeftColumnContent
|
||||||
from webapp_column_right import getRightColumnContent
|
from webapp_column_right import getRightColumnContent
|
||||||
|
from webapp_headerbuttons import headerButtonsTimeline
|
||||||
from posts import isModerator
|
from posts import isModerator
|
||||||
from posts import isEditor
|
from posts import isEditor
|
||||||
|
|
||||||
|
|
@ -427,6 +426,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
||||||
calendarImage, followApprovals,
|
calendarImage, followApprovals,
|
||||||
iconsAsButtons)
|
iconsAsButtons)
|
||||||
|
|
||||||
|
tlStr += ' <div class="timeline-posts">\n'
|
||||||
|
|
||||||
# second row of buttons for moderator actions
|
# second row of buttons for moderator actions
|
||||||
if moderator and boxName == 'moderation':
|
if moderator and boxName == 'moderation':
|
||||||
tlStr += \
|
tlStr += \
|
||||||
|
|
@ -580,9 +581,9 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
||||||
|
|
||||||
if currTlStr:
|
if currTlStr:
|
||||||
itemCtr += 1
|
itemCtr += 1
|
||||||
|
tlStr += currTlStr
|
||||||
if separatorStr:
|
if separatorStr:
|
||||||
tlStr += separatorStr
|
tlStr += separatorStr
|
||||||
tlStr += currTlStr
|
|
||||||
if boxName == 'tlmedia':
|
if boxName == 'tlmedia':
|
||||||
tlStr += '</div>\n'
|
tlStr += '</div>\n'
|
||||||
|
|
||||||
|
|
@ -598,6 +599,9 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
||||||
translate['Page down'] + '"></a>\n' + \
|
translate['Page down'] + '"></a>\n' + \
|
||||||
' </center>\n'
|
' </center>\n'
|
||||||
|
|
||||||
|
# end of timeline-posts
|
||||||
|
tlStr += ' </div>\n'
|
||||||
|
|
||||||
# end of column-center
|
# end of column-center
|
||||||
tlStr += ' </td>\n'
|
tlStr += ' </td>\n'
|
||||||
|
|
||||||
|
|
@ -694,9 +698,10 @@ def htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int,
|
||||||
showRemoveButton = False
|
showRemoveButton = False
|
||||||
if item['actor'] == actor:
|
if item['actor'] == actor:
|
||||||
showRemoveButton = True
|
showRemoveButton = True
|
||||||
timelineStr += separatorStr + \
|
timelineStr += \
|
||||||
htmlIndividualShare(actor, item, translate,
|
htmlIndividualShare(actor, item, translate,
|
||||||
showContactButton, showRemoveButton)
|
showContactButton, showRemoveButton)
|
||||||
|
timelineStr += separatorStr
|
||||||
|
|
||||||
if not lastPage:
|
if not lastPage:
|
||||||
iconsPath = getIconsWebPath(baseDir)
|
iconsPath = getIconsWebPath(baseDir)
|
||||||
|
|
@ -712,332 +717,6 @@ def htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int,
|
||||||
return timelineStr
|
return timelineStr
|
||||||
|
|
||||||
|
|
||||||
def htmlHighlightLabel(label: str, highlight: bool) -> str:
|
|
||||||
"""If the give text should be highlighted then return
|
|
||||||
the appropriate markup.
|
|
||||||
This is so that in shell browsers, like lynx, it's possible
|
|
||||||
to see if the replies or DM button are highlighted.
|
|
||||||
"""
|
|
||||||
if not highlight:
|
|
||||||
return label
|
|
||||||
return '*' + str(label) + '*'
|
|
||||||
|
|
||||||
|
|
||||||
def headerButtonsTimeline(defaultTimeline: str,
|
|
||||||
boxName: str,
|
|
||||||
pageNumber: int,
|
|
||||||
translate: {},
|
|
||||||
usersPath: str,
|
|
||||||
mediaButton: str,
|
|
||||||
blogsButton: str,
|
|
||||||
newsButton: str,
|
|
||||||
inboxButton: str,
|
|
||||||
dmButton: str,
|
|
||||||
newDM: str,
|
|
||||||
repliesButton: str,
|
|
||||||
newReply: str,
|
|
||||||
minimal: bool,
|
|
||||||
sentButton: str,
|
|
||||||
sharesButtonStr: str,
|
|
||||||
bookmarksButtonStr: str,
|
|
||||||
eventsButtonStr: str,
|
|
||||||
moderationButtonStr: str,
|
|
||||||
newPostButtonStr: str,
|
|
||||||
baseDir: str,
|
|
||||||
nickname: str, domain: str,
|
|
||||||
iconsPath: str,
|
|
||||||
timelineStartTime,
|
|
||||||
newCalendarEvent: bool,
|
|
||||||
calendarPath: str,
|
|
||||||
calendarImage: str,
|
|
||||||
followApprovals: str,
|
|
||||||
iconsAsButtons: bool) -> str:
|
|
||||||
"""Returns the header at the top of the timeline, containing
|
|
||||||
buttons for inbox, outbox, search, calendar, etc
|
|
||||||
"""
|
|
||||||
# start of the button header with inbox, outbox, etc
|
|
||||||
tlStr = '<div class="containerHeader">\n'
|
|
||||||
# first button
|
|
||||||
if defaultTimeline == 'tlmedia':
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/tlmedia"><button class="' + \
|
|
||||||
mediaButton + '"><span>' + translate['Media'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
elif defaultTimeline == 'tlblogs':
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/tlblogs"><button class="' + \
|
|
||||||
blogsButton + '"><span>' + translate['Blogs'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
elif defaultTimeline == 'tlnews':
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/tlnews"><button class="' + \
|
|
||||||
newsButton + '"><span>' + translate['Features'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
else:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/inbox"><button class="' + \
|
|
||||||
inboxButton + '"><span>' + \
|
|
||||||
translate['Inbox'] + '</span></button></a>'
|
|
||||||
|
|
||||||
# if this is a news instance and we are viewing the news timeline
|
|
||||||
newsHeader = False
|
|
||||||
if defaultTimeline == 'tlnews' and boxName == 'tlnews':
|
|
||||||
newsHeader = True
|
|
||||||
|
|
||||||
if not newsHeader:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/dm"><button class="' + dmButton + \
|
|
||||||
'"><span>' + htmlHighlightLabel(translate['DM'], newDM) + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + '/tlreplies"><button class="' + \
|
|
||||||
repliesButton + '"><span>' + \
|
|
||||||
htmlHighlightLabel(translate['Replies'], newReply) + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
# typically the media button
|
|
||||||
if defaultTimeline != 'tlmedia':
|
|
||||||
if not minimal and not newsHeader:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/tlmedia"><button class="' + \
|
|
||||||
mediaButton + '"><span>' + translate['Media'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
else:
|
|
||||||
if not minimal:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/inbox"><button class="' + \
|
|
||||||
inboxButton+'"><span>' + translate['Inbox'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
isFeaturesTimeline = \
|
|
||||||
defaultTimeline == 'tlnews' and boxName == 'tlnews'
|
|
||||||
|
|
||||||
if not isFeaturesTimeline:
|
|
||||||
# typically the blogs button
|
|
||||||
# but may change if this is a blogging oriented instance
|
|
||||||
if defaultTimeline != 'tlblogs':
|
|
||||||
if not minimal and not isFeaturesTimeline:
|
|
||||||
titleStr = translate['Blogs']
|
|
||||||
if defaultTimeline == 'tlnews':
|
|
||||||
titleStr = translate['Article']
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/tlblogs"><button class="' + \
|
|
||||||
blogsButton + '"><span>' + titleStr + \
|
|
||||||
'</span></button></a>'
|
|
||||||
else:
|
|
||||||
if not minimal:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/inbox"><button class="' + \
|
|
||||||
inboxButton + '"><span>' + translate['Inbox'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
# typically the news button
|
|
||||||
# but may change if this is a news oriented instance
|
|
||||||
if defaultTimeline != 'tlnews':
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/tlnews"><button class="' + \
|
|
||||||
newsButton + '"><span>' + translate['News'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
else:
|
|
||||||
if not newsHeader:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/inbox"><button class="' + \
|
|
||||||
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 += \
|
|
||||||
'<a href="' + usersPath + \
|
|
||||||
'/outbox"><button class="' + \
|
|
||||||
sentButton + '"><span>' + translate['Outbox'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
# add other buttons
|
|
||||||
tlStr += \
|
|
||||||
sharesButtonStr + bookmarksButtonStr + eventsButtonStr + \
|
|
||||||
moderationButtonStr + happeningStr + newPostButtonStr
|
|
||||||
|
|
||||||
if not newsHeader:
|
|
||||||
if not iconsAsButtons:
|
|
||||||
# the search icon
|
|
||||||
tlStr += \
|
|
||||||
'<a class="imageAnchor" href="' + usersPath + \
|
|
||||||
'/search"><img loading="lazy" src="/' + \
|
|
||||||
iconsPath + '/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">' + \
|
|
||||||
'<span>' + translate['Search'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
# benchmark 5
|
|
||||||
timeDiff = int((time.time() - timelineStartTime) * 1000)
|
|
||||||
if timeDiff > 100:
|
|
||||||
print('TIMELINE TIMING ' + boxName + ' 5 = ' + str(timeDiff))
|
|
||||||
|
|
||||||
# the calendar button
|
|
||||||
if not isFeaturesTimeline:
|
|
||||||
calendarAltText = translate['Calendar']
|
|
||||||
if newCalendarEvent:
|
|
||||||
# indicate that the calendar icon is highlighted
|
|
||||||
calendarAltText = '*' + calendarAltText + '*'
|
|
||||||
if not iconsAsButtons:
|
|
||||||
tlStr += \
|
|
||||||
' <a class="imageAnchor" href="' + \
|
|
||||||
usersPath + calendarPath + \
|
|
||||||
'"><img loading="lazy" src="/' + iconsPath + '/' + \
|
|
||||||
calendarImage + '" title="' + translate['Calendar'] + \
|
|
||||||
'" alt="| ' + calendarAltText + \
|
|
||||||
'" class="timelineicon"/></a>\n'
|
|
||||||
else:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + calendarPath + \
|
|
||||||
'"><button class="button">' + \
|
|
||||||
'<span>' + translate['Calendar'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
if not newsHeader:
|
|
||||||
# the show/hide button, for a simpler header appearance
|
|
||||||
if not iconsAsButtons:
|
|
||||||
tlStr += \
|
|
||||||
' <a class="imageAnchor" href="' + \
|
|
||||||
usersPath + '/minimal' + \
|
|
||||||
'"><img loading="lazy" src="/' + iconsPath + \
|
|
||||||
'/showhide.png" title="' + translate['Show/Hide Buttons'] + \
|
|
||||||
'" alt="| ' + translate['Show/Hide Buttons'] + \
|
|
||||||
'" class="timelineicon"/></a>\n'
|
|
||||||
else:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + '/minimal' + \
|
|
||||||
'"><button class="button">' + \
|
|
||||||
'<span>' + translate['Show/Hide Buttons'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
if newsHeader:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + '/inbox">' + \
|
|
||||||
'<button class="button">' + \
|
|
||||||
'<span>' + translate['User'] + '</span></button></a>'
|
|
||||||
|
|
||||||
# the newswire button to show right column links
|
|
||||||
if not iconsAsButtons:
|
|
||||||
tlStr += \
|
|
||||||
'<a class="imageAnchorMobile" href="' + \
|
|
||||||
usersPath + '/newswiremobile">' + \
|
|
||||||
'<img loading="lazy" src="/' + iconsPath + \
|
|
||||||
'/newswire.png" title="' + translate['News'] + \
|
|
||||||
'" alt="| ' + translate['News'] + \
|
|
||||||
'" class="timelineicon"/></a>'
|
|
||||||
else:
|
|
||||||
# NOTE: deliberately no \n at end of line
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + \
|
|
||||||
usersPath + '/newswiremobile' + \
|
|
||||||
'"><button class="buttonMobile">' + \
|
|
||||||
'<span>' + translate['Newswire'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
# the links button to show left column links
|
|
||||||
if not iconsAsButtons:
|
|
||||||
tlStr += \
|
|
||||||
'<a class="imageAnchorMobile" href="' + \
|
|
||||||
usersPath + '/linksmobile">' + \
|
|
||||||
'<img loading="lazy" src="/' + iconsPath + \
|
|
||||||
'/links.png" title="' + translate['Edit Links'] + \
|
|
||||||
'" alt="| ' + translate['Edit Links'] + \
|
|
||||||
'" class="timelineicon"/></a>'
|
|
||||||
else:
|
|
||||||
# NOTE: deliberately no \n at end of line
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + \
|
|
||||||
usersPath + '/linksmobile' + \
|
|
||||||
'"><button class="buttonMobile">' + \
|
|
||||||
'<span>' + translate['Links'] + \
|
|
||||||
'</span></button></a>'
|
|
||||||
|
|
||||||
if newsHeader:
|
|
||||||
tlStr += \
|
|
||||||
'<a href="' + usersPath + '/editprofile">' + \
|
|
||||||
'<button class="buttonDesktop">' + \
|
|
||||||
'<span>' + translate['Settings'] + '</span></button></a>'
|
|
||||||
|
|
||||||
if not iconsAsButtons:
|
|
||||||
# end of headericons div
|
|
||||||
tlStr += '</div>'
|
|
||||||
|
|
||||||
if not newsHeader:
|
|
||||||
tlStr += followApprovals
|
|
||||||
# end of the button header with inbox, outbox, etc
|
|
||||||
tlStr += ' </div>\n'
|
|
||||||
return tlStr
|
|
||||||
|
|
||||||
|
|
||||||
def htmlShares(cssCache: {}, defaultTimeline: str,
|
def htmlShares(cssCache: {}, defaultTimeline: str,
|
||||||
recentPostsCache: {}, maxRecentPosts: int,
|
recentPostsCache: {}, maxRecentPosts: int,
|
||||||
translate: {}, pageNumber: int, itemsPerPage: int,
|
translate: {}, pageNumber: int, itemsPerPage: int,
|
||||||
|
|
|
||||||
|
|
@ -728,13 +728,15 @@ def htmlPostSeparator(baseDir: str, column: str) -> str:
|
||||||
iconsPath = getIconsWebPath(baseDir)
|
iconsPath = getIconsWebPath(baseDir)
|
||||||
theme = getConfigParam(baseDir, 'theme')
|
theme = getConfigParam(baseDir, 'theme')
|
||||||
filename = 'separator.png'
|
filename = 'separator.png'
|
||||||
|
separatorClass = "postSeparatorImage"
|
||||||
if column:
|
if column:
|
||||||
|
separatorClass = "postSeparatorImage" + column.title()
|
||||||
filename = 'separator_' + column + '.png'
|
filename = 'separator_' + column + '.png'
|
||||||
separatorImageFilename = baseDir + '/theme/' + theme + '/icons/' + filename
|
separatorImageFilename = baseDir + '/theme/' + theme + '/icons/' + filename
|
||||||
separatorStr = ''
|
separatorStr = ''
|
||||||
if os.path.isfile(separatorImageFilename):
|
if os.path.isfile(separatorImageFilename):
|
||||||
separatorStr = \
|
separatorStr = \
|
||||||
'<div class="postSeparatorImage"><center>' + \
|
'<div class="' + separatorClass + '"><center>' + \
|
||||||
'<img src="/' + iconsPath + '/' + filename + '"/>' + \
|
'<img src="/' + iconsPath + '/' + filename + '"/>' + \
|
||||||
'</center></div>\n'
|
'</center></div>\n'
|
||||||
return separatorStr
|
return separatorStr
|
||||||
|
|
@ -808,3 +810,14 @@ def headerButtonsFrontScreen(translate: {},
|
||||||
headerStr + \
|
headerStr + \
|
||||||
' </div>\n'
|
' </div>\n'
|
||||||
return headerStr
|
return headerStr
|
||||||
|
|
||||||
|
|
||||||
|
def htmlHighlightLabel(label: str, highlight: bool) -> str:
|
||||||
|
"""If the given text should be highlighted then return
|
||||||
|
the appropriate markup.
|
||||||
|
This is so that in shell browsers, like lynx, it's possible
|
||||||
|
to see if the replies or DM button are highlighted.
|
||||||
|
"""
|
||||||
|
if not highlight:
|
||||||
|
return label
|
||||||
|
return '*' + str(label) + '*'
|
||||||
|
|
|
||||||