uhhhh
|
@ -0,0 +1,60 @@
|
|||
#global-menu li a:hover, #global-menu li button:hover {
|
||||
box-shadow: inset 0 -4px 0 -1px var(--theme);
|
||||
}
|
||||
h2.label,
|
||||
#global-menu #global-my-menu .symbol:before,
|
||||
.feeling-selector .feeling-button.checked,
|
||||
.sidebar-setting .symbol:before,
|
||||
.post-meta .yeah-added + .yeah,
|
||||
.reply-meta .yeah-added + .yeah,
|
||||
.post-meta .yeah-added + .yeah:before,
|
||||
.reply-meta .yeah-added + .yeah:before,
|
||||
.user-organization,
|
||||
.user-sidebar .follow-button:before,
|
||||
.user-sidebar .friend-button:before,
|
||||
.list .toggle-button .follow-button:before,
|
||||
.list .toggle-button .follow-done-button:before,
|
||||
.news-list a.link,
|
||||
#admin-menu li.selected a {
|
||||
color: var(--theme);
|
||||
}
|
||||
#global-menu li.selected a,
|
||||
#global-menu li.selected a:before {
|
||||
color: var(--theme) !important;
|
||||
}
|
||||
#nprogress .bar,
|
||||
h2.reply-label,
|
||||
#cookie-policy-notice a:hover,
|
||||
#cookie-policy-notice button:hover,
|
||||
#cookie-policy-notice .cookie-policy-notice,
|
||||
#cookie-policy-notice .cookie-policy-notice:hover,
|
||||
.user-data h4 span,
|
||||
.index-memo h2:not(.label),
|
||||
#help .help-content h2,
|
||||
.tab2 a.selected, .tab3 a.selected,
|
||||
.textarea-with-menu.active-text .textarea-menu-text,
|
||||
.textarea-with-menu.active-memo .textarea-menu-memo,
|
||||
.textarea-with-menu.active-poll .textarea-menu-poll,
|
||||
.textarea-with-menu .textarea-poll .delete:hover {
|
||||
background: var(--theme);
|
||||
}
|
||||
h2.label {
|
||||
border-bottom: 3px solid var(--theme);
|
||||
}
|
||||
h2.reply-label {
|
||||
border-top: 1px solid var(--theme);
|
||||
}
|
||||
.dialog .window-title {
|
||||
border-top: 1px solid var(--theme);
|
||||
border-bottom: 1px solid var(--theme);
|
||||
background: var(--theme);
|
||||
}
|
||||
#reply-content .list .my,
|
||||
#reply-content .list > li.my:hover {
|
||||
background: var(--theme-light);
|
||||
}
|
||||
@media screen and (max-width: 980px) {
|
||||
#global-menu li.selected a {
|
||||
border-bottom: 2px solid var(--theme);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,494 @@
|
|||
/* Credits:
|
||||
CodyMKW - created the original version for Miiverse
|
||||
Arian K. - made an updated version for Closedverse
|
||||
PF2M - colorized everything and later updated it for Indigo
|
||||
*/
|
||||
#sidebar-profile-body .nick-name, .news-list .nick-name, .list-content-with-icon-and-text .nick-name a, .user-name a, .file-button-container, .file-button {
|
||||
color: #00fbff;
|
||||
}
|
||||
.post-permalink-button:hover, .favorite-community-link.symbol, .trigger:active, #wrapper, #reply-content button.more-button:hover, .spoiler-button:hover, #sidebar-profile-status, #guide-menu .arrow-button:hover, .community-eyecatch-info:hover, #community-favorite:hover, .big-button:hover, .sidebar-setting a:hover, .multi-timeline-post-list .post.hidden .screenshot-container, .multi-timeline-post-list .post .screenshot-container, .trigger:hover, .textarea-container .community-container, .post-form-album-content, .admin-messages .post.my, #identified-user-banner:hover, .sidebar-setting a.selected, .sidebar-container h4 a:hover, #yeah-content, .sidebar-profile .profile-comment, .post-list .recent-reply-content, .post-list .recent-reply-content .recent-reply-read-more-container:hover, .community-name .owner, .post-list-heading-button, .topic-title-input, .textarea, .yeah-button, .hidden-content-button, .community-description, .file-button-container, .file-button, .repost-error, .messages .post.my, #reply-content .list .my {
|
||||
background-color: var(--theme-darker, #006466) !important;
|
||||
}
|
||||
body, #footer, .favorite-community-link.symbol:hover, .post.hidden:hover, .filtering-label, #guide-menu:hover, #post-diary-window, .spoiler-button, .textarea-line, .dialog .window-body, .digest .post .icon-container, .icon-container .icon, #sidebar-profile-body .icon, .sidebar-container, .post .community-container, #global-menu #global-my-menu, .guest#post-permlink .guest-message p, form.search input[type="text"], form.search input[type="submit"], .warning-content, .community-card-list > li, .post-permalink-button, .repost:hover {
|
||||
background-color: var(--theme-dark, #006466) !important;
|
||||
}
|
||||
body, .community-title, #footer-selector li button, #footer-inner p a, .post-list.empty, .album-list.empty, .list > .no-content, .no-content, .community-game-title, .arrow-button, .post .hidden-content button, .post .deleted-message button, .post .hidden_as_violation button, .post .hidden-content a, .post .deleted-message a, .post .hidden_as_violation a, .post .community-container-heading a, .list-content-with-icon-and-text .text, .list-content-with-icon-and-text .id-name, #sidebar-profile-body .id-name, #post-diary-window p, .sidebar-container h4 a, .post-list .empathized-user-name, .post-list .acted-user-name, .post-list .recommend-user-title, .headline h2, #post-content .post-content-text, #global-menu li a, #global-menu li button, #sidebar-profile-status .number, .community-name, .community-name a, .community-list .title, .sidebar-setting .sidebar-menu-relation:before, #reply-content button.more-button, .sidebar-profile .profile-comment, .guest#post-permlink .guest-message p, form.search input[type="text"], .community-name .owner, .post-list-heading-button, .topic-title-input, .textarea, .yeah-button, .post-permalink-button, .hidden-content-button, .community-description, #admin-menu ul li:not(.selected) a {
|
||||
color: #ccc !important;
|
||||
}
|
||||
input[type=text], input[type=number], input[type=password], input[type=email], input:not([type]), select {
|
||||
background-color: var(--theme-darker, #180030);
|
||||
color: #fff;
|
||||
border: 1px solid #000;
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.guest .guest-message p {
|
||||
color: #006466;
|
||||
background: #006466;
|
||||
}
|
||||
.guest-message .arrow-button {
|
||||
border-top: 1px solid #000 !important;
|
||||
}
|
||||
.delete {
|
||||
color: #006466;
|
||||
}
|
||||
.delete:hover {
|
||||
color: var(--theme-dark, #200040) !important;
|
||||
}
|
||||
.spoiler-button, .textarea-line {
|
||||
border: 1px solid #000;
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.post-form-album-content {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
.post-form-album-content:before {
|
||||
border-bottom-color: #000;
|
||||
}
|
||||
#sidebar-cover {
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
.post-list-heading-button {
|
||||
border: 1px solid #000;
|
||||
border-bottom-color: #000;
|
||||
}
|
||||
.filtering-label .button {
|
||||
border-left: 1px solid #000;
|
||||
}
|
||||
.mask {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.post-list-heading-button:hover {
|
||||
background: rgb(35, 35, 35);
|
||||
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.topic-title-input, .textarea {
|
||||
border: 1px solid #000;
|
||||
box-shadow: inset 0 2px 6px rgba(0,0,0,0.12), 0 1px 0 #211e1e;
|
||||
}
|
||||
.hidden-content-button {
|
||||
border: 1px solid #000;
|
||||
border-bottom-color: #000;
|
||||
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.hidden-content-button:hover {
|
||||
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0), 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.yeah-button {
|
||||
border: 1px solid #000;
|
||||
border-bottom-color: #000;
|
||||
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.yeah-button:hover {
|
||||
background: rgb(35, 35, 35);
|
||||
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.yeah-button:disabled:hover {
|
||||
background: var(--theme-darker, #100020);
|
||||
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.setting-form li {
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
form.search input[type="submit"] {
|
||||
color: #646464;
|
||||
}
|
||||
.sidebar-setting .sidebar-menu-relation:before {
|
||||
color: #000;
|
||||
}
|
||||
.community-list-cover {
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
.simple-wrapper.simple-wrapper-content #wrapper {
|
||||
margin: 0px auto 0;
|
||||
}
|
||||
.simple-wrapper.simple-wrapper-content #main-body {
|
||||
border: 1px solid rgba(21, 20, 20, 0) !important;
|
||||
}
|
||||
.warning-content {
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
.community-eyecatch-balloon {
|
||||
color: #CCCCCC;
|
||||
background: rgb(32, 32, 32);
|
||||
}
|
||||
.community-eyecatch-balloon:after {
|
||||
border-color: transparent rgb(32, 32, 32) rgba(0, 0, 0, 0) transparent;
|
||||
}
|
||||
.post-body .multi-timeline-post-list .post-subtype-artwork .hidden-content {
|
||||
background-color: var(--theme-darker, #100020);
|
||||
border-top: 1px dashed #000;
|
||||
}
|
||||
.community-list .siblings {
|
||||
color: #969696;
|
||||
border-left: 1px solid #000;
|
||||
}
|
||||
.post-list > .post-list-outline {
|
||||
border-top: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
#main-body > .no-content {
|
||||
background: var(--theme-dark, #00fbff) none repeat scroll 0% 0%;
|
||||
border: 1px solid #000;
|
||||
box-shadow: 0px 1px 0px #000;
|
||||
}
|
||||
.notify {
|
||||
background: #424242 !important;
|
||||
}
|
||||
.tab2 a, .tab3 a {
|
||||
background-color: var(--theme-dark, #200040);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(var(--theme-dark, #200040)), to(var(--theme-dark, #200040)), color-stop(0.5, var(--theme-dark, #200040)));
|
||||
background: -webkit-linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
background: -moz-linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
background: -ms-linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
background: -o-linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
background: linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
color: #CCCCCC;
|
||||
border: 1px solid #000;
|
||||
border-bottom-color: #000;
|
||||
}
|
||||
.album-dialog .album-close-button {
|
||||
background: -webkit-linear-gradient(bottom, var(--theme-dark, #200040), var(--theme-dark, #200040));
|
||||
border: 1px solid #000000;
|
||||
}
|
||||
.tab2 a:hover, .tab3 a:hover {
|
||||
background: var(--theme-darker, #100020);
|
||||
-webkit-box-shadow: inset 0 1px 0 var(--theme-darker, #100020), 0 2px 4px rgba(22, 22, 22, 0);
|
||||
-moz-box-shadow: inset 0 1px 0 var(--theme-darker, #100020), 0 2px 4px rgba(22, 22, 22, 0);
|
||||
-ms-box-shadow: inset 0 1px 0 var(--theme-darker, #100020), 0 2px 4px rgba(22, 22, 22, 0);
|
||||
-o-box-shadow: inset 0 1px 0 var(--theme-darker, #100020), 0 2px 4px rgba(22, 22, 22, 0);
|
||||
box-shadow: inset 0 1px 0 var(--theme-darker, #100020), 0 2px 4px rgba(22, 22, 22, 0);
|
||||
}
|
||||
.tab2 > a:first-child, .tab3 > a:first-child {
|
||||
border-left: 1px solid #000;
|
||||
}
|
||||
.community-eyecatch-infoicon {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
.open-topic-post-existing-warning {
|
||||
background-color: rgb(32, 32, 32);
|
||||
}
|
||||
.open-topic-post-existing-warning .content {
|
||||
border: 2px solid #E8316E;
|
||||
}
|
||||
.list .toggle-button .button:before {
|
||||
color: #CCC;
|
||||
width: 30px;
|
||||
height: 34px;
|
||||
border: 1px solid #000;
|
||||
border-bottom-color: #000;
|
||||
box-shadow: inset 0 0.5px 0 rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
.topic-categories-container {
|
||||
background: var(--theme-dark, #200040);
|
||||
-webkit-box-shadow: 0 1px 0 var(--theme-dark, #200040);
|
||||
-moz-box-shadow: 0 1px 0 var(--theme-dark, #200040);
|
||||
-ms-box-shadow: 0 1px 0 var(--theme-dark, #200040);
|
||||
-o-box-shadow: 0 1px 0 var(--theme-dark, #200040);
|
||||
box-shadow: 0 1px 0 var(--theme-dark, #200040);
|
||||
}
|
||||
.button {
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
border-bottom-color: #000;
|
||||
background: var(--theme-dark, #200040);
|
||||
color: #ccc;
|
||||
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.button:hover {
|
||||
background: var(--theme-darker, #100020);
|
||||
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
.post-list a.another-posts {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
border-top: 1px solid #000;
|
||||
color: #ccc;
|
||||
}
|
||||
.sidebar-setting li {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
.community-list .icon-container .icon {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
#sidebar-profile-status li a > span {
|
||||
display: block;
|
||||
border-right: 1px solid var(--theme-dark, #200040);
|
||||
}
|
||||
#global-menu li#global-menu-mymenu .icon-container img:not(.official-tag) {
|
||||
border: 1px solid #000;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
#global-menu #global-my-menu {
|
||||
border: 2px solid #000;
|
||||
}
|
||||
#global-menu #global-my-menu:before {
|
||||
border-color: transparent;
|
||||
border-bottom-color: #000;
|
||||
}
|
||||
#global-menu #global-my-menu:after {
|
||||
border-color: transparent;
|
||||
border-bottom-color: var(--theme-dark, #200040);
|
||||
}
|
||||
.list-content-with-icon-and-text li {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
.filter-button {
|
||||
color: #ccc;
|
||||
background-color: rgb(32, 32, 32);
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
.filter-button:hover {
|
||||
background-color: rgba(22,22,22,1);
|
||||
color: #696666;
|
||||
}
|
||||
#community-eyecatch-main > div {
|
||||
background: var(--theme-dark, #200040);
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
#guide-menu {
|
||||
background: var(--theme-dark, #200040);
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
#guide-menu .arrow-button {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
#community-favorite {
|
||||
background: var(--theme-dark, #200040);
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
#identified-user-banner {
|
||||
background: var(--theme-dark, #200040);
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
.digest .post .body {
|
||||
background: var(--theme-dark, #200040);
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
.digest .post .body:after {
|
||||
border-color: transparent var(--theme-dark, #200040) transparent transparent;
|
||||
}
|
||||
.digest .post .body:before {
|
||||
border-color: transparent #000 transparent transparent;
|
||||
}
|
||||
.digest .post .community-container {
|
||||
border: 1px solid #000;
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
.accepting > span {
|
||||
background-color: rgba(255, 247, 179, 0);
|
||||
color: #e33ea2;
|
||||
}
|
||||
.accepting > span:before {
|
||||
color: #f90047;
|
||||
}
|
||||
.not-accepting > span {
|
||||
background-color: rgba(255, 247, 179, 0);
|
||||
color: #969696;
|
||||
}
|
||||
.not-accepting > span:before {
|
||||
color: #FF0000;
|
||||
}
|
||||
.guest#post-permlink .guest-message .arrow-button {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
.community-card-list > li {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-color: #000;
|
||||
}
|
||||
#sub-body {
|
||||
background: var(--theme-dark, #006466);
|
||||
border-bottom: 2px solid #000;
|
||||
box-shadow: 0 0 15px #000;
|
||||
}
|
||||
.content-loading-window.activity-feed img {
|
||||
visibility: hidden;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.post-form-album-content:after {
|
||||
border-bottom-color: #1a1a1a;
|
||||
}
|
||||
.pager-button {
|
||||
background: #1A1A1A;
|
||||
}
|
||||
.pager-button .back-button {
|
||||
border-right: 1px solid #000;
|
||||
}
|
||||
|
||||
.pager-button .next-button {
|
||||
border-left: 1px solid #000;
|
||||
}
|
||||
.pager-button .button {
|
||||
color: #969696;
|
||||
background-color: #313131;
|
||||
}
|
||||
.guest #about {
|
||||
background: var(--theme-dark, #006466);
|
||||
border-bottom: 2px solid #000;
|
||||
}
|
||||
.guest .guest-terms-link:hover {
|
||||
background-color: #006466;
|
||||
}
|
||||
.tutorial-window {
|
||||
background-color: rgb(32, 32, 32);
|
||||
}
|
||||
.post-list .recent-reply-content {
|
||||
border: 1px solid #000 !important;
|
||||
}
|
||||
.post-list .recent-reply-content .recent-reply-read-more-container {
|
||||
border-bottom: 1px solid #000 !important;
|
||||
}
|
||||
h2.label-diary_post, h2.label-diary {
|
||||
border-bottom: 3px solid #04c9db;
|
||||
color: #00b7d8;
|
||||
}
|
||||
h2.label-artwork_post, h2.label-artwork {
|
||||
border-bottom: 3px solid #fcc735;
|
||||
color: #ffae00;
|
||||
}
|
||||
h2.label-topic_post, h2.label-topic {
|
||||
border-bottom: 3px solid #e8316e;
|
||||
color: #e8316e;
|
||||
}
|
||||
.post-list-outline {
|
||||
background: var(--theme-dark, #200040);
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-ms-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-o-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.post .community-container {
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
.list > li, .list > div, .list > a, .list > span {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
/*.list > li span {
|
||||
padding: 5px!important;
|
||||
}*/
|
||||
#sidebar-community .sidebar-setting a {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
.sidebar-container {
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-ms-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-o-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.big-button {
|
||||
background: #fff;
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-ms-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
-o-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
background-color: var(--theme-dark, #200040);
|
||||
color: #ccc;
|
||||
}
|
||||
form.search {
|
||||
background: #fff;
|
||||
border: 2px solid #000;
|
||||
}
|
||||
.sidebar-setting a {
|
||||
color: #ccc;
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
.user-data .note {
|
||||
border-bottom: 2px dashed var(--theme-darker, #100020);
|
||||
color: #ccc;
|
||||
}
|
||||
#sidebar-profile-body .icon {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
.icon-container .icon {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
.digest .post .icon-container {
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
}
|
||||
#yeah-content .post-permalink-feeling-icon .user-icon {
|
||||
border: 1px solid #000;
|
||||
background: transparent;
|
||||
}
|
||||
#yeah-content.none + .buttons-content {
|
||||
border-top: 1px solid #222;
|
||||
}
|
||||
#yeah-content {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
#reply-content button.more-button {
|
||||
border: #000;
|
||||
}
|
||||
#reply-content .list .my.trigger:hover,
|
||||
#reply-content .list .my.trigger:hover .yeah-button,
|
||||
.list-content-with-icon-and-text .trigger.selected {
|
||||
background-color: #181818 !important;
|
||||
}
|
||||
#reply-content .info-reply-list + button.more-button, #reply-content .more-button + .more-button {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
.dialog .window-body {
|
||||
border: 1px solid #050207;
|
||||
}
|
||||
#post-form + #community-post-list .list > div:first-child {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
.user-community .icon-container .user-icon {
|
||||
background: rgba(255, 255, 255, 0);
|
||||
border: 1px solid rgba(221, 221, 221, 0);
|
||||
}
|
||||
#wrapper, #image-header-content {
|
||||
background: var(--theme, #00fbff) url('data:image/png;iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAEpElEQVR4nO2dW3LiQBAEE8kmOPae2w7j/ZjFa0CAHi1N16jqABMZlf2pKB1Op9MfgnI8Hun7Puq5q5zPZz4+Pvj+/g55z6yFs4t6zIWWqLBeOEMOwIWWqLD+5lx8AC60RIX1lnPRAbjQEhXWIc7ZB+BCS1RYH3HOOgAXWqLC+oxz8gG40BIV1leckw7AhZaosI7hHH0ALrREhXUs56gDcKElKqxTOF8egAstUWGdyvn0AFxoiQrrHM6HB+BCS1RY53IOHoALLVFhXcJ5dwAutESFdSnn1QG40BIV1gjOnwNwoSUqrFGcHbjQS1RYIzk7F1qiwhrN2e29UNBhXYPzLeSlm3x9ffH5+Zm+UNi3/L7v4z4KvcTydeTDwk/CbmP5WvIh8AAsX08+BB2A5WvKh4ADOJ/Plh+cLTkXHYBKoaDDujXn7ANQKRR0WGtwzjoAlUJBh7UW5+QDUCkUdFhrck46AJVCQYe1NufoA6gNOiUqrBk4Rx1ABtCxUWHNwvnyALKAjokKaybOpweQCfRVVFizcT48gGygz6LCmpFz8AAygj6KCmtWzrsDyAo6FBXWzJxXB5AZ9DYqrNk5fw4gO+jvqLAqcHagAXqJCqsKZ6cCCjqlqnACdCqgKqWqcEL5kqtTAFUpVYUTinxPxQZGhRP+y4eAbwItX4cTruWDp2IXR4UT7uWDp2IXRYUThuWDp2JnR4UTHssHT8XOigonPJcPnoqdHBVOeC0fPBU7KSqcME4+eCp2dFQ4Ybx88FTsqKhwwjT54KnYl1HhhOnywVOxT6PCCfPkg6diH0aFE+bLB0/FDkaFE5bJB0/F3kWFE5bLB0/FXkWFE2Lkg6dif6LCCXHywVOxgA4nxMo/HA6eilXhhHj5x+MxfikUdEpV4YSV5Hdd/AGolKrCCevJh+CpWJVSVThhXfkQeAAqpapwwvryIegAVEpV4YRt5EPQVKxCqSqcsJ182MlUrAonbCsfdjAVq8IJ28uHxqdiVTihjnxoeCpWhRPqyYdGp2JVOKGufGhwKlaFE+rLh8amYlU4IYd8aGgqVoUT8siHRqZiVTghl3xoYCpWhRPyyQfxqVgVTsgpH4SnYlU4Ia98EJ2KVeGE3PJBcCpWhRPyywexqVgVTtCQD/Cm8t9fy1/l+106y4+NknyJqVjLX09++qlYy19XPiSeirX89eVD0qlYy99GPiScirX87eRDsqlYy99WPiSairX87eVDkqlYy68jHxJMxVp+PflQeSrW8uvKh4pTsZZfXz5Umoq1/BzyocJUrOXnkQ8bT8Vafi75sOFUrOXnkw8bTcVafk75sMFUrOXnlQ8rT8Vafm75sOJUrOXnl7/aVKzla8h/f3/f71IoWH7f97yFvPgvlq8hf5WpWMvXkw87WwoFyw+firV8Xfmwk6VQsPzwqVjL15cPjS+FguWHT8VafjvyodGlULD88KlYy29PPjS2FAr1C52SDKzNLIVCjkLHJgtrE0uhkKfQMcnEKr8UCrkKfZVsrNJLoZCv0GfJyCq7FAo5C32UrKySS6GQt9ChZGaVWwqF3IXeJjur1FIo5C/0dxRYO8vfr3yAv3rECG2I4F0DAAAAAElFTkSuQmCC') !important;
|
||||
}
|
||||
#yeah-content:before {
|
||||
background: url(/assets/img/rks.png) no-repeat 0 0 !important;
|
||||
}
|
||||
.community-switcher .community-switcher-tab {
|
||||
background-color: var(--theme-dark, #200040);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(var(--theme-dark, #200040)), to(var(--theme-dark, #200040)), color-stop(0.5, var(--theme-dark, #200040)));
|
||||
background: -webkit-linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
background: -moz-linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
background: -ms-linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
background: -o-linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
background: linear-gradient(top, var(--theme-dark, #200040) 0%, var(--theme-dark, #200040) 50%, var(--theme-dark, #200040) 100%);
|
||||
border: 1px solid black;
|
||||
color: #fff;
|
||||
}
|
||||
.community-switcher .community-switcher-tab:first-of-type {
|
||||
border-left: 1px solid black;
|
||||
}
|
||||
code,
|
||||
pre {
|
||||
border: 1px solid #404050 !important;
|
||||
color: #CCC !important;
|
||||
background-color: var(--theme-darker, #100020) !important;
|
||||
}
|
||||
@media screen and (max-width: 980px) {
|
||||
#global-menu #global-menu-list > ul > li > a:hover {
|
||||
background-color: #333 !important;
|
||||
}
|
||||
#global-menu #global-menu-list > ul > li#global-menu-my-menu button:hover {
|
||||
background-color: #333 !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 640px) {
|
||||
.list .toggle-button .button:before {
|
||||
height: 28px !important;
|
||||
}
|
||||
}
|
||||
#my-menu-logout input {
|
||||
color: #ccc !important;
|
||||
}
|
||||
.warning-content {
|
||||
color: #ccc;
|
||||
}
|
||||
.post-migration-label {
|
||||
background-color: #333;
|
||||
}
|
||||
.character-count {
|
||||
color: #fff;
|
||||
}
|
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 3.5 MiB |
After Width: | Height: | Size: 919 B |
After Width: | Height: | Size: 130 B |
After Width: | Height: | Size: 144 B |
After Width: | Height: | Size: 413 B |
After Width: | Height: | Size: 76 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 14 MiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 362 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 410 B |
After Width: | Height: | Size: 581 B |
After Width: | Height: | Size: 198 B |
After Width: | Height: | Size: 196 B |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 100 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 862 B |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 164 B |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,187 @@
|
|||
function checkForm() {
|
||||
if($("textarea[name=body]").length && ($("textarea[name=body]").val().length > 0 || $("input[name=image]").val().length > 0)) {
|
||||
Olv.Form.toggleDisabled($("input.post-button"), false);
|
||||
Olv.Form.toggleDisabled($("input.reply-button"), false);
|
||||
} else {
|
||||
Olv.Form.toggleDisabled($("input.post-button"), true);
|
||||
Olv.Form.toggleDisabled($("input.reply-button"), true);
|
||||
}
|
||||
}
|
||||
function showError(error) {
|
||||
console.log(error);
|
||||
$("input[name=image]").val("");
|
||||
if(!$(".file-upload-button").hasClass("for-avatar")) {
|
||||
$(".preview-container").css("display", "none");
|
||||
checkForm();
|
||||
}
|
||||
$(".file-button").removeAttr("disabled");
|
||||
$(".file-button").val(null);
|
||||
$(".file-upload-button").text("Upload");
|
||||
Olv.showMessage("Attachment upload failed", "There was an error trying to upload your attachment.\nThe response received from the server was this:\n" + error.responseText);
|
||||
}
|
||||
function postFile(file, fileType, isDrawing, inputName) {
|
||||
if(!fileType.startsWith("image/") && !fileType.startsWith("audio/") && !fileType.startsWith("video/")) return $("input[name=image]").val(""), Olv.showMessage("Error", "Invalid file type."), $(".file-button").removeAttr("disabled"), $(".file-button").val(null), void $(".file-upload-button").text("Upload");
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append(inputName, file);
|
||||
var csrfTokenData = Olv.Form.csrftoken({});
|
||||
formData.append('csrfmiddlewaretoken', csrfTokenData.csrfmiddlewaretoken);
|
||||
|
||||
$.ajax({
|
||||
url: '/upload',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function(data) {
|
||||
if(isDrawing) {
|
||||
$("input[name=painting]").val(data);
|
||||
} else {
|
||||
$("input[name=" + inputName + "]").val(data);
|
||||
}
|
||||
if(fileType.startsWith("audio/")) {
|
||||
$(".preview-audio").attr("src", URL.createObjectURL(file));
|
||||
$(".preview-image").addClass("none");
|
||||
$(".preview-audio").removeClass("none");
|
||||
$(".preview-video").addClass("none");
|
||||
$("input[name=attachment_type]").val("1");
|
||||
} else if(fileType.startsWith("video/")) {
|
||||
$(".preview-video").attr("src", URL.createObjectURL(file));
|
||||
$(".preview-image").addClass("none");
|
||||
$(".preview-audio").addClass("none");
|
||||
$(".preview-video").removeClass("none");
|
||||
$("input[name=attachment_type]").val("2");
|
||||
} else if($(".file-upload-button").hasClass("for-avatar")) {
|
||||
$(".preview-image").attr("src", URL.createObjectURL(file));
|
||||
$(".preview-image").removeClass("none");
|
||||
} else {
|
||||
$("input[name=" + inputName + "]").siblings(".screenshot-container").children(".preview-image").attr("src", URL.createObjectURL(file));
|
||||
$("input[name=" + inputName + "]").siblings(".screenshot-container").children(".preview-image").removeClass("none");
|
||||
$(".preview-audio").addClass("none");
|
||||
$(".preview-video").addClass("none");
|
||||
$("input[name=attachment_type]").val("0");
|
||||
}
|
||||
if(!isDrawing && !$(".file-upload-button").hasClass("for-avatar")) {
|
||||
$(".preview-container").attr("style", "");
|
||||
checkForm();
|
||||
} else if(!isDrawing) {
|
||||
$("input[name=avatar][value=0]").prop("checked", true).change();
|
||||
} else {
|
||||
$("#drawing").remove();
|
||||
$(".textarea-memo").append("<img id=\"drawing\" src=\"" + URL.createObjectURL(file) + "\" style=\"background:white;\"></img>");
|
||||
Olv.Form.toggleDisabled($("input.post-button"), false);
|
||||
Olv.Form.toggleDisabled($(".memo-finish-btn"), false);
|
||||
}
|
||||
if(!isDrawing) {
|
||||
Olv.Form.toggleDisabled($(".file-button"), false);
|
||||
$(".file-button").val(null);
|
||||
$(".file-upload-button").text("Upload");
|
||||
}
|
||||
},
|
||||
error: function(error) {
|
||||
showError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function handleChange(event) {
|
||||
console.log(event);
|
||||
var inputName = "image";
|
||||
if($(this).attr("id") !== undefined) inputName = $(this).attr("id");
|
||||
if(this.files.length) {
|
||||
Olv.Form.toggleDisabled($("input.post-button"), true);
|
||||
$("input[name=" + inputName + "]").siblings(".file-button").attr("disabled", "disabled");
|
||||
$("input[name=" + inputName + "]").siblings(".file-upload-button").text("Uploading...");
|
||||
var fileType = this.files[0].type;
|
||||
var file = this.files[0];
|
||||
if(($(".file-upload-button").hasClass("for-avatar") || inputName === "icon") && fileType !== "image/gif") {
|
||||
var img = new Image();
|
||||
img.src = URL.createObjectURL(file);
|
||||
img.onload = function() {
|
||||
var canvas = document.createElement("canvas");
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.imageSmoothingQuality = "high";
|
||||
var size = 128, factor, startX, startY, resizeWidth, resizeHeight;
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
if(img.width > img.height) {
|
||||
factor = img.width / img.height;
|
||||
startX = (img.width - img.height) / 2;
|
||||
startY = 0;
|
||||
resizeWidth = size * factor;
|
||||
resizeHeight = size;
|
||||
} else if(img.height > img.width) {
|
||||
factor = img.height / img.width;
|
||||
startX = 0;
|
||||
startY = (img.height - img.width) / 2;
|
||||
resizeWidth = size;
|
||||
resizeHeight = size * factor;
|
||||
} else {
|
||||
factor = 1;
|
||||
startX = 0;
|
||||
startY = 0;
|
||||
resizeWidth = size;
|
||||
resizeHeight = size;
|
||||
}
|
||||
ctx.drawImage(img, startX, startY, img.width, img.height, 0, 0, resizeWidth, resizeHeight);
|
||||
canvas.toBlob(function(blob) {
|
||||
postFile(blob, fileType, false, inputName);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
postFile(file, fileType, false, inputName);
|
||||
}
|
||||
} else {
|
||||
$("input[name=image]").val("");
|
||||
if(!$(".file-upload-button").hasClass("for-avatar")) {
|
||||
$(".preview-container").css("display", "none");
|
||||
checkForm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleDropPaste(event) {
|
||||
if($(this).siblings(".file-button").attr("disabled")) {
|
||||
return;
|
||||
}
|
||||
var files;
|
||||
switch(event.type) {
|
||||
case "drop":
|
||||
files = event.originalEvent.dataTransfer.files;
|
||||
break;
|
||||
case "paste":
|
||||
if(event.originalEvent.clipboardData.files.length == 0) {
|
||||
return;
|
||||
}
|
||||
files = event.originalEvent.clipboardData.files;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if(files[0].type.startsWith("image/") || files[0].type.startsWith("audio/") || files[0].type.startsWith("video/")) {
|
||||
$(".file-button")[0].files = files;
|
||||
$(".file-button").trigger("change");
|
||||
} else {
|
||||
Olv.showMessage("Attachment upload failed", "You can only upload images, audio or videos.");
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrag(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
event.originalEvent.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
|
||||
function init() {
|
||||
$(".file-button").off().on("change", handleChange);
|
||||
$(document).off("dragover dragenter").on("dragover dragenter", handleDrag);
|
||||
$(document).off("drop paste").on("drop paste", handleDropPaste);
|
||||
}
|
||||
|
||||
$(document).off("ready", init).off("pjax:end", init).on("ready", init).on("pjax:end", init);
|
||||
init();
|
|
@ -0,0 +1,214 @@
|
|||
var waitingTime = 1000;
|
||||
var conn;
|
||||
|
||||
function initWebsockets() {
|
||||
conn = new WebSocket('ws' + (window.location.protocol === 'https' ? 's' : '') + '://' + window.location.host + '/ws');
|
||||
conn.onopen = function() {
|
||||
console.log("Connection established!");
|
||||
var message = '{"type":"onPage","content":"'+ window.location.pathname +'"}';
|
||||
conn.send(message);
|
||||
};
|
||||
|
||||
conn.onmessage = function(e) {
|
||||
var message = JSON.parse(e.data);
|
||||
switch(message.type) {
|
||||
case "comment":
|
||||
$('.reply-list').append(message.content);
|
||||
$('.post').last().hide().fadeIn(400);
|
||||
Olv.Entry.incrementReplyCount(1);
|
||||
break;
|
||||
case "commentPreview":
|
||||
$('#' + message.id).find('.recent-reply-content').remove();
|
||||
$('#' + message.id).find('.post-meta').after(message.content);
|
||||
var commentCount = parseInt($('#' + message.id).find('.reply-count').text());
|
||||
$('#' + message.id).find('.reply-count').text(commentCount + 1);
|
||||
if(commentCount > 1) {
|
||||
$('#' + message.id).find('.recent-reply-content').prepend('<div class="recent-reply-read-more-container" tabindex="0">View all comments (' + (commentCount + 1) + ')</div>');
|
||||
}
|
||||
break;
|
||||
case "notif":
|
||||
$('#global-menu-news').find('.badge').text(message.content);
|
||||
if(message.content > 0) {
|
||||
$('#global-menu-news .badge').show();
|
||||
} else {
|
||||
$('#global-menu-news .badge').hide();
|
||||
}
|
||||
getNewFaviconBadge();
|
||||
break;
|
||||
case "messageNotif":
|
||||
$('#global-menu-message').find('.badge').text(message.content);
|
||||
if(message.content > 0) {
|
||||
$('#global-menu-message .badge').show();
|
||||
} else {
|
||||
$('#global-menu-message .badge').hide();
|
||||
}
|
||||
getNewFaviconBadge();
|
||||
break;
|
||||
case "postYeah":
|
||||
if(window.location.pathname.substr(1, 5) == "posts") {
|
||||
var yeahCount = parseInt($('#the-post').find('.yeah-count').text());
|
||||
$('#the-post').find('.yeah-count').text(yeahCount + 1);
|
||||
$('#yeah-content').removeClass('none').prepend(message.content);
|
||||
} else {
|
||||
var yeahCount = parseInt($('#' + message.id).find('.yeah-count').text());
|
||||
$('#' + message.id).find('.yeah-count').text(yeahCount + 1);
|
||||
}
|
||||
break;
|
||||
case "postUnyeah":
|
||||
if(window.location.pathname.substr(1, 5) == "posts") {
|
||||
var yeahCount = parseInt($('#the-post').find('.yeah-count').text());
|
||||
$('#the-post').find('.yeah-count').text(yeahCount - 1);
|
||||
$('#yeah-content').find('#' + message.content).remove();
|
||||
if(yeahCount - 1 == 0) {
|
||||
$('#yeah-content').addClass('none');
|
||||
}
|
||||
} else {
|
||||
var yeahCount = parseInt($('#' + message.id).find('.yeah-count').text());
|
||||
$('#' + message.id).find('.yeah-count').text(yeahCount - 1);
|
||||
}
|
||||
break;
|
||||
case "postEdit":
|
||||
if($("#post-content").length) {
|
||||
$('#post-content').find('.post-content-text').html(message.content);
|
||||
} else {
|
||||
$('#' + message.id).find('.post-content-text').html(message.content);
|
||||
}
|
||||
break;
|
||||
case "pollVote":
|
||||
var pollOption = $(".poll-option[option-id=" + message.id + "]");
|
||||
pollOption.attr("votes", parseInt(pollOption.attr("votes")) + 1);
|
||||
recalculateVotes(pollOption.siblings(".poll-option").addBack());
|
||||
break;
|
||||
case "pollUnvote":
|
||||
var pollOption = $(".poll-option[option-id=" + message.id + "]");
|
||||
pollOption.attr("votes", pollOption.attr("votes") - 1);
|
||||
recalculateVotes(pollOption.siblings(".poll-option").addBack());
|
||||
break;
|
||||
case "pollChange":
|
||||
var pollOption = $(".poll-option[option-id=" + message.id + "]");
|
||||
pollOption.attr("votes", parseInt(pollOption.attr("votes")) + 1);
|
||||
var oldPollOption = $(".poll-option[option-id=" + message.content + "]");
|
||||
oldPollOption.attr("votes", oldPollOption.attr("votes") - 1);
|
||||
recalculateVotes(pollOption.siblings(".poll-option").addBack());
|
||||
break;
|
||||
case "commentYeah":
|
||||
if(window.location.pathname.substr(1, 8) == "comments") {
|
||||
var yeahCount = parseInt($('#the-post').find('.yeah-count').text());
|
||||
$('#the-post').find('.yeah-count').text(yeahCount + 1);
|
||||
$('#yeah-content').removeClass('none').prepend(message.content);
|
||||
} else {
|
||||
var yeahCount = parseInt($('#' + message.id).find('.yeah-count').text());
|
||||
$('#' + message.id).find('.yeah-count').text(yeahCount + 1);
|
||||
}
|
||||
break;
|
||||
case "commentUnyeah":
|
||||
if(window.location.pathname.substr(1, 8) == "comments") {
|
||||
var yeahCount = parseInt($('#the-post').find('.yeah-count').text());
|
||||
$('#the-post').find('.yeah-count').text(yeahCount - 1);
|
||||
$('#yeah-content').find('#' + message.content).remove();
|
||||
if(yeahCount - 1 == 0) {
|
||||
$('#yeah-content').addClass('none');
|
||||
}
|
||||
} else {
|
||||
var yeahCount = parseInt($('#' + message.id).find('.yeah-count').text());
|
||||
$('#' + message.id).find('.yeah-count').text(yeahCount - 1);
|
||||
}
|
||||
break;
|
||||
case "commentEdit":
|
||||
$('#' + message.id).find('.reply-content-text').html(message.content);
|
||||
break;
|
||||
case "follow":
|
||||
var followCount = parseInt($('.test-follower-count').text());
|
||||
$('.test-follower-count').text(followCount + 1);
|
||||
break;
|
||||
case "unfollow":
|
||||
var followCount = parseInt($('.test-follower-count').text());
|
||||
$('.test-follower-count').text(followCount - 1);
|
||||
break;
|
||||
case "online":
|
||||
$('.icon-container[username="' + message.content + '"].offline').removeClass('offline').addClass('online');
|
||||
break;
|
||||
case "offline":
|
||||
$('.icon-container[username="' + message.content + '"].online').removeClass('online').addClass('offline');
|
||||
break;
|
||||
case "block":
|
||||
if(window.location.pathname.startsWith("/users/" + message.content)) {
|
||||
Olv.Form.toggleDisabled($('.post:has(.icon-container[username="' + message.content + '"])').find(".yeah-button"), true);
|
||||
} else {
|
||||
$('.post:not(#post-content):has(.icon-container[username="' + message.content + '"])').remove();
|
||||
}
|
||||
Olv.Form.toggleDisabled($('#post-content:has(.icon-container[username="' + message.content + '"])').find(".yeah-button"), true);
|
||||
break;
|
||||
case "unblock":
|
||||
Olv.Form.toggleDisabled($('.post:has(.icon-container[username="' + message.content + '"])').find(".yeah-button"), false);
|
||||
break;
|
||||
case "delete":
|
||||
$('.post#' + message.id).remove();
|
||||
break;
|
||||
case "refresh":
|
||||
location.reload();
|
||||
break;
|
||||
case "post":
|
||||
$('.post-list').prepend(message.content);
|
||||
$('.post').first().hide().fadeIn(400);
|
||||
$(".no-post-content").addClass("none");
|
||||
if(window.location.pathname.startsWith("/communities/") && !$(message.content).hasClass("pinned")) {
|
||||
$(".pinned:not(.repost)").prependTo('.post-list');
|
||||
}
|
||||
break;
|
||||
case "message":
|
||||
$('.messages').prepend(message.content);
|
||||
$('.post').first().hide().fadeIn(400);
|
||||
$(".no-content").remove();
|
||||
break;
|
||||
case "messagePreview":
|
||||
message.content = JSON.parse(message.content);
|
||||
if(message.content.URLType == 1) {
|
||||
$('.list-content-with-icon-and-text li[data-href="/conversations/' + message.content.ByUsername + '"]').remove();
|
||||
} else {
|
||||
$('.list-content-with-icon-and-text li:has(.icon-container[username="' + message.content.ByUsername + '"])').remove();
|
||||
}
|
||||
$(".list-content-with-icon-and-text").prepend(`<li class="trigger notify" data-href="/${message.content.URLType ? "conversations" : "messages"}/${message.content.ByUsername}"><a href="/${message.content.URLType ? "conversations" : "users"}/${message.content.ByUsername}" username="${message.content.URLType ? "" : message.content.ByUsername}" class="icon-container ${!message.content.ByHideOnline ? message.content.ByOnline ? 'online' : 'offline' : ''} ${message.content.ByRoleImage ? 'official-user"><img src="' + message.content.ByRoleImage + '" class=official-tag>' : '">'}<img src="${message.content.ByAvatar}" class=icon></a><div class=body><p class=title><span class=nick-name><a href="/${message.content.URLType ? "conversations" : "users"}/${message.content.ByUsername}">${message.content.URL}</a></span> <span class=id-name>${message.content.URLType ? "" : message.content.ByUsername}</span></p><span class=timestamp>${message.content.Date}</span><p class="text other${message.content.BodyText ? '">' + message.content.BodyText : "text-memo\">(attachment)"}</p></div></li>`);
|
||||
$("#global-menu-message .badge").text(parseInt($("#global-menu-message .badge").text()) + 1).show();
|
||||
getNewFaviconBadge();
|
||||
break;
|
||||
default:
|
||||
console.log(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
conn.onclose = function() {
|
||||
if(websocketsEnabled) {
|
||||
$('.icon-container[username="' + $("body").attr("sess-usern") + '"].online').removeClass('online').addClass('offline');
|
||||
console.log("Disconnected, reconnecting in " + waitingTime / 1000 + " seconds");
|
||||
setTimeout(function() {
|
||||
waitingTime *= 2;
|
||||
console.log("Reconnecting...");
|
||||
initWebsockets();
|
||||
}, waitingTime);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).off("pjax:end", onPJAXEnd).on("pjax:end", onPJAXEnd);
|
||||
}
|
||||
|
||||
function onPJAXEnd() {
|
||||
var message = '{"type":"onPage","content":"'+ window.location.pathname +'"}';
|
||||
conn.send(message);
|
||||
}
|
||||
|
||||
function toggleWebsockets() {
|
||||
if(websocketsEnabled) {
|
||||
conn.close(1000);
|
||||
websocketsEnabled = false;
|
||||
$('.icon-container[username="' + $("body").attr("sess-usern") + '"].online').removeClass('online').addClass('offline');
|
||||
$(document).off("pjax:end", onPJAXEnd);
|
||||
} else {
|
||||
websocketsEnabled = true;
|
||||
initWebsockets();
|
||||
}
|
||||
}
|
||||
var websocketsEnabled = true;
|
||||
initWebsockets();
|
After Width: | Height: | Size: 751 B |
After Width: | Height: | Size: 337 B |
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
if (!empty($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'])) {
|
||||
$uri = 'https://';
|
||||
} else {
|
||||
$uri = 'http://';
|
||||
}
|
||||
$uri .= $_SERVER['HTTP_HOST'];
|
||||
header('Location: '.$uri.'/dashboard/');
|
||||
exit;
|
||||
?>
|
||||
Something is wrong with the XAMPP installation :-(
|
|
@ -0,0 +1,383 @@
|
|||
////////////////////////
|
||||
// //
|
||||
// Indigo //
|
||||
// The Miiverse clone //
|
||||
// that will end all //
|
||||
// other Miiverse //
|
||||
// clones, for real //
|
||||
// this time. //
|
||||
// //
|
||||
// Lead Devs: PF2M, //
|
||||
// Seth/EnergeticBark //
|
||||
// //
|
||||
// Developers: Ben, //
|
||||
// triangles.py, jod, //
|
||||
// & Chance/SRGNation //
|
||||
// //
|
||||
// Artwork: Spicy & //
|
||||
// Inverse & Gnarly //
|
||||
// //
|
||||
// Marketing: Pip //
|
||||
// //
|
||||
// Testing: Mippy ♥ //
|
||||
// //
|
||||
// https://github.com //
|
||||
// /PF2M/Indigo //
|
||||
// //
|
||||
////////////////////////
|
||||
|
||||
package main
|
||||
|
||||
// Import dependencies.
|
||||
import (
|
||||
// Internals
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
// "user" is already defined in types
|
||||
osUser "os/user"
|
||||
"strconv"
|
||||
|
||||
"regexp"
|
||||
|
||||
// Externals
|
||||
"github.com/NYTimes/gziphandler"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
// Initialize some variables.
|
||||
var db *sql.DB
|
||||
var err error
|
||||
var clients = make(map[*websocket.Conn]*wsSession)
|
||||
var settings config
|
||||
var admin adminConfig
|
||||
var youtube *regexp.Regexp
|
||||
var spotify *regexp.Regexp
|
||||
var soundcloud *regexp.Regexp
|
||||
var symbols *regexp.Regexp
|
||||
var emotes *regexp.Regexp
|
||||
var renderer *blackfriday.HTMLRenderer
|
||||
var geoip *geoip2.Reader
|
||||
var isGeoIPEnabled bool
|
||||
|
||||
// Configure the upgrader.
|
||||
var upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
// Todo: Add to this if necessary
|
||||
return true
|
||||
},
|
||||
EnableCompression: true,
|
||||
}
|
||||
|
||||
// Define the templates.
|
||||
var templates *template.Template
|
||||
|
||||
// Redirect HTTP requests to HTTPS if properly configured.
|
||||
func redirect(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "https://"+r.Host+r.URL.Path, http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
// Now let's start the main function!
|
||||
func main() {
|
||||
// Fetch the site's settings from JSON files.
|
||||
settings = getSettings()
|
||||
adminJSON, err := os.ReadFile("admin.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = json.Unmarshal(adminJSON, &admin)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Connect to the database.
|
||||
db, err = sql.Open("mysql", settings.DB.Username+":"+settings.DB.Password+"@tcp("+settings.DB.Host+")/"+settings.DB.Name+"?parseTime=true&loc=US%2FEastern&charset=utf8mb4,utf8")
|
||||
if err != nil {
|
||||
log.Printf("[err]: unable to connect to the database...\n")
|
||||
log.Printf(" %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Ping the database to make sure we connected properly.
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
log.Printf("[err]: unable to ping the database...\n")
|
||||
log.Printf(" %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = db.Exec("SET CHARACTER SET utf8mb4")
|
||||
if err != nil {
|
||||
log.Printf("[err]: unable to set the character set...\n")
|
||||
log.Printf(" %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = db.Exec("SET collation_connection = utf8mb4_bin")
|
||||
if err != nil {
|
||||
log.Printf("[err]: unable to set the connection collation...\n")
|
||||
log.Printf(" %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Initialize some regex.
|
||||
youtube, _ = regexp.Compile("(?:youtube\\.com/\\S*(?:(?:/e(?:mbed))?/|watch/?\\?(?:\\S*?&?v=))|youtu\\.be/)([a-zA-Z0-9_-]{6,11})")
|
||||
spotify, _ = regexp.Compile("(?:embed\\.|open\\.)(?:spotify\\.com/)(?:track/|\\?uri=spotify:track:)((\\w|-){22})")
|
||||
soundcloud, _ = regexp.Compile("(soundcloud\\.com|snd\\.sc)(.*)")
|
||||
symbols, _ = regexp.Compile("(\\|\\\\|`|\\*|{|}|\\[|\\](|)|\\+|-|!|_|>|\\n|&|:|<)")
|
||||
emotes, err = regexp.Compile(":([^ :]+):")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Initialize Markdown renderer.
|
||||
renderer = blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{
|
||||
Flags: 2 | 4 | 128,
|
||||
})
|
||||
|
||||
// Initialize GeoIP if a database is present.
|
||||
isGeoIPEnabled = false
|
||||
if _, err = os.Stat("geoip.mmdb"); err == nil {
|
||||
geoip, err = geoip2.Open("geoip.mmdb")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer geoip.Close()
|
||||
isGeoIPEnabled = true
|
||||
}
|
||||
|
||||
// Wipe the online statuses of all the users and delete all session keys (necessary after crashes, shutdowns, etc.)
|
||||
db.QueryRow("UPDATE users SET online = 0").Scan()
|
||||
db.QueryRow("TRUNCATE TABLE sessions").Scan()
|
||||
|
||||
// Close the database connection after this function exits.
|
||||
defer db.Close()
|
||||
|
||||
// initialize the templates by parsing everything from the views directory recursively
|
||||
var tmplFiles []string
|
||||
err = filepath.Walk("views", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// exclude non-html files
|
||||
if !info.IsDir() && filepath.Ext(path) == ".html" {
|
||||
// feel free to instead make this directly build the template
|
||||
tmplFiles = append(tmplFiles, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("could not add or find templates (they are stored in views, is this accessible?): ", err)
|
||||
}
|
||||
|
||||
templates = template.Must(template.ParseFiles(tmplFiles...))
|
||||
|
||||
// make the directory for the local image provider if it doesn't exist
|
||||
if settings.ImageHost.Provider == "local" {
|
||||
// check if the error is specifically os.IsNotExist
|
||||
if _, err := os.Stat(settings.ImageHost.ImageEndpoint); os.IsNotExist(err) {
|
||||
// should make it in this working directory
|
||||
err = os.MkdirAll(settings.ImageHost.ImageEndpoint, 0755)
|
||||
if err != nil {
|
||||
log.Println("could not make \""+settings.ImageHost.ImageEndpoint+"\" directory for local image host:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set up CSRF.
|
||||
CSRF := csrf.Protect([]byte(settings.CSRFSecret), csrf.FieldName("csrfmiddlewaretoken"), csrf.Path("/"), csrf.Secure(settings.SSL.Enabled))
|
||||
|
||||
// Initialize routes.
|
||||
r := mux.NewRouter()
|
||||
|
||||
// functions that don't useLogin or requireLogin,
|
||||
// they don't necessarily not access the user
|
||||
// but just do it independently, not utilizing the CurrentUser
|
||||
|
||||
// Index route.
|
||||
r.HandleFunc("/", useLogin(index)).Methods("GET")
|
||||
|
||||
// Auth routes.
|
||||
r.HandleFunc("/signup", signup).Methods("GET", "POST")
|
||||
r.HandleFunc("/login", login).Methods("GET", "POST")
|
||||
r.HandleFunc("/logout", logout).Methods("POST")
|
||||
r.HandleFunc("/reset", useLogin(resetPassword)).Methods("GET", "POST").Queries("token", "{token}")
|
||||
r.HandleFunc("/reset", useLogin(showResetPassword)).Methods("GET", "POST")
|
||||
|
||||
// User routes.
|
||||
r.HandleFunc("/users", requireLogin(showUserSearch)).Methods("GET").Queries("query", "{username}")
|
||||
r.HandleFunc("/users/{username}", useLogin(showUser)).Methods("GET")
|
||||
r.HandleFunc("/users/{username}/posts", useLogin(showUserPosts)).Methods("GET")
|
||||
r.HandleFunc("/users/{username}/comments", useLogin(showUserComments)).Methods("GET")
|
||||
r.HandleFunc("/users/{username}/yeahs", useLogin(showUserYeahs)).Methods("GET")
|
||||
r.HandleFunc("/users/{username}/friends", useLogin(showFriends)).Methods("GET")
|
||||
r.HandleFunc("/users/{username}/following", useLogin(showFollowing)).Methods("GET")
|
||||
r.HandleFunc("/users/{username}/followers", useLogin(showFollowers)).Methods("GET")
|
||||
r.HandleFunc("/users/{username}/favorites", useLogin(showFavorites)).Methods("GET")
|
||||
r.HandleFunc("/users/{username}/friend_new", requireLogin(newFriendRequest)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/friend_accept", requireLogin(acceptFriendRequest)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/friend_reject", requireLogin(rejectFriendRequest)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/friend_cancel", requireLogin(cancelFriendRequest)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/friend_delete", requireLogin(deleteFriend)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/follow", requireLogin(createFollow)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/unfollow", requireLogin(deleteFollow)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/violators", requireLogin(reportUser)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/block", requireLogin(blockUser)).Methods("POST")
|
||||
r.HandleFunc("/users/{username}/unblock", requireLogin(unblockUser)).Methods("POST")
|
||||
|
||||
// Post routes.
|
||||
r.HandleFunc("/posts/{id:[0-9]+}", useLogin(showPost)).Methods("GET")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/yeah", requireLogin(createPostYeah)).Methods("POST")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/yeahu", requireLogin(deletePostYeah)).Methods("POST")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/comments", useLogin(showAllComments)).Methods("GET")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/comments", requireLogin(createComment)).Methods("POST")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/favorite", requireLogin(favoritePost)).Methods("POST")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/unfavorite", requireLogin(unfavoritePost)).Methods("POST")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/violations", requireLogin(reportPost)).Methods("POST")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/vote", requireLogin(voteOnPoll)).Methods("POST")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/edit", requireLogin(editPost)).Methods("POST")
|
||||
r.HandleFunc("/posts/{id:[0-9]+}/delete", requireLogin(deletePost)).Methods("POST")
|
||||
|
||||
// Comment routes.
|
||||
r.HandleFunc("/comments/{id:[0-9]+}", useLogin(showComment)).Methods("GET")
|
||||
r.HandleFunc("/comments/{id:[0-9]+}/yeah", requireLogin(createCommentYeah)).Methods("POST")
|
||||
r.HandleFunc("/comments/{id:[0-9]+}/yeahu", requireLogin(deleteCommentYeah)).Methods("POST")
|
||||
r.HandleFunc("/comments/{id:[0-9]+}/violations", requireLogin(reportComment)).Methods("POST")
|
||||
r.HandleFunc("/comments/{id:[0-9]+}/edit", requireLogin(editComment)).Methods("POST")
|
||||
r.HandleFunc("/comments/{id:[0-9]+}/delete", requireLogin(deleteComment)).Methods("POST")
|
||||
|
||||
// Community routes.
|
||||
r.HandleFunc("/communities/all", useLogin(showAllCommunities)).Methods("GET")
|
||||
r.HandleFunc("/communities/recent", requireLogin(showRecentCommunities)).Methods("GET")
|
||||
r.HandleFunc("/communities/search", useLogin(showCommunitySearch)).Methods("GET").Queries("query", "{search}")
|
||||
r.HandleFunc("/communities/{id:[0-9]+}", useLogin(showCommunity)).Methods("GET")
|
||||
r.HandleFunc("/communities/{id:[0-9]+}/hot", useLogin(showPopularPosts)).Methods("GET")
|
||||
r.HandleFunc("/communities/{id:[0-9]+}/posts", requireLogin(createPost)).Methods("POST")
|
||||
r.HandleFunc("/communities/{id:[0-9]+}/favorite", requireLogin(addCommunityFavorite)).Methods("POST")
|
||||
r.HandleFunc("/communities/{id:[0-9]+}/unfavorite", requireLogin(deleteCommunityFavorite)).Methods("POST")
|
||||
|
||||
// Activiy Feed route.
|
||||
r.HandleFunc("/activity", requireLogin(showActivityFeed)).Methods("GET")
|
||||
|
||||
// Message routes.
|
||||
r.HandleFunc("/messages", requireLogin(showMessages)).Methods("GET")
|
||||
r.HandleFunc("/messages", requireLogin(sendMessage)).Methods("POST")
|
||||
r.HandleFunc("/messages/{id:[0-9]+}/delete", requireLogin(deleteMessage)).Methods("POST")
|
||||
r.HandleFunc("/messages/{username}", requireLogin(showConversation)).Methods("GET")
|
||||
r.HandleFunc("/conversations/{id:[0-9]+}", requireLogin(showGroupChat)).Methods("GET")
|
||||
r.HandleFunc("/conversations/create", requireLogin(showCreateGroupChat)).Methods("GET")
|
||||
r.HandleFunc("/conversations/create", requireLogin(createGroupChat)).Methods("POST")
|
||||
r.HandleFunc("/conversations/{id:[0-9]+}/edit", requireLogin(showEditGroupChat)).Methods("GET")
|
||||
r.HandleFunc("/conversations/{id:[0-9]+}/edit", requireLogin(editGroupChat)).Methods("POST")
|
||||
r.HandleFunc("/conversations/{id:[0-9]+}/leave", requireLogin(leaveGroupChat)).Methods("POST")
|
||||
r.HandleFunc("/conversations/{id:[0-9]+}/delete", requireLogin(deleteGroupChat)).Methods("POST")
|
||||
|
||||
// Notification routes.
|
||||
r.HandleFunc("/check_update.json", requireLogin(getNotificationCounts)).Methods("GET")
|
||||
r.HandleFunc("/notifications", requireLogin(showNotifications)).Methods("GET")
|
||||
r.HandleFunc("/notifications/friend_requests", requireLogin(showFriendRequests)).Methods("GET")
|
||||
|
||||
// Settings routes.
|
||||
r.HandleFunc("/settings/profile", requireLogin(showProfileSettings)).Methods("GET")
|
||||
r.HandleFunc("/settings/profile", requireLogin(editProfileSettings)).Methods("POST")
|
||||
r.HandleFunc("/region", requireLogin(getRegion)).Methods("POST")
|
||||
r.HandleFunc("/miis", getMii).Methods("POST")
|
||||
r.HandleFunc("/migrate/{id:[0-9]+}", requireLogin(migratePosts)).Methods("POST")
|
||||
r.HandleFunc("/rollback/{id:[0-9]+}", requireLogin(rollbackImport)).Methods("POST")
|
||||
r.HandleFunc("/settings/account", requireLogin(showAccountSettings)).Methods("GET")
|
||||
r.HandleFunc("/settings/account", requireLogin(editAccountSettings)).Methods("POST")
|
||||
r.HandleFunc("/blocked", requireLogin(showBlocked)).Methods("GET")
|
||||
|
||||
// Help page routes.
|
||||
r.HandleFunc("/help/rules", useLogin(showRulesPage)).Methods("GET")
|
||||
r.HandleFunc("/help/faq", useLogin(showFAQPage)).Methods("GET")
|
||||
r.HandleFunc("/help/legal", useLogin(showLegalPage)).Methods("GET")
|
||||
r.HandleFunc("/help/contact", useLogin(showContactPage)).Methods("GET")
|
||||
|
||||
// Image upload route.
|
||||
r.HandleFunc("/upload", uploadImage).Methods("POST")
|
||||
|
||||
// Admin routes.
|
||||
r.HandleFunc("/admin", requireLogin(showAdminDashboard)).Methods("GET")
|
||||
r.HandleFunc("/reports/{id:[0-9]+}/ignore", requireLogin(reportIgnore)).Methods("POST")
|
||||
r.HandleFunc("/admin/manage", requireLogin(showAdminManagerList)).Methods("GET")
|
||||
r.HandleFunc("/admin/manage/bantemp", requireLogin(adminBanUser)).Methods("POST")
|
||||
r.HandleFunc("/admin/manage/unbantemp", requireLogin(adminUnbanUser)).Methods("POST")
|
||||
//r.HandleFunc("/admin/manage/{table}", requireLogin(showAdminManager)).Methods("GET")
|
||||
//r.HandleFunc("/admin/manage/{table}/{id:[0-9]+}", requireLogin(showAdminEditor)).Methods("GET", "POST")
|
||||
r.HandleFunc("/admin/settings", requireLogin(showAdminSettings)).Methods("GET", "POST")
|
||||
r.HandleFunc("/admin/audit_log", requireLogin(showAdminAuditLog)).Methods("GET")
|
||||
|
||||
// Websocket route.
|
||||
r.HandleFunc("/ws", requireLogin(handleConnections)).Methods("GET")
|
||||
|
||||
// Add a 404 page.
|
||||
r.NotFoundHandler = useLogin(handle404)
|
||||
|
||||
// Serve static assets.
|
||||
r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("assets"))))
|
||||
// serve images as /images even though this can be changed
|
||||
r.PathPrefix("/images/").Handler(http.StripPrefix("/images/", http.FileServer(http.Dir("images"))))
|
||||
|
||||
if !settings.CSRFProtectDisable {
|
||||
r.Use(CSRF)
|
||||
}
|
||||
if settings.GzipEnabled {
|
||||
r.Use(gziphandler.GzipHandler)
|
||||
}
|
||||
|
||||
// Tell the http server to handle routing with the router we just made.
|
||||
http.Handle("/", r)
|
||||
// Tell the person who started this that we are starting the server.
|
||||
log.Printf("listening on " + settings.Port)
|
||||
|
||||
// Start the server.
|
||||
if settings.ListenSocket {
|
||||
// remove tha socket first or else
|
||||
os.Remove(settings.Port)
|
||||
|
||||
unixListener, err := net.Listen("unix", settings.Port)
|
||||
if err != nil {
|
||||
log.Fatal("cannot listen on unix socket: ", err)
|
||||
}
|
||||
|
||||
// set socket owner but only if the value is not blank
|
||||
if settings.SocketOwner != "" {
|
||||
socketUser, err := osUser.Lookup(settings.SocketOwner)
|
||||
if err != nil {
|
||||
log.Fatal("could not look up user so that we can change the owner of the unix socket so that we can listen on it:\n", err)
|
||||
}
|
||||
// should probably handle errors here
|
||||
uidInt, _ := strconv.Atoi(socketUser.Uid)
|
||||
gidInt, _ := strconv.Atoi(socketUser.Gid)
|
||||
err = os.Chown(settings.Port, uidInt, gidInt)
|
||||
if err != nil {
|
||||
log.Fatal("could not change socket owner", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = http.Serve(unixListener, nil) // Just serve HTTP requests.
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
if settings.SSL.Enabled && settings.Port != ":80" {
|
||||
go http.ListenAndServe(":80", http.HandlerFunc(redirect)) // Redirect HTTP requests to the HTTPS site.
|
||||
err = http.ListenAndServeTLS(settings.Port, settings.SSL.Certificate, settings.SSL.Key, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
log.Fatal(http.ListenAndServe(settings.Port, nil))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,655 @@
|
|||
-- MySQL dump 10.15 Distrib 10.0.38-MariaDB, for debian-linux-gnu (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: indigo
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 10.0.38-MariaDB-0ubuntu0.16.04.1
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `admin_notifications`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `admin_notifications`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `admin_notifications` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`reason` int(11) NOT NULL,
|
||||
`post` int(11) NOT NULL,
|
||||
`type` tinyint(1) NOT NULL,
|
||||
`user` int(11) NOT NULL,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`notif_read` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `audit_log_entries`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `audit_log_entries`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `audit_log_entries` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`type` tinyint(1) NOT NULL,
|
||||
`context` int(11) NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`created_by` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `created_by` (`created_by`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `bans`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `bans`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `bans` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user` int(11) NOT NULL,
|
||||
`ip` varchar(42) NOT NULL,
|
||||
`cidr` tinyint(1) NOT NULL,
|
||||
`until` datetime NOT NULL,
|
||||
`ban_by` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `bans_ibfk_1` (`user`),
|
||||
CONSTRAINT `bans_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `blocks`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `blocks`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `blocks` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`source` int(11) NOT NULL,
|
||||
`target` int(11) NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `source` (`source`,`target`),
|
||||
KEY `blocks_ibfk_2` (`target`),
|
||||
CONSTRAINT `blocks_ibfk_1` FOREIGN KEY (`source`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `blocks_ibfk_2` FOREIGN KEY (`target`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `comments`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `comments`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `comments` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`created_by` int(11) NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`edited_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`feeling` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`post` int(11) NOT NULL,
|
||||
`body` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`image` tinytext COLLATE utf8mb4_bin,
|
||||
`url` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
`is_spoiler` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`post_type` tinyint(1) DEFAULT '0',
|
||||
`is_rm` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_rm_by_admin` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`attachment_type` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`pinned` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`url_type` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `created_by` (`created_by`),
|
||||
KEY `post` (`post`),
|
||||
CONSTRAINT `comments_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `comments_ibfk_2` FOREIGN KEY (`post`) REFERENCES `posts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `communities`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `communities`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `communities` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`description` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`icon` tinytext COLLATE utf8mb4_bin NOT NULL,
|
||||
`banner` tinytext COLLATE utf8mb4_bin NOT NULL,
|
||||
`is_featured` tinyint(1) NOT NULL,
|
||||
`permissions` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`rm` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `community_favorites`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `community_favorites`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `community_favorites` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`community` int(11) NOT NULL,
|
||||
`favorite_by` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `community_favorites_ibfk_1` (`community`),
|
||||
KEY `community_favorites_ibfk_2` (`favorite_by`),
|
||||
CONSTRAINT `community_favorites_ibfk_1` FOREIGN KEY (`community`) REFERENCES `communities` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `community_favorites_ibfk_2` FOREIGN KEY (`favorite_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `conversations`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `conversations`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `conversations` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`source` int(11) NOT NULL,
|
||||
`target` int(11) NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`is_rm` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `conversations_ibfk_1` (`source`),
|
||||
CONSTRAINT `conversations_ibfk_1` FOREIGN KEY (`source`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `emotes`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `emotes`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `emotes` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` text COLLATE utf8mb4_bin NOT NULL,
|
||||
`image` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `follows`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `follows`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `follows` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`follow_to` int(11) NOT NULL,
|
||||
`follow_by` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `follow_to` (`follow_to`,`follow_by`),
|
||||
KEY `follow_by` (`follow_by`),
|
||||
CONSTRAINT `follows_ibfk_1` FOREIGN KEY (`follow_to`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `follows_ibfk_2` FOREIGN KEY (`follow_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `friend_requests`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `friend_requests`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `friend_requests` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`request_to` int(11) NOT NULL,
|
||||
`request_by` int(11) NOT NULL,
|
||||
`message` text NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`request_read` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `request_to` (`request_to`,`request_by`),
|
||||
KEY `request_by` (`request_by`),
|
||||
CONSTRAINT `friend_requests_ibfk_1` FOREIGN KEY (`request_to`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `friend_requests_ibfk_2` FOREIGN KEY (`request_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `friendships`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `friendships`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `friendships` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`source` int(11) NOT NULL,
|
||||
`target` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `source` (`source`,`target`),
|
||||
KEY `target` (`target`),
|
||||
CONSTRAINT `friendships_ibfk_1` FOREIGN KEY (`target`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `friendships_ibfk_2` FOREIGN KEY (`source`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `group_members`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `group_members`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `group_members` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user` int(11) NOT NULL,
|
||||
`conversation` int(11) unsigned NOT NULL,
|
||||
`unread_messages` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `group_members_ibfk_1` (`user`),
|
||||
KEY `group_members_ibfk_2` (`conversation`),
|
||||
CONSTRAINT `group_members_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `group_members_ibfk_2` FOREIGN KEY (`conversation`) REFERENCES `conversations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `images`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `images`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `images` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`value` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
`hash` char(32) COLLATE utf8mb4_bin NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `imports`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `imports`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `imports` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user` int(11) NOT NULL,
|
||||
`migration` int(11) NOT NULL,
|
||||
`username` varchar(64) COLLATE utf8mb4_bin NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `imports_ibfk_1` (`user`),
|
||||
KEY `imports_ibfk_2` (`migration`),
|
||||
CONSTRAINT `imports_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `imports_ibfk_2` FOREIGN KEY (`migration`) REFERENCES `migrations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `login_tokens`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `login_tokens`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `login_tokens` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`value` varchar(16) COLLATE utf8mb4_bin NOT NULL,
|
||||
`user` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `login_tokens_ibfk_1` (`user`),
|
||||
CONSTRAINT `login_tokens_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `messages`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `messages`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `messages` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`created_by` int(11) NOT NULL,
|
||||
`conversation_id` int(11) unsigned NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`feeling` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`body` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`image` tinytext NOT NULL,
|
||||
`attachment_type` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`url` varchar(1024) NOT NULL,
|
||||
`url_type` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`post_type` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_rm` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`msg_read` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `created_by` (`created_by`),
|
||||
KEY `messages_ibfk_2` (`conversation_id`),
|
||||
CONSTRAINT `messages_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `messages_ibfk_2` FOREIGN KEY (`conversation_id`) REFERENCES `conversations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `migrated_communities`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `migrated_communities`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `migrated_communities` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`migrated_id` varchar(64) COLLATE utf8mb4_bin NOT NULL,
|
||||
`icon` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
`title` text COLLATE utf8mb4_bin NOT NULL,
|
||||
`migration` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `migrations`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `migrations`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `migrations` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`image` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
`script` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
`url` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
`is_rm` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`password_required` tinyint(1) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `notifications`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `notifications`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `notifications` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`notif_type` tinyint(1) NOT NULL,
|
||||
`notif_by` int(11) DEFAULT NULL,
|
||||
`notif_to` int(11) NOT NULL,
|
||||
`notif_post` int(11) DEFAULT NULL,
|
||||
`merged` int(11) DEFAULT NULL,
|
||||
`notif_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`notif_read` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `notif_read` (`notif_read`),
|
||||
KEY `merged` (`merged`),
|
||||
KEY `notif_to` (`notif_to`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `options`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `options`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `options` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`post` int(11) NOT NULL,
|
||||
`name` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `options_ibfk_1` (`post`),
|
||||
CONSTRAINT `options_ibfk_1` FOREIGN KEY (`post`) REFERENCES `posts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `password_resets`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `password_resets`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `password_resets` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`token` varchar(16) COLLATE utf8mb4_bin NOT NULL,
|
||||
`user` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `password_resets_ibfk_1` (`user`),
|
||||
CONSTRAINT `password_resets_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `posts`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `posts`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `posts` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`created_by` int(11) NOT NULL,
|
||||
`community_id` int(11) NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`edited_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`feeling` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`body` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`image` tinytext COLLATE utf8mb4_bin,
|
||||
`attachment_type` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`url` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
`is_spoiler` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_rm` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_rm_by_admin` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`post_type` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`migration` int(11) NOT NULL DEFAULT '0',
|
||||
`import_id` int(11) NOT NULL DEFAULT '0',
|
||||
`migrated_id` varchar(64) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`migrated_community` varchar(32) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`pinned` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`privacy` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`url_type` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`repost` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `created_by` (`created_by`),
|
||||
KEY `community_id` (`community_id`),
|
||||
CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `posts_ibfk_2` FOREIGN KEY (`community_id`) REFERENCES `communities` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `profiles`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `profiles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `profiles` (
|
||||
`user` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`comment` text COLLATE utf8mb4_bin NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`nnid` varchar(16) COLLATE utf8mb4_bin NOT NULL,
|
||||
`mh` varchar(13) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`region` varchar(64) COLLATE utf8mb4_bin NOT NULL,
|
||||
`gender` int(1) NOT NULL,
|
||||
`nnid_visibility` tinyint(1) NOT NULL,
|
||||
`yeah_visibility` tinyint(1) NOT NULL,
|
||||
`reply_visibility` tinyint(1) NOT NULL,
|
||||
`discord` varchar(37) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`steam` varchar(64) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`psn` varchar(16) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`switch_code` varchar(17) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`twitter` varchar(15) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`youtube` varchar(1024) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`allow_friend` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`favorite` int(11) NOT NULL DEFAULT '0',
|
||||
`avatar_image` varchar(1024) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`avatar_id` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`user`),
|
||||
CONSTRAINT `profiles_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `reports`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `reports`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `reports` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`type` tinyint(1) NOT NULL,
|
||||
`pid` int(11) NOT NULL,
|
||||
`message` varchar(100) COLLATE utf8mb4_bin NOT NULL,
|
||||
`user` int(11) NOT NULL,
|
||||
`reason` int(11) NOT NULL,
|
||||
`is_rm` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `reports_ibfk_1` (`user`),
|
||||
CONSTRAINT `reports_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `roles`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `roles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `roles` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`image` varchar(1024) COLLATE utf8mb4_bin NOT NULL,
|
||||
`organization` varchar(32) COLLATE utf8mb4_bin NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `sessions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `sessions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `sessions` (
|
||||
`id` text COLLATE utf8mb4_bin NOT NULL,
|
||||
`user` int(11) NOT NULL,
|
||||
KEY `sessions_ibfk_1` (`user`),
|
||||
CONSTRAINT `sessions_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `users` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`nickname` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`avatar` tinytext COLLATE utf8mb4_bin NOT NULL,
|
||||
`email` tinytext COLLATE utf8mb4_bin,
|
||||
`password` varchar(75) COLLATE utf8mb4_bin NOT NULL,
|
||||
`ip` varchar(39) COLLATE utf8mb4_bin NOT NULL,
|
||||
`level` int(2) NOT NULL,
|
||||
`role` int(11) NOT NULL,
|
||||
`online` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`last_seen` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`hide_last_seen` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`color` varchar(7) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`theme` varchar(31) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`yeah_notifications` tinyint(1) NOT NULL,
|
||||
`has_mh` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`hide_online` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`group_permissions` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`websockets_enabled` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`forbidden_keywords` longtext COLLATE utf8mb4_bin NOT NULL,
|
||||
`default_privacy` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `votes`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `votes`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `votes` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`option_id` int(11) NOT NULL,
|
||||
`user` int(11) NOT NULL,
|
||||
`poll` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `votes_ibfk_1` (`poll`),
|
||||
KEY `votes_ibfk_2` (`user`),
|
||||
CONSTRAINT `votes_ibfk_2` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `yeahs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `yeahs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `yeahs` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`yeah_post` int(11) NOT NULL,
|
||||
`yeah_by` int(11) NOT NULL,
|
||||
`on_comment` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `yeah_post` (`yeah_post`,`yeah_by`,`on_comment`),
|
||||
KEY `yeah_by` (`yeah_by`),
|
||||
CONSTRAINT `yeahs_ibfk_1` FOREIGN KEY (`yeah_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2019-02-17 21:42:15
|
|
@ -0,0 +1,492 @@
|
|||
// All the types/structures defined in Indigo.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"html/template"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Variable declarations for admin settings.
|
||||
type adminConfig struct {
|
||||
Manage struct {
|
||||
MinimumLevel int
|
||||
}
|
||||
Settings struct {
|
||||
MinimumLevel int
|
||||
}
|
||||
}
|
||||
|
||||
// Variable declarations for comments.
|
||||
type comment struct {
|
||||
ID int
|
||||
CreatedBy int
|
||||
PostID int
|
||||
CreatedAt string
|
||||
CreatedAtUnix int64
|
||||
EditedAt string
|
||||
EditedAtUnix int64
|
||||
Feeling int
|
||||
BodyText string
|
||||
Body template.HTML
|
||||
Image string
|
||||
AttachmentType int
|
||||
URL string
|
||||
URLType int
|
||||
Pinned bool
|
||||
IsSpoiler bool
|
||||
IsRMByAdmin bool
|
||||
PostType int
|
||||
CommenterUsername string
|
||||
CommenterNickname string
|
||||
CommenterIcon string
|
||||
CommenterHasMii bool
|
||||
CommenterOnline bool
|
||||
CommenterHideOnline bool
|
||||
CommenterColor string
|
||||
CommenterRoleImage string
|
||||
CommenterRoleOrganization string
|
||||
Yeahed bool
|
||||
YeahCount int
|
||||
ByMe bool
|
||||
ByMii bool
|
||||
CanYeah bool
|
||||
}
|
||||
|
||||
// Variable declarations for communities.
|
||||
type community struct {
|
||||
ID int
|
||||
Title string
|
||||
DescriptionText string
|
||||
Description template.HTML
|
||||
Icon string
|
||||
Banner string
|
||||
IsFeatured bool
|
||||
Permissions int
|
||||
RM bool
|
||||
}
|
||||
|
||||
// Variable declarations for settings.
|
||||
type config struct {
|
||||
// if this is true, then it will listen on a unix socket instead of a tcp port
|
||||
ListenSocket bool
|
||||
// this can be left blank if you are listening and accessing as the same user
|
||||
SocketOwner string
|
||||
// if listensocket is true then port is the socket
|
||||
Port string
|
||||
// whether to enable gzip compression
|
||||
// unnecessary with a reverse proxy, absolutely necessary without one
|
||||
GzipEnabled bool
|
||||
// will disable csrf protection which you probably want to do because it's annoying
|
||||
CSRFProtectDisable bool
|
||||
SSL struct {
|
||||
Enabled bool
|
||||
Certificate string
|
||||
Key string
|
||||
}
|
||||
DB struct {
|
||||
Host string
|
||||
Username string
|
||||
Password string
|
||||
Name string
|
||||
}
|
||||
ImageHost struct {
|
||||
Provider string
|
||||
Username string
|
||||
APIEndpoint string
|
||||
APIPublic string
|
||||
APISecret string
|
||||
ImageEndpoint string
|
||||
UploadPreset string
|
||||
MaxUploadSize string
|
||||
}
|
||||
Webhooks struct {
|
||||
Enabled bool
|
||||
Reports string
|
||||
Signups string
|
||||
Logins string
|
||||
}
|
||||
ReCAPTCHA struct {
|
||||
Enabled bool
|
||||
SiteKey string
|
||||
SecretKey string
|
||||
}
|
||||
SMTP struct {
|
||||
Enabled bool
|
||||
Hostname string
|
||||
Port string
|
||||
Email string
|
||||
Password string
|
||||
}
|
||||
CSRFSecret string
|
||||
IPHubKey string
|
||||
MiiEndpointPrefix string
|
||||
Proxy bool
|
||||
ForceLogins bool
|
||||
AllowSignups bool
|
||||
DefaultTimezone string
|
||||
ReportReasons []reportReason
|
||||
TextToReplace []struct {
|
||||
Original string
|
||||
Replaced string
|
||||
}
|
||||
EmoteLimit int
|
||||
}
|
||||
|
||||
// Variable declarations for conversations.
|
||||
type conversation struct {
|
||||
ID int
|
||||
Target int
|
||||
Nickname string
|
||||
Username string
|
||||
Online bool
|
||||
HideOnline bool
|
||||
Color string
|
||||
Icon string
|
||||
HasMii bool
|
||||
RoleImage string
|
||||
CreatedBy int
|
||||
BodyText string
|
||||
Body template.HTML
|
||||
Image string
|
||||
PostType int
|
||||
Date string
|
||||
DateUnix int64
|
||||
Read bool
|
||||
}
|
||||
|
||||
// Variable declarations for friend requests.
|
||||
type friendRequest struct {
|
||||
ID int
|
||||
By int
|
||||
CreatedAt string
|
||||
CreatedAtUnix int64
|
||||
Date string
|
||||
Message string
|
||||
Read bool
|
||||
ByUsername string
|
||||
ByAvatar string
|
||||
ByHasMii bool
|
||||
ByNickname string
|
||||
ByOnline bool
|
||||
ByHideOnline bool
|
||||
ByColor string
|
||||
ByRoleImage string
|
||||
ByRoleOrganization string
|
||||
}
|
||||
|
||||
// Variable declarations for import log entries.
|
||||
type importLog struct {
|
||||
ID int
|
||||
Image string
|
||||
Username string
|
||||
}
|
||||
|
||||
// Variable declarations for messages.
|
||||
type message struct {
|
||||
ID int
|
||||
Date string
|
||||
DateUnix int64
|
||||
Feeling int
|
||||
BodyText string
|
||||
Body template.HTML
|
||||
Image string
|
||||
AttachmentType int
|
||||
URL string
|
||||
URLType int
|
||||
PostType int
|
||||
ByUsername string
|
||||
ByAvatar string
|
||||
ByHasMii bool
|
||||
ByOnline bool
|
||||
ByHideOnline bool
|
||||
ByColor string
|
||||
ByRoleImage string
|
||||
ByMe bool
|
||||
}
|
||||
|
||||
// Variable declarations for migrations.
|
||||
type migration struct {
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
Posts []struct {
|
||||
ID interface{} `json:"id"`
|
||||
CreatedBy interface{} `json:"created_by"`
|
||||
CommunityID interface{} `json:"community_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
EditedAt string `json:"edited_at"`
|
||||
Feeling int `json:"feeling"`
|
||||
Body string `json:"body"`
|
||||
Image string `json:"image"`
|
||||
AttachmentType int `json:"attachment_type"`
|
||||
URL string `json:"url"`
|
||||
IsSpoiler interface{} `json:"is_spoiler"`
|
||||
IsRM int `json:"is_rm"`
|
||||
IsRMByAdmin int `json:"is_rm_by_admin"`
|
||||
PostType int `json:"post_type"`
|
||||
} `json:"posts"`
|
||||
Communities []struct {
|
||||
ID interface{} `json:"id"`
|
||||
Title string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
} `json:"communities"`
|
||||
}
|
||||
|
||||
// Variable declarations for the migration options.
|
||||
type migrationOption struct {
|
||||
ID int
|
||||
Image string
|
||||
PasswordRequired bool
|
||||
}
|
||||
|
||||
// Variable declarations for notifications.
|
||||
type notification struct {
|
||||
ID int
|
||||
Type int
|
||||
By int
|
||||
Post sql.NullInt64
|
||||
Date string
|
||||
DateUnix int64
|
||||
Read bool
|
||||
MergedCount int
|
||||
MergedOthers int
|
||||
URL string
|
||||
ByUsername string
|
||||
ByAvatar string
|
||||
ByHasMii bool
|
||||
ByNickname string
|
||||
ByOnline bool
|
||||
ByHideOnline bool
|
||||
ByColor string
|
||||
ByRoleImage string
|
||||
PostText string
|
||||
PostType int
|
||||
PostIsRM bool
|
||||
MergedUsername [3]string
|
||||
MergedNickname [3]string
|
||||
MergedColor [3]string
|
||||
}
|
||||
|
||||
// Variable declarations for notification counts.
|
||||
type notificationCount struct {
|
||||
Messages int
|
||||
Notifications int
|
||||
}
|
||||
|
||||
// Variable declarations for poll options.
|
||||
type option struct {
|
||||
ID int
|
||||
Name string
|
||||
Votes float64
|
||||
Percentage float64
|
||||
Selected bool
|
||||
}
|
||||
|
||||
// Variable declarations for polls.
|
||||
type poll struct {
|
||||
ID int
|
||||
Votes float64
|
||||
Options []option
|
||||
Selected bool
|
||||
}
|
||||
|
||||
// Variable declarations for posts.
|
||||
type post struct {
|
||||
ID int
|
||||
Type int
|
||||
CreatedBy int
|
||||
CreatedAt string
|
||||
CreatedAtTime time.Time
|
||||
CreatedAtUnix int64
|
||||
EditedAt string
|
||||
EditedAtTime time.Time
|
||||
EditedAtUnix int64
|
||||
Feeling int
|
||||
Body template.HTML
|
||||
BodyText string
|
||||
Image string
|
||||
AttachmentType int
|
||||
URL string
|
||||
URLType int
|
||||
Pinned bool
|
||||
Privacy int
|
||||
IsSpoiler bool
|
||||
IsRM bool
|
||||
IsRMByAdmin bool
|
||||
ByMe bool
|
||||
Poll poll
|
||||
HasPoll bool
|
||||
PostType int
|
||||
Repost *post
|
||||
RepostID int
|
||||
MigratedID string
|
||||
MigratedCommunity string
|
||||
MigrationID int
|
||||
MigrationImage string
|
||||
MigrationURL string
|
||||
PosterUsername string
|
||||
PosterNickname string
|
||||
PosterIcon string
|
||||
PosterHasMii bool
|
||||
PosterOnline bool
|
||||
PosterHideOnline bool
|
||||
PosterColor string
|
||||
PosterRoleID int
|
||||
PosterRoleImage string
|
||||
PosterRoleOrganization string
|
||||
CommunityID int
|
||||
CommunityName string
|
||||
CommunityIcon string
|
||||
CommunityRM bool
|
||||
Yeahed bool
|
||||
CanYeah bool
|
||||
YeahCount int
|
||||
CommentCount int
|
||||
CommentPreview comment
|
||||
}
|
||||
|
||||
// Variable declarations for profiles.
|
||||
type profile struct {
|
||||
User int
|
||||
CreatedAt string
|
||||
CreatedAtUnix int64
|
||||
NNID string
|
||||
MiiHash string
|
||||
AvatarImage string
|
||||
AvatarID int
|
||||
Gender string
|
||||
Region string
|
||||
Discord string
|
||||
Twitter string
|
||||
SwitchCode string
|
||||
PSN string
|
||||
YouTube string
|
||||
Steam string
|
||||
AllowFriend int
|
||||
CommentText string
|
||||
Comment template.HTML
|
||||
NNIDVisibility int
|
||||
YeahVisibility int
|
||||
ReplyVisibility int
|
||||
FavoritePostID int
|
||||
FavoritePostImage string
|
||||
FriendCount int
|
||||
FollowingCount int
|
||||
FollowerCount int
|
||||
PostCount int
|
||||
CommentCount int
|
||||
YeahCount int
|
||||
}
|
||||
|
||||
// Variable declarations for profile sidebars.
|
||||
type profileSidebar struct {
|
||||
User user
|
||||
CurrentUser user
|
||||
Profile profile
|
||||
ProfileOnPage string
|
||||
IsFollowing bool
|
||||
IsFollowingMe bool
|
||||
Reasons []reportReason
|
||||
FavoriteCommunities []community
|
||||
FriendStatus int
|
||||
Request friendRequest
|
||||
RequestTime string
|
||||
}
|
||||
|
||||
// Variable declarations for reports.
|
||||
type report struct {
|
||||
ID int
|
||||
Type int
|
||||
Message string
|
||||
Reason int
|
||||
ByID int
|
||||
ByUsername string
|
||||
ByNickname string
|
||||
ByColor string
|
||||
Post *post
|
||||
User *user
|
||||
}
|
||||
|
||||
// Variable declarations for report reasons.
|
||||
type reportReason struct {
|
||||
Name string
|
||||
Message string
|
||||
Enabled bool
|
||||
BodyRequired bool
|
||||
}
|
||||
|
||||
// Variable declarations for repost previews.
|
||||
type repostPreview struct {
|
||||
ID int
|
||||
Nickname string
|
||||
Text string
|
||||
PostType int
|
||||
}
|
||||
|
||||
// Variable declarations for users.
|
||||
type user struct {
|
||||
ID int
|
||||
Username string
|
||||
Nickname string
|
||||
Avatar string
|
||||
HasMii bool
|
||||
Email string
|
||||
Password string
|
||||
IP string
|
||||
Level int
|
||||
Role struct {
|
||||
Image string
|
||||
Organization string
|
||||
}
|
||||
Online bool
|
||||
HideOnline bool
|
||||
Color string
|
||||
Theme string
|
||||
ThemeColors []string
|
||||
LastSeen string
|
||||
LastSeenUnix int64
|
||||
HideLastSeen bool
|
||||
Comment string
|
||||
YeahNotifications bool
|
||||
LightMode bool
|
||||
WebsocketsEnabled bool
|
||||
DefaultPrivacy int
|
||||
Blocked bool
|
||||
Timezone string
|
||||
Notifications notificationCount
|
||||
ForbiddenKeywords string
|
||||
CSRFToken string
|
||||
}
|
||||
|
||||
// Variable declarations for websocket messages.
|
||||
type wsMessage struct {
|
||||
Type string `json:"type"`
|
||||
ID string `json:"id"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// Variable declarations for websocket sessions.
|
||||
type wsSession struct {
|
||||
Mutex *sync.Mutex
|
||||
Connected bool
|
||||
UserID int
|
||||
Level int
|
||||
OnPage string
|
||||
Send chan wsMessage
|
||||
}
|
||||
|
||||
// Variable declarations for Yeahs.
|
||||
type yeah struct {
|
||||
ID int
|
||||
Username string
|
||||
Avatar string
|
||||
HasMii bool
|
||||
Role string
|
||||
}
|
||||
|
||||
type iphubBlockResponse struct {
|
||||
Block int8 `json:"block"`
|
||||
ASN uint16 `json:"asn""`
|
||||
}
|
|
@ -0,0 +1,906 @@
|
|||
// Various utility functions used in Indigo.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
// Internals
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
// Externals
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/gorilla/websocket"
|
||||
sessions "github.com/kataras/go-sessions/v3"
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
// Inititialize sessions and other variables. Used in almost every page that uses HTML, and even some that don't.
|
||||
func doSession(w http.ResponseWriter, r *http.Request) (user, bool) {
|
||||
session := sessions.Start(w, r)
|
||||
currentUser := user{}
|
||||
ip := getIP(r)
|
||||
|
||||
timezone, err := r.Cookie("timezone")
|
||||
if err != nil || len(timezone.Value) == 0 {
|
||||
timezone = &http.Cookie{Name: "timezone", Value: getTimezone(ip), Expires: time.Now().Add(365 * 24 * time.Hour)}
|
||||
http.SetCookie(w, timezone)
|
||||
}
|
||||
currentUser.Timezone = timezone.Value
|
||||
|
||||
host, _, _ := net.SplitHostPort(ip)
|
||||
cidr := getCIDR(host, "1")
|
||||
cidr2 := getCIDR(host, "2")
|
||||
var banLength time.Time
|
||||
db.QueryRow("SELECT until FROM bans WHERE (cidr = 0 AND ip = ?) OR (cidr = 1 AND ip = ?) OR (cidr = 2 AND ip = ?)", host, cidr, cidr2).Scan(&banLength)
|
||||
if int64(banLength.Unix()) != -62135596800 {
|
||||
success := showBan(w, currentUser, banLength)
|
||||
if success {
|
||||
return currentUser, false
|
||||
}
|
||||
}
|
||||
if len(session.GetString("username")) != 0 {
|
||||
currentUser = QueryUser(session.GetString("username"), currentUser.Timezone)
|
||||
if len(currentUser.Theme) > 0 {
|
||||
currentUser.ThemeColors = strings.Split(currentUser.Theme, ",")
|
||||
}
|
||||
currentUser.Avatar = getAvatar(currentUser.Avatar, currentUser.HasMii, 0)
|
||||
|
||||
db.QueryRow("SELECT until FROM bans WHERE user = ?", currentUser.ID).Scan(&banLength)
|
||||
if int64(banLength.Unix()) != -62135596800 {
|
||||
success := showBan(w, currentUser, banLength)
|
||||
if success {
|
||||
return currentUser, false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
indigoAuth, err := r.Cookie("indigo-auth")
|
||||
if err == nil && len(indigoAuth.Value) > 0 {
|
||||
var username string
|
||||
db.QueryRow("SELECT username FROM login_tokens LEFT JOIN users ON user = users.id WHERE value = ?", &indigoAuth.Value).Scan(&username)
|
||||
if len(username) > 0 {
|
||||
currentUser = QueryUser(username, currentUser.Timezone)
|
||||
if len(currentUser.Theme) > 0 {
|
||||
currentUser.ThemeColors = strings.Split(currentUser.Theme, ",")
|
||||
}
|
||||
currentUser.Avatar = getAvatar(currentUser.Avatar, currentUser.HasMii, 0)
|
||||
|
||||
session.Set("username", currentUser.Username)
|
||||
session.Set("user_id", currentUser.ID)
|
||||
stmt, err := db.Prepare("INSERT INTO sessions (id, user) VALUES (?, ?)")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return currentUser, false
|
||||
}
|
||||
stmt.Exec(session.ID(), currentUser.ID)
|
||||
stmt.Close()
|
||||
|
||||
db.QueryRow("SELECT until FROM bans WHERE user = ?", currentUser.ID).Scan(&banLength)
|
||||
if int64(banLength.Unix()) != -62135596800 {
|
||||
success := showBan(w, currentUser, banLength)
|
||||
if success {
|
||||
return currentUser, false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if settings.ForceLogins && r.URL.Path != "/reset" {
|
||||
http.Redirect(w, r, "/login", 301)
|
||||
return currentUser, false
|
||||
}
|
||||
return currentUser, true
|
||||
}
|
||||
} else {
|
||||
if settings.ForceLogins && r.URL.Path != "/reset" {
|
||||
http.Redirect(w, r, "/login", 301)
|
||||
return currentUser, false
|
||||
}
|
||||
return currentUser, true
|
||||
}
|
||||
}
|
||||
|
||||
currentUser.CSRFToken = csrf.Token(r)
|
||||
currentUser.LightMode = getLightMode(w, r)
|
||||
|
||||
if r.Header.Get("X-PJAX") == "" {
|
||||
var friendRequests int
|
||||
db.QueryRow("SELECT COUNT(*) FROM messages LEFT JOIN conversations ON conversation_id = conversations.id WHERE (source = ? OR target = ?) AND created_by <> ? AND msg_read = 0 AND messages.is_rm = 0 AND conversations.is_rm = 0", currentUser.ID, currentUser.ID, currentUser.ID).Scan(¤tUser.Notifications.Messages)
|
||||
var groupUnread int
|
||||
db.QueryRow("SELECT SUM(unread_messages) FROM group_members WHERE user = ?", currentUser.ID).Scan(&groupUnread)
|
||||
currentUser.Notifications.Messages += groupUnread
|
||||
db.QueryRow("SELECT COUNT(*) FROM notifications WHERE notif_to = ? AND merged IS NULL AND notif_read = 0", currentUser.ID).Scan(¤tUser.Notifications.Notifications)
|
||||
db.QueryRow("SELECT COUNT(*) FROM friend_requests WHERE request_to = ? AND request_read = 0", currentUser.ID).Scan(&friendRequests)
|
||||
currentUser.Notifications.Notifications += friendRequests
|
||||
}
|
||||
|
||||
return currentUser, true
|
||||
}
|
||||
|
||||
// Escape the "forbidden keywords" field for regexp.
|
||||
func escapeForbiddenKeywords(regex string) string {
|
||||
if len(regex) == 0 {
|
||||
return "b\bb" // This regex always returns "false", so no posts are ever filtered out if you don't have any reserved words.
|
||||
}
|
||||
regex = regexp.QuoteMeta(regex)
|
||||
split := strings.Split(regex, ",")
|
||||
fixed := []string{}
|
||||
for _, s := range split {
|
||||
if len(s) > 0 {
|
||||
fixed = append(fixed, s)
|
||||
}
|
||||
}
|
||||
regex = strings.Join(fixed, "|")
|
||||
regex = strings.Replace(regex, "\\|", ",", -1)
|
||||
return strings.Replace(regex, "\\\\", "\\", -1)
|
||||
}
|
||||
|
||||
// Escape Markdown.
|
||||
func escapeMarkdown(text string) string {
|
||||
text = string(symbols.ReplaceAll([]byte(text), []byte("\\$1")))
|
||||
return text
|
||||
}
|
||||
|
||||
// Get a CIDR-esque range from an IP.
|
||||
func getCIDR(ip string, cidr string) string {
|
||||
netmasks := strings.Split(ip, ".")
|
||||
netmasks[3] = "0"
|
||||
if cidr == "2" {
|
||||
netmasks[2] = "0"
|
||||
}
|
||||
return strings.Join(netmasks, ".")
|
||||
}
|
||||
|
||||
// Get the user's light mode status.
|
||||
func getLightMode(w http.ResponseWriter, r *http.Request) bool {
|
||||
lightMode, err := r.Cookie("light")
|
||||
if err != nil || len(lightMode.Value) == 0 {
|
||||
lightMode = &http.Cookie{Name: "light", Value: "false", Expires: time.Unix(253402300799, 0)}
|
||||
http.SetCookie(w, lightMode)
|
||||
}
|
||||
lightModeBool, err := strconv.ParseBool(lightMode.Value)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
return lightModeBool
|
||||
}
|
||||
|
||||
// Get the data of a post's migration.
|
||||
func getPostMigration(migration int, migratedCommunity string) (string, string, string, string) {
|
||||
var migrationImage string
|
||||
var migrationURL string
|
||||
err := db.QueryRow("SELECT image, url FROM migrations WHERE id = ?", migration).Scan(&migrationImage, &migrationURL)
|
||||
if err != nil {
|
||||
fmt.Println("no migrations")
|
||||
fmt.Println(err.Error())
|
||||
return "https://i.ytimg.com/vi/DkIVqD8pJt8/maxresdefault.jpg", "http://marios-princess-sex.ga/#", "This message should not appear. An error occurred while grabbing the migration data. Check the console.", "https://closed.pizza/s/img/title-icon-default.png"
|
||||
}
|
||||
|
||||
var communityTitle string
|
||||
var communityIcon string
|
||||
err = db.QueryRow("SELECT title, icon FROM migrated_communities WHERE migrated_id = ? AND migration = ?", migratedCommunity, migration).Scan(&communityTitle, &communityIcon)
|
||||
if err != nil {
|
||||
return migrationImage, migrationURL, "Unknown Community", "/assets/img/title-icon-default.png"
|
||||
}
|
||||
|
||||
return migrationImage, migrationURL, communityTitle, communityIcon
|
||||
}
|
||||
|
||||
// Format timestamps in a way that normal people who AREN'T robots can read.
|
||||
func humanTiming(timestamp time.Time, timezone string) string {
|
||||
location, err := time.LoadLocation(timezone)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return err.Error()
|
||||
}
|
||||
timestamp = timestamp.In(location)
|
||||
since := time.Now().In(location).Sub(timestamp).Seconds()
|
||||
if since <= 1 {
|
||||
return "Less than a second ago"
|
||||
} else if since < 2 {
|
||||
return "1 second ago"
|
||||
} else if since < 60 {
|
||||
return strconv.Itoa(int(math.Floor(since))) + " seconds ago"
|
||||
} else if since < 120 {
|
||||
return "1 minute ago"
|
||||
} else if since < 3600 {
|
||||
return strconv.Itoa(int(math.Floor(since/60))) + " minutes ago"
|
||||
} else if since < 7200 {
|
||||
return "1 hour ago"
|
||||
} else if since < 86400 {
|
||||
return strconv.Itoa(int(math.Floor(since/60/60))) + " hours ago"
|
||||
} else if since < 172800 {
|
||||
return "1 day ago"
|
||||
} else if since < 345600 {
|
||||
return strconv.Itoa(int(math.Floor(since/60/60/24))) + " days ago"
|
||||
} else {
|
||||
return timestamp.Format("01/02/2006 3:04 PM")
|
||||
}
|
||||
/* Discord styled timestamp code here.
|
||||
now := time.Now().In(location)
|
||||
if now.Day() == timestamp.Day() && now.Month() == timestamp.Month() && now.Year() == timestamp.Year() {
|
||||
return timestamp.Format("Today at 3:04 PM")
|
||||
} else if now.Day()-1 == timestamp.Day() && now.Month() == timestamp.Month() && now.Year() == timestamp.Year() {
|
||||
return timestamp.Format("Yesterday at 3:04 PM")
|
||||
} else if now.Day()-2 == timestamp.Day() && now.Month() == timestamp.Month() && now.Year() == timestamp.Year() {
|
||||
return timestamp.Format("Last Monday at 3:04 PM")
|
||||
} else {
|
||||
return timestamp.Format("01/02/2006 3:04 PM")
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Send a notification to a user.
|
||||
func createNotif(to int, notif_type int, post string, currentUser int) {
|
||||
notif_read := 0
|
||||
for client := range clients {
|
||||
if clients[client].UserID == to {
|
||||
if ((notif_type == 0 || notif_type == 2 || notif_type == 3) && clients[client].OnPage == "/posts/"+post) || (notif_type == 1 && clients[client].OnPage == "/comments/"+post) {
|
||||
notif_read = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if notif_type == 0 || notif_type == 1 {
|
||||
var hasYeahNotificationsEnabled bool
|
||||
db.QueryRow("SELECT yeah_notifications FROM users WHERE id = ?", to).Scan(&hasYeahNotificationsEnabled)
|
||||
if !hasYeahNotificationsEnabled {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 0 = post yeah, 1 = reply yeah, 2 = comment on your post, 3 = poster's comment, 4 = follow
|
||||
var check_mergedusernews int
|
||||
if notif_type != 4 {
|
||||
db.QueryRow("SELECT merged FROM notifications WHERE notif_by = ? AND notif_to = ? AND notif_type = ? AND notif_post = ? AND merged IS NOT NULL AND notif_date > NOW() - 28800 ORDER BY notif_date DESC", currentUser, to, notif_type, post).Scan(&check_mergedusernews)
|
||||
} else {
|
||||
db.QueryRow("SELECT merged FROM notifications WHERE notif_by = ? AND notif_to = ? AND notif_type = ? AND merged IS NOT NULL AND notif_date > NOW() - 28800 ORDER BY notif_date DESC", currentUser, to, notif_type).Scan(&check_mergedusernews)
|
||||
}
|
||||
if check_mergedusernews != 0 {
|
||||
stmt, _ := db.Prepare("UPDATE notifications SET notif_read = 0, notif_date = CURRENT_TIMESTAMP WHERE id = ?")
|
||||
stmt.Exec(&check_mergedusernews)
|
||||
stmt.Close()
|
||||
} else {
|
||||
var result_update_newsmergesearch int
|
||||
if notif_type != 4 {
|
||||
db.QueryRow("SELECT id FROM notifications WHERE notif_to = ? AND notif_post = ? AND notif_date > NOW() - 28800 AND notif_type = ? ORDER BY notif_date DESC", to, post, notif_type).Scan(&result_update_newsmergesearch)
|
||||
} else {
|
||||
db.QueryRow("SELECT id FROM notifications WHERE notif_to = ? AND notif_date > NOW() - 28800 AND notif_type = ? ORDER BY notif_date DESC", to, notif_type).Scan(&result_update_newsmergesearch)
|
||||
}
|
||||
if result_update_newsmergesearch != 0 {
|
||||
if notif_type != 4 {
|
||||
stmt, _ := db.Prepare("INSERT INTO notifications(notif_by, notif_to, notif_post, merged, notif_type, notif_read) VALUES (?, ?, ?, ?, ?, ?)")
|
||||
stmt.Exec(currentUser, to, post, result_update_newsmergesearch, notif_type, notif_read)
|
||||
stmt.Close()
|
||||
} else {
|
||||
stmt, _ := db.Prepare("INSERT INTO notifications(notif_by, notif_to, merged, notif_type, notif_read) VALUES (?, ?, ?, ?, ?)")
|
||||
stmt.Exec(currentUser, to, result_update_newsmergesearch, notif_type, notif_read)
|
||||
stmt.Close()
|
||||
}
|
||||
stmt, _ := db.Prepare("UPDATE notifications SET notif_read = ?, notif_date = NOW() WHERE id = ?")
|
||||
stmt.Exec(notif_read, result_update_newsmergesearch)
|
||||
stmt.Close()
|
||||
} else {
|
||||
if notif_type != 4 {
|
||||
stmt, _ := db.Prepare("INSERT INTO notifications(notif_by, notif_to, notif_post, notif_type, notif_read) VALUES (?, ?, ?, ?, ?)")
|
||||
stmt.Exec(currentUser, to, post, notif_type, notif_read)
|
||||
stmt.Close()
|
||||
} else {
|
||||
stmt, _ := db.Prepare("INSERT INTO notifications(notif_by, notif_to, notif_type, notif_read) VALUES (?, ?, ?, ?)")
|
||||
stmt.Exec(currentUser, to, notif_type, notif_read)
|
||||
stmt.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if notif_read == 0 {
|
||||
var msg wsMessage
|
||||
msg.Type = "notif"
|
||||
var notifCount int
|
||||
var friendRequests int
|
||||
db.QueryRow("SELECT COUNT(*) FROM notifications WHERE notif_to = ? AND merged IS NULL AND notif_read = 0", &to).Scan(¬ifCount)
|
||||
db.QueryRow("SELECT COUNT(*) FROM friend_requests WHERE request_to = ? AND request_read = 0", to).Scan(&friendRequests)
|
||||
msg.Content = strconv.Itoa(notifCount + friendRequests)
|
||||
for client := range clients {
|
||||
if clients[client].UserID == to {
|
||||
err := client.WriteJSON(msg)
|
||||
if err != nil {
|
||||
client.Close()
|
||||
delete(clients, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find a user with a username.
|
||||
func QueryUser(username string, timezone string) user {
|
||||
var users = user{}
|
||||
var role int
|
||||
var lastSeenTime time.Time
|
||||
db.QueryRow("SELECT id, username, nickname, avatar, has_mh, email, password, ip, level, role, online, hide_online, last_seen, hide_last_seen, color, theme, yeah_notifications, websockets_enabled, forbidden_keywords, default_privacy FROM users WHERE username=?", username).Scan(&users.ID, &users.Username, &users.Nickname, &users.Avatar, &users.HasMii, &users.Email, &users.Password, &users.IP, &users.Level, &role, &users.Online, &users.HideOnline, &lastSeenTime, &users.HideLastSeen, &users.Color, &users.Theme, &users.YeahNotifications, &users.WebsocketsEnabled, &users.ForbiddenKeywords, &users.DefaultPrivacy)
|
||||
|
||||
if role > 0 {
|
||||
db.QueryRow("SELECT image, organization FROM roles WHERE id = ?", role).Scan(&users.Role.Image, &users.Role.Organization)
|
||||
}
|
||||
users.Timezone = timezone
|
||||
users.LastSeen = humanTiming(lastSeenTime, timezone)
|
||||
users.LastSeenUnix = lastSeenTime.Unix()
|
||||
|
||||
return users
|
||||
}
|
||||
|
||||
// Send JSON to a websocket.
|
||||
//func sendJSON()
|
||||
|
||||
// Get an array of posts from an SQL query.
|
||||
func setupPost(row *post, currentUser user, postType int, repostLayer int) *post {
|
||||
row.PosterIcon = getAvatar(row.PosterIcon, row.PosterHasMii, row.Feeling)
|
||||
if row.PosterRoleID > 0 {
|
||||
row.PosterRoleImage = getRoleImage(row.PosterRoleID)
|
||||
}
|
||||
|
||||
row.CreatedAt = humanTiming(row.CreatedAtTime, currentUser.Timezone)
|
||||
row.CreatedAtUnix = row.CreatedAtTime.Unix()
|
||||
if row.EditedAtTime.Sub(row.CreatedAtTime).Minutes() > 5 {
|
||||
row.EditedAt = humanTiming(row.EditedAtTime, currentUser.Timezone)
|
||||
row.EditedAtUnix = row.EditedAtTime.Unix()
|
||||
}
|
||||
if len(row.MigratedID) == 0 || strings.Contains(row.BodyText, ":markdown:") {
|
||||
row.Body = parseBodyWithLineBreaks(row.BodyText, true, true)
|
||||
} else {
|
||||
row.Body = parseBodyWithLineBreaks(row.BodyText, true, false)
|
||||
}
|
||||
if row.CreatedBy == currentUser.ID {
|
||||
row.ByMe = true
|
||||
}
|
||||
if row.PostType == 2 {
|
||||
row.Poll = getPoll(row.ID, currentUser.ID)
|
||||
}
|
||||
row.Type = postType
|
||||
if row.RepostID > 0 {
|
||||
var repost post
|
||||
if repostLayer < 3 {
|
||||
db.QueryRow("SELECT posts.id, created_by, created_at, edited_at, feeling, body, image, attachment_type, is_spoiler, post_type, url, url_type, pinned, privacy, repost, migration, migrated_id, migrated_community, is_rm_by_admin, communities.id, title, icon, rm, username, nickname, avatar, has_mh, online, hide_online, color, role FROM posts LEFT JOIN communities ON communities.id = community_id LEFT JOIN users ON users.id = created_by WHERE posts.id = ? AND is_rm = 0 AND users.id NOT IN (SELECT if(source = ?, target, source) FROM blocks WHERE (source = ? AND target = users.id) OR (source = users.id AND target = ?)) AND IF(created_by = ?, true, LOWER(body) NOT REGEXP LOWER(?)) AND (privacy = 0 OR (privacy IN (1, 2, 3, 4) AND (SELECT COUNT(*) FROM friendships WHERE source = ? AND target = created_by OR source = created_by AND target = ? LIMIT 1) = 1) OR (privacy IN (1, 3, 5, 6) AND (SELECT COUNT(*) FROM follows WHERE follow_to = created_by AND follow_by = ? LIMIT 1) = 1) OR (privacy IN (1, 2, 5, 7) AND (SELECT COUNT(*) FROM follows WHERE follow_to = ? AND follow_by = created_by) = 1) OR (privacy = 8 AND ? > 0) OR created_by = ?) LIMIT 1", row.RepostID, currentUser.ID, currentUser.ID, currentUser.ID, currentUser.ID, escapeForbiddenKeywords(currentUser.ForbiddenKeywords), currentUser.ID, currentUser.ID, currentUser.ID, currentUser.ID, currentUser.Level, currentUser.ID).Scan(&repost.ID, &repost.CreatedBy, &repost.CreatedAtTime, &repost.EditedAtTime, &repost.Feeling, &repost.BodyText, &repost.Image, &repost.AttachmentType, &repost.IsSpoiler, &repost.PostType, &repost.URL, &repost.URLType, &repost.Pinned, &repost.Privacy, &repost.RepostID, &repost.MigrationID, &repost.MigratedID, &repost.MigratedCommunity, &repost.IsRMByAdmin, &repost.CommunityID, &repost.CommunityName, &repost.CommunityIcon, &repost.CommunityRM, &repost.PosterUsername, &repost.PosterNickname, &repost.PosterIcon, &repost.PosterHasMii, &repost.PosterOnline, &repost.PosterHideOnline, &repost.PosterColor, &repost.PosterRoleID)
|
||||
row.Repost = &repost
|
||||
row.Repost.Type = 3
|
||||
if len(row.Repost.CommunityName) > 0 {
|
||||
repostLayer = repostLayer + 1
|
||||
row.Repost = setupPost(row.Repost, currentUser, 3, repostLayer)
|
||||
}
|
||||
} else {
|
||||
repost.Type = 4
|
||||
repost.ID = row.ID
|
||||
row.Repost = &repost
|
||||
}
|
||||
}
|
||||
|
||||
if row.MigrationID > 0 {
|
||||
row.MigrationImage, row.MigrationURL, row.CommunityName, row.CommunityIcon = getPostMigration(row.MigrationID, row.MigratedCommunity)
|
||||
}
|
||||
|
||||
db.QueryRow("SELECT COUNT(*) FROM yeahs WHERE yeah_post = ? AND yeah_by = ? AND on_comment = 0 LIMIT 1", row.ID, currentUser.ID).Scan(&row.Yeahed)
|
||||
row.CanYeah = checkIfCanYeah(currentUser, row.CreatedBy)
|
||||
|
||||
if row.CommentCount != -1 {
|
||||
db.QueryRow("SELECT COUNT(*) FROM yeahs WHERE yeah_post = ? AND on_comment = 0", row.ID).Scan(&row.YeahCount)
|
||||
db.QueryRow("SELECT COUNT(*) FROM comments WHERE post = ? AND is_rm = 0", row.ID).Scan(&row.CommentCount)
|
||||
if row.CommentCount > 0 && postType != -1 && postType != 3 {
|
||||
row.CommentPreview = getCommentPreview(row.ID, currentUser)
|
||||
}
|
||||
} else {
|
||||
db.QueryRow("SELECT COUNT(*) FROM yeahs WHERE yeah_post = ? AND on_comment = 1", row.ID).Scan(&row.YeahCount)
|
||||
}
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
// Show a ban screen.
|
||||
func showBan(w http.ResponseWriter, currentUser user, banLength time.Time) bool {
|
||||
if time.Now().Sub(banLength).Seconds() > 1 {
|
||||
stmt, _ := db.Prepare("DELETE FROM bans WHERE user = ?")
|
||||
stmt.Exec(currentUser.ID)
|
||||
stmt.Close()
|
||||
return false
|
||||
} else {
|
||||
var data = map[string]interface{}{
|
||||
"CurrentUser": currentUser,
|
||||
"Length": banLength.Format("01/02/2006 3:04 PM"),
|
||||
"LengthForever": banLength.Year() > 2100,
|
||||
}
|
||||
err := templates.ExecuteTemplate(w, "ban.html", data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Find a profile by user ID.
|
||||
func QueryProfile(id int, timezone string) profile {
|
||||
var profiles = profile{}
|
||||
var createdAtTime time.Time
|
||||
var genderNumber int // Gender is just a number.
|
||||
db.QueryRow("SELECT created_at, nnid, mh, avatar_image, avatar_id, gender, region, comment, nnid_visibility, yeah_visibility, reply_visibility, discord, steam, psn, switch_code, twitter, youtube, allow_friend, favorite FROM profiles WHERE user = ?", id).Scan(&createdAtTime, &profiles.NNID, &profiles.MiiHash, &profiles.AvatarImage, &profiles.AvatarID, &genderNumber, &profiles.Region, &profiles.CommentText, &profiles.NNIDVisibility, &profiles.YeahVisibility, &profiles.ReplyVisibility, &profiles.Discord, &profiles.Steam, &profiles.PSN, &profiles.SwitchCode, &profiles.Twitter, &profiles.YouTube, &profiles.AllowFriend, &profiles.FavoritePostID)
|
||||
|
||||
profiles.Gender = [6]string{"", "He/him", "She/her", "He/she", "Nonbinary", "They/them"}[genderNumber]
|
||||
profiles.User = id
|
||||
profiles.Comment = parseBodyWithLineBreaks(profiles.CommentText, false, true)
|
||||
profiles.CreatedAt = humanTiming(createdAtTime, timezone)
|
||||
profiles.CreatedAtUnix = createdAtTime.Unix()
|
||||
return profiles
|
||||
}
|
||||
|
||||
// Find a community by ID.
|
||||
func QueryCommunity(id string, canBeRM bool) community {
|
||||
var communities = community{}
|
||||
if canBeRM {
|
||||
db.QueryRow("SELECT id, title, description, icon, banner, is_featured, permissions, rm FROM communities WHERE id = ?", id).Scan(&communities.ID, &communities.Title, &communities.DescriptionText, &communities.Icon, &communities.Banner, &communities.IsFeatured, &communities.Permissions, &communities.RM)
|
||||
} else {
|
||||
db.QueryRow("SELECT id, title, description, icon, banner, is_featured, permissions, rm FROM communities WHERE id = ? AND rm = 0", id).Scan(&communities.ID, &communities.Title, &communities.DescriptionText, &communities.Icon, &communities.Banner, &communities.IsFeatured, &communities.Permissions, &communities.RM)
|
||||
}
|
||||
|
||||
communities.Description = parseBodyWithLineBreaks(communities.DescriptionText, false, true)
|
||||
return communities
|
||||
}
|
||||
|
||||
// Set variables for profile sidebar.
|
||||
func setupProfileSidebar(user user, currentUser user, profileOnPage string) profileSidebar {
|
||||
var sidebar profileSidebar
|
||||
sidebar.Profile = QueryProfile(user.ID, currentUser.Timezone)
|
||||
sidebar.User = user
|
||||
sidebar.CurrentUser = currentUser
|
||||
sidebar.ProfileOnPage = profileOnPage
|
||||
sidebar.Reasons = settings.ReportReasons
|
||||
|
||||
if len(sidebar.User.Theme) > 0 {
|
||||
sidebar.User.ThemeColors = strings.Split(sidebar.User.Theme, ",")
|
||||
}
|
||||
|
||||
db.QueryRow("SELECT COUNT(*) FROM follows WHERE follow_to = ? AND follow_by = ? LIMIT 1", user.ID, currentUser.ID).Scan(&sidebar.IsFollowing)
|
||||
var requestTimestamp time.Time
|
||||
_ = db.QueryRow("SELECT COUNT(*), created_at FROM friend_requests WHERE request_to = ? AND request_by = ? GROUP BY created_at", user.ID, currentUser.ID).Scan(&sidebar.FriendStatus, &requestTimestamp)
|
||||
if sidebar.FriendStatus > 0 {
|
||||
sidebar.FriendStatus = 2
|
||||
sidebar.RequestTime = requestTimestamp.Format("01/02/2006 3:04 PM")
|
||||
} else {
|
||||
db.QueryRow("SELECT COUNT(*) FROM friend_requests WHERE request_to = ? AND request_by = ?", currentUser.ID, user.ID).Scan(&sidebar.FriendStatus)
|
||||
if sidebar.FriendStatus > 0 {
|
||||
sidebar.FriendStatus = 1
|
||||
var createdAt time.Time
|
||||
db.QueryRow("SELECT id, message, created_at FROM friend_requests WHERE request_to = ? AND request_by = ? ORDER BY friend_requests.id DESC", currentUser.ID, user.ID).Scan(&sidebar.Request.ID, &sidebar.Request.Message, &createdAt)
|
||||
sidebar.Request.CreatedAt = createdAt.Format("01/02/2006 3:04 PM")
|
||||
} else {
|
||||
db.QueryRow("SELECT COUNT(*) FROM friendships WHERE (source = ? AND target = ?) OR (source = ? AND target = ?)", user.ID, currentUser.ID, currentUser.ID, user.ID).Scan(&sidebar.FriendStatus)
|
||||
if sidebar.FriendStatus > 0 {
|
||||
sidebar.FriendStatus = 3
|
||||
} else {
|
||||
sidebar.FriendStatus = 0
|
||||
if sidebar.Profile.AllowFriend == 1 {
|
||||
db.QueryRow("SELECT COUNT(*) FROM follows WHERE follow_to = ? AND follow_by = ? LIMIT 1", currentUser.ID, user.ID).Scan(&sidebar.IsFollowingMe)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sidebar.User.Blocked = checkIfBlocked(currentUser.ID, user.ID)
|
||||
sidebar.Profile.FriendCount, sidebar.Profile.FollowingCount, sidebar.Profile.FollowerCount = setupSidebarStatus(user.ID)
|
||||
var banCount int
|
||||
db.QueryRow("SELECT COUNT(*) FROM bans WHERE user = ?", user.ID).Scan(&banCount)
|
||||
if banCount > 0 {
|
||||
if len(sidebar.User.Role.Organization) > 0 {
|
||||
sidebar.User.Role.Organization = "Banned<br>" + sidebar.User.Role.Organization
|
||||
} else {
|
||||
sidebar.User.Role.Organization = "Banned"
|
||||
}
|
||||
}
|
||||
|
||||
db.QueryRow("SELECT COUNT(*) FROM posts WHERE created_by = ? AND is_rm = 0", user.ID).Scan(&sidebar.Profile.PostCount)
|
||||
db.QueryRow("SELECT COUNT(*) FROM comments WHERE created_by = ? AND is_rm = 0", user.ID).Scan(&sidebar.Profile.CommentCount)
|
||||
db.QueryRow("SELECT COUNT(*) FROM yeahs WHERE yeah_by = ?", user.ID).Scan(&sidebar.Profile.YeahCount)
|
||||
|
||||
db.QueryRow("SELECT image FROM posts WHERE id = ?", sidebar.Profile.FavoritePostID).Scan(&sidebar.Profile.FavoritePostImage)
|
||||
|
||||
favorite_rows, err := db.Query("SELECT communities.id, title, icon FROM communities LEFT JOIN community_favorites ON communities.id = community WHERE favorite_by = ? AND rm = 0 ORDER BY community_favorites.id DESC LIMIT 10", user.ID)
|
||||
if err != nil {
|
||||
fmt.Println("error while getting favorite communities")
|
||||
fmt.Println(err.Error())
|
||||
return sidebar
|
||||
}
|
||||
|
||||
var favorites []community
|
||||
for favorite_rows.Next() {
|
||||
var row = community{}
|
||||
|
||||
err := favorite_rows.Scan(&row.ID, &row.Title, &row.Icon)
|
||||
if err != nil {
|
||||
fmt.Println("error while scanning favorite communities")
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
favorites = append(favorites, row)
|
||||
}
|
||||
favorite_rows.Close()
|
||||
sidebar.FavoriteCommunities = favorites
|
||||
return sidebar
|
||||
}
|
||||
|
||||
// Set friend/following/follower counts for sidebars.
|
||||
func setupSidebarStatus(userID int) (int, int, int) {
|
||||
friendCount, followingCount, followerCount := 0, 0, 0
|
||||
if userID != 0 {
|
||||
db.QueryRow("SELECT COUNT(*) FROM friendships WHERE source = ? OR target = ?", userID, userID).Scan(&friendCount)
|
||||
db.QueryRow("SELECT COUNT(*) FROM follows WHERE follow_by = ?", userID).Scan(&followingCount)
|
||||
db.QueryRow("SELECT COUNT(*) FROM follows WHERE follow_to = ?", userID).Scan(&followerCount)
|
||||
}
|
||||
return friendCount, followingCount, followerCount
|
||||
}
|
||||
|
||||
// Cut a string off at 200 characters if needed, and parse Markdown and later emotes.
|
||||
func parseBody(body string, cutoff bool, parseMarkdown bool) template.HTML {
|
||||
// Cut off at 200 characters if cutoff is set.
|
||||
if cutoff && utf8.RuneCountInString(body) > 203 {
|
||||
runes := []rune(body) // What is this, fucking RuneScape!?
|
||||
body = string(runes[0:200]) + "..."
|
||||
}
|
||||
|
||||
// Parse markdown and sanitize HTML.
|
||||
if parseMarkdown {
|
||||
body = strings.Replace(body, "<3", "\\<3", -1)
|
||||
bodyTemp := blackfriday.Run([]byte(body), blackfriday.WithRenderer(renderer))
|
||||
if len(bodyTemp) >= 7 {
|
||||
rune2 := []rune(string(bodyTemp))
|
||||
body = string(rune2[:len(rune2)-1])
|
||||
} else {
|
||||
body = string(bodyTemp)
|
||||
}
|
||||
}
|
||||
body = bluemonday.UGCPolicy().Sanitize(body)
|
||||
|
||||
// Parse emotes.
|
||||
matches := emotes.FindAllStringSubmatch(body, settings.EmoteLimit)
|
||||
for _, match := range matches {
|
||||
var image sql.NullString
|
||||
db.QueryRow("SELECT image FROM emotes WHERE name = ?", match[1]).Scan(&image)
|
||||
if image.Valid {
|
||||
if len(image.String) > 0 {
|
||||
body = strings.Replace(body, match[0], "<img title=\"꞉"+match[1]+"꞉\" src=\""+image.String+"\">", 1)
|
||||
} else {
|
||||
body = strings.Replace(body, match[0], "", 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the output.
|
||||
return template.HTML(body)
|
||||
}
|
||||
|
||||
// Parse a body with parseBody(), and then replace line breaks with <br> elements.
|
||||
func parseBodyWithLineBreaks(body string, cutoff bool, parseMarkdown bool) template.HTML {
|
||||
bodyHTML := parseBody(body, cutoff, parseMarkdown)
|
||||
body = strings.Replace(string(bodyHTML), "\n", "<br>", -1)
|
||||
return template.HTML(body)
|
||||
}
|
||||
|
||||
// Cut a string off at 200 characters.
|
||||
func parseCutoff(body template.HTML) template.HTML {
|
||||
return body
|
||||
}
|
||||
|
||||
// Cut a string off at 15 characters. Used for notifications and the "View _____'s post for this comment" bar thingy at the top of the comments page.
|
||||
func parsePreview(body string, postType int, isRM bool) string {
|
||||
if isRM {
|
||||
body = "deleted"
|
||||
} else if len(body) == 0 {
|
||||
body = "empty"
|
||||
} else if postType == 1 {
|
||||
body = "handwritten"
|
||||
} else if utf8.RuneCountInString(body) > 18 {
|
||||
runes := []rune(body)
|
||||
body = string(runes[0:15]) + "..."
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
// Really minimal function to check if the user viewing the page can give a Yeah to a certain post.
|
||||
func checkIfCanYeah(currentUser user, createdBy int) bool {
|
||||
if len(currentUser.Username) == 0 {
|
||||
return false
|
||||
}
|
||||
if createdBy == currentUser.ID {
|
||||
return false
|
||||
}
|
||||
if checkIfEitherBlocked(currentUser.ID, createdBy) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Generate the name of a group chat from an array of user nicknames.
|
||||
func getGroupName(users []string) string {
|
||||
groupName := "Group chat with "
|
||||
if len(users) < 1 {
|
||||
groupName += "yourself"
|
||||
} else if len(users) == 1 {
|
||||
groupName += users[0]
|
||||
} else if len(users) == 2 {
|
||||
groupName += users[0] + " and " + users[1]
|
||||
} else {
|
||||
for i := 0; i < len(users)-2; i++ {
|
||||
groupName += users[i] + ", "
|
||||
}
|
||||
groupName += users[len(users)-2] + " and " + users[len(users)-1]
|
||||
}
|
||||
return groupName
|
||||
}
|
||||
|
||||
// Fetch the site's settings from a config.json file.
|
||||
func getSettings() config {
|
||||
var settings config
|
||||
settingsJSON, err := ioutil.ReadFile("config.json")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return settings
|
||||
}
|
||||
err = json.Unmarshal(settingsJSON, &settings)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
settings.ReportReasons = append([]reportReason{{
|
||||
Name: "Spoiler",
|
||||
Message: "Your post contained spoilers, so it was removed.",
|
||||
Enabled: false,
|
||||
BodyRequired: true,
|
||||
}}, settings.ReportReasons...)
|
||||
return settings
|
||||
}
|
||||
|
||||
// Generate a login token for autoauth.
|
||||
func generateLoginToken() string {
|
||||
const letterBytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
|
||||
const (
|
||||
letterIdxBits = 6
|
||||
letterIdxMask = 1<<letterIdxBits - 1
|
||||
letterIdxMax = 63 / letterIdxBits
|
||||
)
|
||||
src := rand.NewSource(time.Now().UnixNano())
|
||||
b := make([]byte, 16)
|
||||
|
||||
for i, cache, remain := 15, src.Int63(), letterIdxMax; i >= 0; {
|
||||
if remain == 0 {
|
||||
cache, remain = src.Int63(), letterIdxMax
|
||||
}
|
||||
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
||||
b[i] = letterBytes[idx]
|
||||
i--
|
||||
}
|
||||
cache >>= letterIdxBits
|
||||
remain--
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Check if a user is blocking another user.
|
||||
func checkIfBlocked(source int, target int) bool {
|
||||
var isBlocked bool
|
||||
err := db.QueryRow("SELECT COUNT(*) FROM blocks WHERE source = ? AND target = ?", source, target).Scan(&isBlocked)
|
||||
if err != nil {
|
||||
fmt.Println("error while getting blocks")
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return isBlocked
|
||||
}
|
||||
|
||||
// Check if a user is blocking another user, or vice versa.
|
||||
func checkIfEitherBlocked(source int, target int) bool {
|
||||
var isBlocked bool
|
||||
err := db.QueryRow("SELECT COUNT(*) FROM blocks WHERE (source = ? AND target = ?) OR (source = ? AND target = ?)", source, target, target, source).Scan(&isBlocked)
|
||||
if err != nil {
|
||||
fmt.Println("error while checking if either ballcoks")
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return isBlocked
|
||||
}
|
||||
|
||||
// Render a user's avatar as a Mii URL with an emotion or return it if it's not a Mii.
|
||||
func getAvatar(avatar string, hasMii bool, feeling int) string {
|
||||
const url = "https://mii-secure.cdn.nintendo.net/%s_%s_face.png"
|
||||
if len(avatar) == 0 {
|
||||
return "/assets/img/anonymous.png"
|
||||
}
|
||||
if hasMii {
|
||||
switch feeling {
|
||||
case 1, 8:
|
||||
return fmt.Sprintf(url, avatar, "happy")
|
||||
case 2, 7:
|
||||
return fmt.Sprintf(url, avatar, "like")
|
||||
case 3, 6:
|
||||
return fmt.Sprintf(url, avatar, "surprised")
|
||||
case 4:
|
||||
return fmt.Sprintf(url, avatar, "frustrated")
|
||||
case 5:
|
||||
return fmt.Sprintf(url, avatar, "puzzled")
|
||||
default:
|
||||
return fmt.Sprintf(url, avatar, "normal")
|
||||
}
|
||||
}
|
||||
return avatar
|
||||
}
|
||||
|
||||
// Get the database values necessary for showing a comment preview.
|
||||
func getCommentPreview(postID int, currentUser user) comment {
|
||||
var commentPreview comment
|
||||
var timestamp time.Time
|
||||
var editedAt time.Time
|
||||
var role int
|
||||
|
||||
db.QueryRow("SELECT comments.id, created_at, edited_at, feeling, body, post_type, username, nickname, avatar, has_mh, online, hide_online, color, role FROM comments INNER JOIN users ON users.id = created_by WHERE post = ? AND is_rm = 0 AND is_rm_by_admin = 0 AND is_spoiler = 0 AND (users.id NOT IN (SELECT if(source = ?, target, source) FROM blocks WHERE (source = ? AND target = users.id) OR (source = users.id AND target = ?)) OR ? > 0) AND IF(created_by = ?, true, LOWER(body) NOT REGEXP LOWER(?)) ORDER BY comments.id DESC LIMIT 1", postID, currentUser.ID, currentUser.ID, currentUser.ID, currentUser.Level, currentUser.ID, escapeForbiddenKeywords(currentUser.ForbiddenKeywords)).Scan(&commentPreview.ID, ×tamp, &editedAt, &commentPreview.Feeling, &commentPreview.BodyText, &commentPreview.PostType, &commentPreview.CommenterUsername, &commentPreview.CommenterNickname, &commentPreview.CommenterIcon, &commentPreview.CommenterHasMii, &commentPreview.CommenterOnline, &commentPreview.CommenterHideOnline, &commentPreview.CommenterColor, &role)
|
||||
commentPreview.CommenterIcon = getAvatar(commentPreview.CommenterIcon, commentPreview.CommenterHasMii, commentPreview.Feeling)
|
||||
if role > 0 {
|
||||
commentPreview.CommenterRoleImage = getRoleImage(role)
|
||||
}
|
||||
commentPreview.CreatedAt = humanTiming(timestamp, currentUser.Timezone)
|
||||
commentPreview.CreatedAtUnix = timestamp.Unix()
|
||||
if editedAt.Sub(timestamp).Minutes() > 5 {
|
||||
commentPreview.EditedAt = humanTiming(editedAt, currentUser.Timezone)
|
||||
commentPreview.EditedAtUnix = editedAt.Unix()
|
||||
}
|
||||
commentPreview.Body = parseBody(commentPreview.BodyText, false, true)
|
||||
|
||||
return commentPreview
|
||||
}
|
||||
|
||||
// Check if a string violates a user's forbidden keywords.
|
||||
func inForbiddenKeywords(text string, userID int) bool {
|
||||
var forbiddenKeywords string
|
||||
err := db.QueryRow("SELECT forbidden_keywords FROM users WHERE id = ?", userID).Scan(&forbiddenKeywords)
|
||||
if err != nil {
|
||||
fmt.Println("error while getting forbidden keyword")
|
||||
fmt.Println(err)
|
||||
}
|
||||
isMatch, err := regexp.MatchString(escapeForbiddenKeywords(forbiddenKeywords), text)
|
||||
if err != nil {
|
||||
fmt.Println("error while dying")
|
||||
fmt.Println(err)
|
||||
}
|
||||
return isMatch
|
||||
}
|
||||
|
||||
// Get the current hostname.
|
||||
// TODO: this prints my local server's hostname like 127.0.0.1:8003:8003
|
||||
// but is it really worth fixing
|
||||
func getHostname(host string) string {
|
||||
hostname := "http"
|
||||
if settings.SSL.Enabled {
|
||||
hostname += "s"
|
||||
}
|
||||
hostname += "://" + host
|
||||
if (settings.Port == ":80" && settings.SSL.Enabled) || (settings.Port == ":443" && !settings.SSL.Enabled) || (settings.Port != ":80" && settings.Port != ":443") {
|
||||
hostname += settings.Port
|
||||
}
|
||||
|
||||
return hostname
|
||||
}
|
||||
|
||||
// Get the current user's IP.
|
||||
func getIP(r *http.Request) string {
|
||||
ForwardedForHeader := r.Header.Get("X-Forwarded-For")
|
||||
if settings.Proxy && len(ForwardedForHeader) > 0 { // Proxy sites like Cloudflare mask the IP, so grab that from the headers... if it's set in the settings, that is; otherwise, people could fake this and we'd have an impersonation exploit on our hands. (Looking at you, Seth)
|
||||
//ips := strings.Split(r.Header.Get("X-Forwarded-For"), ", ")
|
||||
//return ips[0] + settings.Port
|
||||
return ForwardedForHeader + ":0"
|
||||
} else {
|
||||
return r.RemoteAddr
|
||||
}
|
||||
}
|
||||
|
||||
// Get a poll from an ID.
|
||||
func getPoll(pollID int, userID int) poll {
|
||||
var newPoll poll
|
||||
option_rows, err := db.Query("SELECT options.id, name FROM options WHERE post = ?", pollID)
|
||||
if err != nil {
|
||||
fmt.Println("could not get poll")
|
||||
fmt.Println(err.Error())
|
||||
return newPoll
|
||||
}
|
||||
for option_rows.Next() {
|
||||
var row = option{}
|
||||
option_rows.Scan(&row.ID, &row.Name)
|
||||
user_rows, err := db.Query("SELECT users.id FROM votes LEFT JOIN users ON users.id = user WHERE poll = ? AND option_id = ?", pollID, row.ID)
|
||||
if err != nil {
|
||||
fmt.Println("could not die")
|
||||
fmt.Println(err.Error())
|
||||
return newPoll
|
||||
}
|
||||
for user_rows.Next() { // OPTIMIZE THIS!!!
|
||||
var currentID int
|
||||
user_rows.Scan(¤tID)
|
||||
if currentID == userID {
|
||||
row.Selected = true
|
||||
newPoll.Selected = true
|
||||
}
|
||||
|
||||
row.Votes = row.Votes + 1
|
||||
}
|
||||
user_rows.Close()
|
||||
|
||||
newPoll.Options = append(newPoll.Options, row)
|
||||
}
|
||||
option_rows.Close()
|
||||
|
||||
for _, row := range newPoll.Options {
|
||||
newPoll.Votes = newPoll.Votes + row.Votes
|
||||
}
|
||||
for i, row := range newPoll.Options {
|
||||
if row.Votes > 0 {
|
||||
newPoll.Options[i].Percentage = math.Round(row.Votes / newPoll.Votes * 100)
|
||||
} else {
|
||||
row.Percentage = 0
|
||||
}
|
||||
}
|
||||
|
||||
newPoll.ID = pollID
|
||||
return newPoll
|
||||
}
|
||||
|
||||
// Get the image of a role from its ID.
|
||||
func getRoleImage(roleID int) string {
|
||||
var image string
|
||||
db.QueryRow("SELECT image FROM roles WHERE id = ?", roleID).Scan(&image)
|
||||
return image
|
||||
}
|
||||
|
||||
// Get the image and organization of a role from its ID.
|
||||
func getRoleImageAndOrganization(roleID int) (string, string) {
|
||||
var image string
|
||||
var organization string
|
||||
err := db.QueryRow("SELECT image, organization FROM roles WHERE id = ?", roleID).Scan(&image, &organization)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
fmt.Println("role lookup failed: " + err.Error())
|
||||
}
|
||||
return image, organization
|
||||
}
|
||||
|
||||
// Get a user's timezone from their IP.
|
||||
func getTimezone(ip string) string {
|
||||
if isGeoIPEnabled == false {
|
||||
return settings.DefaultTimezone
|
||||
}
|
||||
|
||||
parsedHost, _, _ := net.SplitHostPort(ip)
|
||||
parsedIP := net.ParseIP(parsedHost)
|
||||
if parsedIP == nil {
|
||||
fmt.Println("parsedIP was nil")
|
||||
return settings.DefaultTimezone
|
||||
}
|
||||
|
||||
city, err := geoip.City(parsedIP)
|
||||
if err != nil {
|
||||
fmt.Println("no city")
|
||||
fmt.Println(err.Error())
|
||||
return settings.DefaultTimezone
|
||||
}
|
||||
|
||||
if len(city.Location.TimeZone) > 0 {
|
||||
return city.Location.TimeZone
|
||||
} else {
|
||||
return settings.DefaultTimezone
|
||||
}
|
||||
}
|
||||
|
||||
// Write to a websocket.
|
||||
func writeWs(session *wsSession, client *websocket.Conn, message wsMessage) error {
|
||||
session.Send <- message
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{{if .Posts}}
|
||||
<div class="list post-list js-post-list" data-next-page-url="?offset={{.Offset}}">
|
||||
{{$user_id := .CurrentUser.ID}}
|
||||
{{range $post := .Posts}}
|
||||
{{template "render_post.html" $post}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{if eq .Offset 20}}
|
||||
<div class="post-list-outline no-content no-post-content">
|
||||
<p>the timeline empty....<br>spend more time on your Social Links.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -0,0 +1,123 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
{{template "general_sidebar.html" .}}
|
||||
<div class="main-column">
|
||||
<div class="headline">
|
||||
<h2 class="headline-text">
|
||||
<span class="symbol activity-headline">timeline</span>
|
||||
</h2>
|
||||
<form class="search" action="/users">
|
||||
<input type="text" name="query" title="Search users" placeholder="Search users" minlength="1" maxlength="32">
|
||||
<input type="submit" value="q" title="Search">
|
||||
</form>
|
||||
</div>
|
||||
<div class="post-form none">
|
||||
<form id="post-form" method="post" action="/communities/0/posts"{{if not .Repost.ID}} class="folded"{{end}} data-post-subtype="default" name="test-post-default-form">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<input type="hidden" name="community" value="0">
|
||||
{{if .Repost.ID}}<input type="hidden" name="repost" value="{{.Repost.ID}}">
|
||||
<p class="repost-notice">You are reposting <a class="topic-title" href="/posts/{{.Repost.ID}}" target="_blank">{{.Repost.Nickname}}'s post ({{.Repost.Text}})</a> along with this submission.</p>{{end}}
|
||||
<div class="feeling-selector js-feeling-selector"><label class="symbol feeling-button feeling-button-normal checked"><input type="radio" name="feeling_id" value="0" checked><span class="symbol-label">normal</span></label><label class="symbol feeling-button feeling-button-happy"><input type="radio" name="feeling_id" value="1"><span class="symbol-label">happy</span></label><label class="symbol feeling-button feeling-button-like"><input type="radio" name="feeling_id" value="2"><span class="symbol-label">like</span></label><label class="symbol feeling-button feeling-button-surprised"><input type="radio" name="feeling_id" value="3"><span class="symbol-label">surprised</span></label><label class="symbol feeling-button feeling-button-frustrated"><input type="radio" name="feeling_id" value="4"><span class="symbol-label">frustrated</span></label><label class="symbol feeling-button feeling-button-puzzled"><input type="radio" name="feeling_id" value="5"><span class="symbol-label">puzzled</span></label></div>
|
||||
<div class="textarea-with-menu active-text">
|
||||
<menu class="textarea-menu">
|
||||
<li><label class="textarea-menu-text"><input type="radio" name="post_type" value="0"></label></li>
|
||||
<li><label class="textarea-menu-memo"><input type="radio" name="post_type" value="1"></label></li>
|
||||
<li><label class="textarea-menu-poll"><input type="radio" name="post_type" value="2"></label></li>
|
||||
<span class="character-count">2000</span>
|
||||
</menu>
|
||||
<div class="textarea-container">
|
||||
<textarea name="body" class="textarea-text textarea" maxlength="2000" placeholder="Share your thoughts in a post to the Activity Feed." data-open-folded-form data-required></textarea>
|
||||
</div>
|
||||
<div class="textarea-memo none">
|
||||
<div id="memo-drawboard-page" class="none">
|
||||
<div class="window-body">
|
||||
<div class="memo-buttons">
|
||||
<button type="button" class="artwork-clear"></button>
|
||||
<button type="button" class="artwork-undo"></button>
|
||||
<button type="button" class="artwork-pencil small selected"></button>
|
||||
<button type="button" class="artwork-eraser small"></button>
|
||||
<button type="button" class="artwork-fill"></button>
|
||||
<input type="text" class="artwork-color">
|
||||
<button type="button" class="artwork-zoom"></button>
|
||||
</div>
|
||||
<div class="memo-canvas">
|
||||
<canvas id="artwork-canvas" zoom="2"></canvas>
|
||||
<canvas id="artwork-canvas-undo"></canvas>
|
||||
<canvas id="artwork-canvas-redo"></canvas>
|
||||
<input type="hidden" name="painting">
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<input class="olv-modal-close-button black-button memo-finish-btn" type="button" value="Save">
|
||||
<button type="button" class="artwork-lock none"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="textarea-poll none">
|
||||
<button type="button" class="delete none" option="option-a"></button><input type="text" class="url-form option" name="option-a" placeholder="Option A" maxlength="64" data-required>
|
||||
<button type="button" class="delete none" option="option-b"></button><input type="text" class="url-form option" name="option-b" placeholder="Option B" maxlength="64" data-required>
|
||||
<button type="button" class="add-option symbol">Add Option</button>
|
||||
</div>
|
||||
</div>
|
||||
<label class="file-button-container">
|
||||
<span class="input-label">Attachment <span>Images, audio and videos are allowed.
|
||||
{{if .MaxUploadSize}}Maximum upload size: {{.MaxUploadSize}}{{end}}
|
||||
</span></span>
|
||||
<span class="button file-upload-button">Upload</span>
|
||||
<input accept="image/*, audio/*, video/*" type="file" class="file-button none">
|
||||
<input type="hidden" name="image">
|
||||
<input type="hidden" name="attachment_type">
|
||||
<div class="screenshot-container still-image preview-container" style="display: none;">
|
||||
<img class="preview-image none">
|
||||
<video class="preview-video none" controls></video>
|
||||
<audio class="preview-audio none" controls></audio>
|
||||
</div>
|
||||
<script src="/assets/js/upload.js"></script>
|
||||
</label>
|
||||
<div class="post-form-footer-options">
|
||||
<div class="post-form-footer-option-inner post-form-spoiler js-post-form-spoiler test-post-form-spoiler">
|
||||
<label class="spoiler-button symbol"><input type="checkbox" id="is_spoiler" name="is_spoiler" value="1">Spoilers</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="post-form-privacy">
|
||||
<p>Who should be able to see this post?</p>
|
||||
<select class="post-form-privacy-select" name="privacy">
|
||||
<option value="0"{{if eq .CurrentUser.DefaultPrivacy 0}} selected{{end}}>Everyone</option>
|
||||
<option value="1"{{if eq .CurrentUser.DefaultPrivacy 1}} selected{{end}}>Friends, Following and Followers</option>
|
||||
<option value="2"{{if eq .CurrentUser.DefaultPrivacy 2}} selected{{end}}>Friends and Following</option>
|
||||
<option value="3"{{if eq .CurrentUser.DefaultPrivacy 3}} selected{{end}}>Friends and Followers</option>
|
||||
<option value="4"{{if eq .CurrentUser.DefaultPrivacy 4}} selected{{end}}>Friends Only</option>
|
||||
<option value="5"{{if eq .CurrentUser.DefaultPrivacy 5}} selected{{end}}>Followers and Following</option>
|
||||
<option value="6"{{if eq .CurrentUser.DefaultPrivacy 6}} selected{{end}}>Followers Only</option>
|
||||
<option value="7"{{if eq .CurrentUser.DefaultPrivacy 7}} selected{{end}}>Following Only</option>
|
||||
<option value="8"{{if eq .CurrentUser.DefaultPrivacy 8}} selected{{end}}>Admins Only</option>
|
||||
<option value="9"{{if eq .CurrentUser.DefaultPrivacy 9}} selected{{end}}>Only Me</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="black-button post-button disabled" value="Send" data-community-id="{{.Community.ID}}" data-post-content-type="text" data-post-with-screenshot="nodata" disabled>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="js-main">
|
||||
<div class="activity-feed content-loading-window">
|
||||
<span class="loading-spinner"></span>
|
||||
<div><p class="tleft"><span>its all going down....</span></p></div>
|
||||
<div class="activity-feed content-load-error-window none">
|
||||
<div>
|
||||
<p>couldnt load the page sorry!!</p><br>
|
||||
<img src="https://booru.soy/_images/d305f80b6f0b5df18492ab7aa039f899/1229%20-%20SoyBooru.png">
|
||||
<div class="buttons-content"><a href="/activity" class="button">Reload</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,48 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {
|
||||
background-color: #20262e;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #3f8aff;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 128px;
|
||||
}
|
||||
img {
|
||||
width: 50px;
|
||||
}
|
||||
</style>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
{{if eq .Offset 0}}<p>arian was rushed to make this by the Indigo staff team, sorry it sucks so much</p>{{end}}
|
||||
<form>
|
||||
number to start at: <input type="number" name="offset" placeholder="offset" value="{{.Offset}}">
|
||||
type: <select name="type">
|
||||
<option value=""></option>
|
||||
<option value="0"{{if eq .Type "0"}} selected{{ end }}>post delete</option>
|
||||
<option value="1"{{if eq .Type "1"}} selected{{ end }}>comment delete</option>
|
||||
<option value="2"{{if eq .Type "2"}} selected{{ end }}>ban</option>
|
||||
<option value="3"{{if eq .Type "3"}} selected{{ end }}>unban</option>
|
||||
</select>
|
||||
username: <input type="text" name="username" placeholder="admin username" value="{{.User}}">
|
||||
<button>go</button>
|
||||
<input type="hidden" name="offset_time" value="{{.OffsetTime}}">
|
||||
</form>
|
||||
<ul>
|
||||
{{range $entry := .AuditLogEntries}}
|
||||
<li>
|
||||
<a href="/users/{{$entry.CreatorUsername}}" title="{{$entry.CreatorNickname}}"><img src="{{$entry.CreatorFinalAva}}" title="{{$entry.CreatorNickname}}" alt="{{$entry.CreatorNickname}}"></a> <a href="/users/{{$entry.CreatorUsername}}">{{$entry.CreatorNickname}}</a> did <b><a href="{{$entry.TypeURI}}">{{$entry.TypeText}}</b> {{if or (not (eq $entry.Type 4)) (not (eq $entry.CreatorFinalAva $entry.TargetUserAvatar))}}<img src="{{$entry.TargetUserAvatar}}">{{end}}{{$entry.PostSummary}}</a>
|
||||
| #{{$entry.ID}} {{$entry.CreatedAt}}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
{{if eq .Offset 25}}
|
||||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
<div id="sidebar">
|
||||
<menu id="admin-menu">
|
||||
<li id="admin-menu-list">
|
||||
<ul>
|
||||
<li id="admin-menu-dashboard" class="selected"><a href="/admin" class="symbol"><span>Dashboard</span></a></li>
|
||||
{{if le .Admin.Manage.MinimumLevel .CurrentUser.Level}}<li id="admin-menu-manage"><a href="/admin/manage" class="symbol"><span>Manage</span></a></li>{{end}}
|
||||
{{if le .Admin.Settings.MinimumLevel .CurrentUser.Level}}<li id="admin-menu-settings"><a href="/admin/settings" class="symbol"><span>Settings</span></a></li>{{end}}
|
||||
</ul>
|
||||
</li>
|
||||
</menu>
|
||||
</div>
|
||||
<div class="main-column">
|
||||
<div class="admin-dashboard">
|
||||
<div id="postsz">
|
||||
<div class="body-content" id="community-post-list">
|
||||
{{end}}
|
||||
<div class="list post-list js-post-list"{{if .Reports}} data-next-page-url="?offset={{.Offset}}&offset_time={{.OffsetTime}}"{{end}}>
|
||||
{{if .Reports}}
|
||||
{{range $report := .Reports}}
|
||||
<div id="{{$report.ID}}" class="report post post-list-outline">
|
||||
<p class="user-name">Reported by: <a href="/users/{{$report.ByUsername}}"{{if $report.ByColor}} style="color:{{$report.ByColor}}"{{end}}>{{$report.ByNickname}}</a></p>
|
||||
<code class="report-message">{{$report.Message}}</code>
|
||||
{{template "render_post.html" $report.Post}}
|
||||
<div class="form-buttons">
|
||||
<button class="report-action-button gray-button" type="button" data-action="/reports/{{$report.ID}}/ignore">Ignore</button><button class="report-action-button black-button" type="button" data-action="/{{if eq $report.Type 1}}comment{{else if eq $report.Type 2}}user{{else}}post{{end}}s/{{$report.Post.ID}}/delete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else if eq .Offset 25}}
|
||||
<div class="no-content no-post-content post-list-outline">
|
||||
<p>No posts have been reported yet.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if eq .Offset 25}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -0,0 +1,57 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}} - Indigo</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
<div id="sidebar">
|
||||
<menu id="admin-menu">
|
||||
<li id="admin-menu-list">
|
||||
<ul>
|
||||
<li id="admin-menu-dashboard"><a href="/admin" class="symbol"><span>Dashboard</span></a></li>
|
||||
<li id="admin-menu-manage" class="selected"><a href="/admin/manage" class="symbol"><span>Manage</span></a></li>
|
||||
{{if le .Admin.Settings.MinimumLevel .CurrentUser.Level}}<li id="admin-menu-settings"><a href="/admin/settings" class="symbol"><span>Settings</span></a></li>{{end}}
|
||||
</ul>
|
||||
</li>
|
||||
</menu>
|
||||
</div>
|
||||
<div class="main-column">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">Admin Manager</h2>
|
||||
<p style="margin:20px 10px 0px">Pip told me I shouldn't actually work on this until I have the actual essential features for Indigo done, so this is all you're getting for now. Sorry.</p>
|
||||
<form class="setting-form" method="post" action="/admin/manage/bantemp">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<label class="note"><p><a href="/admin/audit_log">Click to view audit logs.</a></p></label>
|
||||
<p class="settings-label">Ban User</p>
|
||||
<input type="text" name="username" placeholder="Username">
|
||||
<p><label class="note">Ban entire IP range: <input type="range" name="cidr" min="0" max="2" step="1"></label></p>
|
||||
<label class="note">Length:
|
||||
<select name="length">
|
||||
<option value="1">1 day</option>
|
||||
<option value="2">2 days</option>
|
||||
<option value="3">3 days</option>
|
||||
<option value="4">4 days</option>
|
||||
<option value="5">5 days</option>
|
||||
<option value="6">6 days</option>
|
||||
<option value="7">1 week</option>
|
||||
<option value="14">2 weeks</option>
|
||||
<option value="28">4 weeks</option>
|
||||
<option value="90">90 days</option>
|
||||
<option value="365">1 year</option>
|
||||
<option value="253383">Life</option>
|
||||
</select>
|
||||
</label><br>
|
||||
<button class="black-button" type="submit">Do it</button>
|
||||
</form><br>
|
||||
<form class="setting-form" method="post" action="/admin/manage/unbantemp">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<p class="settings-label">Unban User</p>
|
||||
<input type="text" name="username" placeholder="Username"><br>
|
||||
<button class="black-button" type="submit">Do it</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,165 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
<div id="sidebar">
|
||||
<menu id="admin-menu">
|
||||
<li id="admin-menu-list">
|
||||
<ul>
|
||||
<li id="admin-menu-dashboard"><a href="/admin" class="symbol"><span>Dashboard</span></a></li>
|
||||
{{if le .Admin.Manage.MinimumLevel .CurrentUser.Level}}<li id="admin-menu-manage"><a href="/admin/manage" class="symbol"><span>Manage</span></a></li>{{end}}
|
||||
<li id="admin-menu-settings" class="selected"><a href="/admin/settings" class="symbol"><span>Settings</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</menu>
|
||||
</div>
|
||||
<div class="main-column">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">Admin Settings</h2>
|
||||
<form class="setting-form" method="post">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<ul class="settings-list admin-settings-list">
|
||||
<p class="settings-label">Here you can edit various global Indigo settings.<br>Any changes you make will impact the entire website. If you don't know what any of this is, LEAVE THIS PAGE NOW.</p>
|
||||
<li>
|
||||
<p class="settings-label">Image Host</p>
|
||||
<p class="note">Hosting Provider</p>
|
||||
<div class="select-content">
|
||||
<div class="select-button">
|
||||
<select name="imagehost_provider">
|
||||
<option value="local"{{if eq .Settings.ImageHost.Provider "local"}} selected{{end}}>Local (not yet implemented)</option>
|
||||
<option value="cloudinary"{{if eq .Settings.ImageHost.Provider "cloudinary"}} selected{{end}}>Cloudinary</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="js-not-local{{if eq .Settings.ImageHost.Provider "local"}} none{{end}}">
|
||||
<p class="note js-cloud-name">{{if eq .Settings.ImageHost.Provider "cloudinary"}}Cloud N{{else}}Usern{{end}}ame</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="imagehost_username" placeholder="{{if eq .Settings.ImageHost.Provider "cloudinary"}}Cloud N{{else}}Usern{{end}}ame" value="{{.Settings.ImageHost.Username}}">
|
||||
</div>
|
||||
<p class="note js-upload-preset">{{if eq .Settings.ImageHost.Provider "cloudinary"}}Upload Preset{{else}}Password{{end}}</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="imagehost_uploadpreset" placeholder="{{if eq .Settings.ImageHost.Provider "cloudinary"}}Upload Preset{{else}}Password{{end}}" value="{{.Settings.ImageHost.UploadPreset}}">
|
||||
</div>
|
||||
<p class="note">API Endpoint</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="imagehost_apiendpoint" placeholder="API Endpoint" value="{{.Settings.ImageHost.APIEndpoint}}">
|
||||
</div>
|
||||
</div>
|
||||
<p class="note">Max Upload Size (shown to the client, not enforced)</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="imagehost_maxuploadsize" placeholder="Max Upload Size" value="{{.Settings.ImageHost.MaxUploadSize}}">
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p class="settings-label">ReCAPTCHA</p>
|
||||
<label class="note">Enabled: <input type="checkbox" name="recaptcha_enabled" value="1"{{if .Settings.ReCAPTCHA.Enabled}} checked{{end}}></label>
|
||||
<div class="js-recaptcha-enabled{{if not .Settings.ReCAPTCHA.Enabled}} none{{end}}">
|
||||
<p class="note">Site Key</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="recaptcha_sitekey" placeholder="Site Key" value="{{.Settings.ReCAPTCHA.SiteKey}}">
|
||||
</div>
|
||||
<p class="note">Secret Key (KEEP SECRET!!!!!!!!!!!!!!!!!!!!)</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="recaptcha_secretkey" placeholder="Secret Key" value="{{.Settings.ReCAPTCHA.SecretKey}}">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p class="settings-label">Webhooks</p>
|
||||
<label class="note">Enabled: <input type="checkbox" name="webhooks_enabled" value="1"{{if .Settings.Webhooks.Enabled}} checked{{end}}></label>
|
||||
<div class="js-webhooks-enabled{{if not .Settings.Webhooks.Enabled}} none{{end}}">
|
||||
<p class="note">Report Webhook</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="webhooks_reports" placeholder="Report Webhook" value="{{.Settings.Webhooks.Reports}}">
|
||||
</div>
|
||||
<p class="note">Signup Webhook</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="webhooks_signups" placeholder="Signup Webhook" value="{{.Settings.Webhooks.Signups}}">
|
||||
</div>
|
||||
<p class="note">Login Webhook</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="webhooks_logins" placeholder="Login Webhook" value="{{.Settings.Webhooks.Logins}}">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p class="settings-label">Email (SMTP)</p>
|
||||
<label class="note">Enabled: <input type="checkbox" name="smtp_enabled" value="1"{{if .Settings.SMTP.Enabled}} checked{{end}}></label>
|
||||
<div class="js-smtp-enabled{{if not .Settings.SMTP.Enabled}} none{{end}}">
|
||||
<p class="note">Hostname</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="smtp_hostname" placeholder="SMTP Hostname" value="{{.Settings.SMTP.Hostname}}">
|
||||
</div>
|
||||
<p class="note">Port</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="smtp_port" placeholder="SMTP Port" value="{{.Settings.SMTP.Port}}">
|
||||
</div>
|
||||
<p class="note">Email Address</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="smtp_email" placeholder="Email Address" value="{{.Settings.SMTP.Email}}">
|
||||
</div>
|
||||
<p class="note">Password</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="smtp_password" placeholder="Password" value="{{.Settings.SMTP.Password}}">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<!-- insert report reason editor here -->
|
||||
<li>
|
||||
<p class="settings-label">Is the site being hosted through a DNS proxy? (e.g. Cloudflare)</p>
|
||||
<div class="select-content">
|
||||
<div class="select-button">
|
||||
<select name="proxy">
|
||||
<option value="1"{{if eq .Settings.Proxy true}} selected{{end}}>Yes</option>
|
||||
<option value="0"{{if eq .Settings.Proxy false}} selected{{end}}>No</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p class="settings-label">Should users be forced to log in to see the site?</p>
|
||||
<div class="select-content">
|
||||
<div class="select-button">
|
||||
<select name="forcelogins">
|
||||
<option value="1"{{if eq .Settings.ForceLogins true}} selected{{end}}>Yes</option>
|
||||
<option value="0"{{if eq .Settings.ForceLogins false}} selected{{end}}>No</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p class="settings-label">Should users be allowed to sign up?</p>
|
||||
<div class="select-content">
|
||||
<div class="select-button">
|
||||
<select name="allowsignups">
|
||||
<option value="1"{{if eq .Settings.AllowSignups true}} selected{{end}}>Yes</option>
|
||||
<option value="0"{{if eq .Settings.AllowSignups false}} selected{{end}}>No</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p class="settings-label">What should the default timezone be?</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="defaulttimezone" placeholder="Default Timezone" value="{{.Settings.DefaultTimezone}}">
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p class="settings-label">Emote Limit</p>
|
||||
<div class="center center-input">
|
||||
<input type="number" name="emotelimit" value="{{.Settings.EmoteLimit}}">
|
||||
</div>
|
||||
</li>
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="black-button apply-button" value="Save Settings">
|
||||
</div>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,8 @@
|
|||
<ul class="list reply-list test-reply-list">
|
||||
{{range $comment := .PinnedComments}}
|
||||
{{template "render_comment.html" $comment}}
|
||||
{{end}}
|
||||
{{range $comment := .Comments}}
|
||||
{{template "render_comment.html" $comment}}
|
||||
{{end}}
|
||||
</ul>
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>BANNED LOL!</title>
|
||||
<meta http-equiv="content-style-type" content="text/css">
|
||||
<meta http-equiv="content-script-type" content="text/javascript">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-title" content="Indigo">
|
||||
<meta name="description" content="go use truth social or some shit">
|
||||
<meta property="og:locale" content="en_US">
|
||||
<meta property="og:title" content="{{.Title}}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:image" content="/assets/img/favicon.png">
|
||||
<meta property="og:description" content="go use truth social or some shit">
|
||||
<meta property="og:site_name" content="sop.epic">
|
||||
<meta property="article:published_time" content="None">
|
||||
<link rel="shortcut icon" type="image/png" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/css/offdevice.css">
|
||||
<link id="darkness" {{if .CurrentUser.LightMode}}disabled{{end}} rel="stylesheet" type="text/css" href="/assets/css/dark.css">
|
||||
</head>
|
||||
<body class="simple-wrapper simple-wrapper-content">
|
||||
<div id="wrapper" style="margin-top:0">
|
||||
<div id="main-body">
|
||||
<div class="warning-content warning-content-restricted track-error" data-track-error="restricted">
|
||||
<div>
|
||||
|
||||
<p>BANNED LOL!!!!!!!!!!!!!</p>
|
||||
<p>Ban expiration date: <strong>{{.Length}}</strong>{{ if .LengthForever }} okay so basically forever{{ end }}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<img src="/assets/img/restricted.png" width="800" height="600">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
|
@ -0,0 +1,42 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
<meta property="og:description" content="welcome back!!">
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body" class="guest">
|
||||
<div class="main-column center">
|
||||
<div class="post-list-outline login-page">
|
||||
<form method="post">
|
||||
{{.CSRFField}}
|
||||
<img src="/assets/img/menu-logo.png">
|
||||
<p class="lh">Log In</p>
|
||||
<p>
|
||||
{{if not .ForceLogins}}
|
||||
welcome back!!
|
||||
{{else}}
|
||||
<img src="https://res.cloudinary.com/indigolang/image/upload/v1540207716/xlg6ms1uq661a5qp9gp1.png" style="width:100%">
|
||||
{{end}}
|
||||
</p>
|
||||
<h3 class="label"><label>User ID: <input type="text" class="auth-input" name="username" maxlength="32" placeholder="User ID"></label></h3>
|
||||
<h3 class="label"><label>Password: <input type="password" class="auth-input" name="password" maxlength="32" placeholder="Password"></label></h3>
|
||||
<p class="red" style="margin-bottom:6px">{{if eq .FormError "1"}}The username/password combination you entered is not correct.{{end}}</p>
|
||||
<button type="submit" class="button">Sign In</button>
|
||||
<div class="ll">
|
||||
</div>
|
||||
{{if .AllowSignups}}
|
||||
<div class="ll">
|
||||
<p>If you don't have an account, <a href="/signup">you can sign up here.</a></p>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="ll">
|
||||
<p>If you don't have an account, <a href="https://www.youtube.com/watch?v=b0T5Qj6DRTk">you can sign up here!!</a></p>
|
||||
</div>
|
||||
{{end}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,57 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
<meta property="og:description" content="Request a password reset here.">
|
||||
{{else}}
|
||||
<title>{{.Title}} - Indigo</title>
|
||||
{{end}}
|
||||
<div id="main-body" class="guest">
|
||||
<div class="main-column center">
|
||||
<div class="post-list-outline login-page">
|
||||
<form method="post">
|
||||
{{.CSRFField}}
|
||||
<img src="/assets/img/menu-logo.png" alt="Indigo">
|
||||
<p class="lh">Reset Password{{if .Username}} for {{.Username}}{{end}}{{if .Error}} Error{{end}}</p>
|
||||
{{if eq .Action "request"}}
|
||||
<p>Forgot your password or accidentally gave it to someone who shouldn't have it? Don't worry, we've got you covered. Just type your email here and we'll send you a link to change it!</p>
|
||||
<h3 class="label">
|
||||
<label>
|
||||
Email address: <input type="text" class="auth-input" name="email" maxlength="255" placeholder="indigo@pf2m.com">
|
||||
</label>
|
||||
</h3>
|
||||
<button type="submit" class="button">Send</button>
|
||||
{{else if eq .Action "sent"}}
|
||||
<p>Success! The email was sent to <strong>{{.Email}}</strong>.</p>
|
||||
<p>Note that the message may be in your "Junk" or "Spam" boxes, so if you can't find it check there. The message may also take a while to send.</p>
|
||||
<p>Until then, you can <a href="/">click here to continue browsing the website if you want.</a></p>
|
||||
{{else if eq .Action "reset"}}
|
||||
<p>Hooray, you got the email! You're almost done now. Just enter in your new password twice (to make sure you don't misspell it) and hit that nice little button below, it's called the "reset button".</p>
|
||||
<h3 class="label">
|
||||
<label>
|
||||
Password: <input type="password" class="auth-input" name="password" maxlength="255" placeholder="hunter2">
|
||||
</label>
|
||||
</h3>
|
||||
<h3 class="label">
|
||||
<label>
|
||||
Confirm Password: <input type="password" class="auth-input" name="confirm" maxlength="255" placeholder="hunter2">
|
||||
</label>
|
||||
</h3>
|
||||
{{if .Error}}<p class="red" style="margin-bottom:6px">{{.Error}}</p>{{end}}
|
||||
<button type="submit" class="button">Reset</button>
|
||||
{{else if eq .Action "success"}}
|
||||
<p>The reset operation was successful! You can now <a href="/login">log into your account</a> with your new password.</p>
|
||||
<p>Thanks for using Indigo!</p>
|
||||
{{else if eq .Action "error"}}
|
||||
<p>{{.Error}}</p>
|
||||
{{else if eq .Action "disabled"}}
|
||||
<p>Password resets are not enabled on this instance of Indigo.</p>
|
||||
<p>To remedy this, tell the owner of the service to set up an SMTP server and specify it in their Indigo configuration.</p>
|
||||
<p>You can learn how to do this <a href="https://github.com/PF2M/Indigo/wiki">on the Indigo GitHub's Wiki page.</a></p>
|
||||
{{end}}
|
||||
<br>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<img src="{{.Hostname}}/assets/img/menu-logo.png">
|
||||
<br>A password reset request has been made for your account.
|
||||
<br>If you initiated this request, go here: <a href="{{.Hostname}}/reset?token={{.Token}}">{{.Hostname}}/reset?token={{.Token}}</a>
|
||||
<br>Otherwise, you can probably ignore this email, as these kinds of requests can be sent by anyone.
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
<meta property="og:description" content="sign up BITCH">
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body" class="guest">
|
||||
<div class="main-column center">
|
||||
<div class="post-list-outline login-page">
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<img src="/assets/img/menu-logo.png">
|
||||
<p class="lh">sign up BITCH</p>
|
||||
<p>creating an sop.epic account lets you interact with everybody else with an account!</p><br>
|
||||
<p><img src="/assets/img/bocchi.gif" width="320" height="240"><br>
|
||||
<a href="/help/rules">please read the TOS thanks</a></p>
|
||||
<h3 class="label"><label><span class="red">*</span> User ID: <input type="text" class="auth-input" name="username" maxlength="32" minlength="4" placeholder="ID"></label></h3>
|
||||
<h3 class="label"><label><span class="red">*</span> Nickname: <input type="text" class="auth-input" name="nickname" maxlength="32" placeholder="Nickname"></label></h3>
|
||||
<label class="file-button-container">
|
||||
<span class="input-label" style="color:black;">Avatar Image <span>PNG, JPEG and GIF are allowed.</span></span>
|
||||
<span class="button file-upload-button for-avatar">Upload</span>
|
||||
<input accept="image/*" type="file" class="file-button none">
|
||||
<input type="hidden" name="image">
|
||||
<div class="screenshot-container still-image preview-container">
|
||||
<img class="preview-image">
|
||||
</div>
|
||||
<script src="/assets/js/upload.js"></script>
|
||||
</label>
|
||||
<h3 class="label"><label>Email address: <input type="email" class="auth-input" name="email" maxlength="255" placeholder="Email (optional)"></label></h3>
|
||||
<h3 class="label"><label><span class="red">*</span> Password: <input type="password" class="auth-input" name="password" maxlength="32" placeholder="Password"></label></h3>
|
||||
<h3 class="label"><label><span class="red">*</span> Confirm Password: <input type="password" class="auth-input" name="confirm" maxlength="32" placeholder="Confirm Password"></label></h3>
|
||||
{{if .ReCAPTCHA.Enabled}}
|
||||
<script src="https://www.google.com/recaptcha/api.js"></script>
|
||||
<div class="g-recaptcha" style="display:inline-block" data-sitekey="{{.ReCAPTCHA.SiteKey}}"></div>
|
||||
{{end}}
|
||||
<p class="red" style="margin-bottom:6px"></p>
|
||||
<button type="submit" class="button">Create account</button>
|
||||
<div class="ll">
|
||||
<p>All fields with a red asterisk (<span class="red">*</span>) are required.</p>
|
||||
<p>You can change all of these fields after you sign up; except for usernames, which can only be changed by admins.</p>
|
||||
<p>If no email address is associated with your account, you won't be able to reset your password.</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,68 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body" class="profile-top">
|
||||
{{template "general_sidebar.html" .}}
|
||||
<div class="main-column">
|
||||
<div class="post-list-outline">
|
||||
<div class="body-content" id="community-top">
|
||||
<h2 class="label">Blocked Users</h2>
|
||||
<ul class="list news-list" {{if .Users}} data-next-page-url="/blocked?offset={{.Offset}}"{{end}}>
|
||||
{{if .Users}}
|
||||
{{range $user := .Users}}
|
||||
<li class="trigger" data-href="/users/{{$user.Username}}">
|
||||
<a href="/users/{{$user.Username}}" username="{{$user.Username}}" class="icon-container{{if not $user.HideOnline}}{{if $user.Online}} online{{else}} offline{{end}}{{end}}
|
||||
{{if $user.Role.Image}} official-user"><img src="{{$user.Role.Image}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{$user.Avatar}}" alt="{{$user.Nickname}}" class="icon">
|
||||
</a>
|
||||
<div class="body">
|
||||
<a href="/users/{{$user.Username}}" class="nick-name"{{if $user.Color}} style="color:{{$user.Color}}"{{end}}>{{$user.Nickname}}</a>
|
||||
<span class="id-name">{{$user.Username}}</span><br>
|
||||
<span class="timestamp">{{$user.LastSeen}}</span>
|
||||
<button class="button received-request-button" type="button" data-user-id="{{$user.ID}}">Unblock</button>
|
||||
</div>
|
||||
</li>
|
||||
<div class="dialog none" data-modal-types="post-unblock" id="{{$user.ID}}">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">Unblock {{$user.Nickname}}</h1>
|
||||
<div class="window-body">
|
||||
<div id="sidebar-profile-body">
|
||||
<div username="{{$user.Username}}" class="icon-container{{if not $user.HideOnline}}{{if $user.Online}} online{{else}} offline{{end}}{{end}}
|
||||
{{if $user.Role.Image}} official-user"><img src="{{$user.Role.Image}}" class="official-tag">{{else}}">{{end}}
|
||||
<a href="/users/{{$user.Username}}">
|
||||
<img src="{{$user.Avatar}}" alt="{{$user.Nickname}}" class="icon">
|
||||
</a>
|
||||
</div>
|
||||
<a href="/users/{{$user.Username}}" class="nick-name"{{if $user.Color}} style="color:{{$user.Color}}"{{end}}>{{$user.Nickname}}</a>
|
||||
<p class="id-name">{{$user.Username}}</p>
|
||||
</div>
|
||||
<form method="post" data-action="/users/{{$user.Username}}/unblock">
|
||||
<p class="window-body-content">Unblock this user?</p>
|
||||
<div class="form-buttons">
|
||||
<input type="button" class="olv-modal-close-button gray-button" value="No">
|
||||
<input type="submit" value="Yes" class="post-button black-button">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Offset 25}}
|
||||
<div class="no-content">
|
||||
<p>You haven't blocked any users.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,242 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
<meta property="og:profile:username" content="{{.Comment.CommenterUsername}}">
|
||||
<meta property="og:description" content="{{if .Comment.Body}}{{.Comment.Body}}{{else}}View {{.Comment.CommenterNickname}}'s comment on Indigo.{{end}}">
|
||||
{{if .Comment.Image}}<meta property="og:{{if eq .Comment.AttachmentType 1}}audio{{else if eq .Comment.AttachmentType 2}}video{{else}}image{{end}}" content="{{.Comment.Image}}">{{end}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
<div class="main-column">
|
||||
<div class="post-list-outline">
|
||||
<a class="post-permalink-button info-ticker"{{if not .Post.IsRM}} href="/posts/{{.Comment.PostID}}"{{end}}>
|
||||
<span class="icon-container"><img src="{{.Post.PosterIcon}}" class="icon"></span>
|
||||
<span>View <span class="post-user-description">{{.Post.PosterNickname}}'s post ({{.Post.BodyText}})</span> for this comment.</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="post-list-outline more">
|
||||
<div id="post-content" class="post reply-permalink-post">
|
||||
<div id="{{.Comment.ID}}" class="other">
|
||||
<p class="community-container"><a{{if not .Post.CommunityRM}} href="/communities/{{.Post.CommunityID}}"{{end}}><img src="{{.Post.CommunityIcon}}" class="community-icon">{{.Post.CommunityName}}</a></p>
|
||||
{{if not .Comment.IsRMByAdmin}}
|
||||
{{if or (eq .Comment.CreatedBy .CurrentUser.ID) (gt .CurrentUser.Level 0)}}
|
||||
<div class="edit-buttons-content">
|
||||
<button type="button" class="symbol button edit-button rm-post-button" data-action="/comments/{{.Comment.ID}}/delete"><span class="symbol-label">Delete</span></button>
|
||||
{{if and (eq .Comment.CreatedBy .CurrentUser.ID) (not (eq .Comment.PostType 1))}}<button type="button" class="symbol button edit-button edit-post-button"><span class="symbol-label">Edit</span></button>{{end}}
|
||||
</div>
|
||||
{{else if .CurrentUser.Username}}
|
||||
<div class="report-buttons-content" style="float:right"><button type="button" class="report-button" data-modal-open="#report-violation-page" data-screen-name="{{.Comment.CommenterNickname}}" data-support-text="#{{.Comment.ID}}" data-action="/comments/{{.Comment.ID}}/violations" data-is-permalink="1" data-can-report-spoiler="{{if .Comment.IsSpoiler}}0{{else}}1{{end}}" data-url-id="{{.Comment.ID}}" data-track-action="openReportModal">Report Violation</button></div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<div class="user-content">
|
||||
<a username="{{.Comment.CommenterUsername}}" href="/users/{{.Comment.CommenterUsername}}" class="icon-container
|
||||
{{if not .Comment.CommenterHideOnline}}
|
||||
{{if .Comment.CommenterOnline}}
|
||||
online
|
||||
{{else}}
|
||||
offline
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .Comment.CommenterRoleImage}} official-user"><img src="{{.Comment.CommenterRoleImage}}" class="official-tag">{{else}}">{{end}}<img src="{{.Comment.CommenterIcon}}" class="icon">
|
||||
</a>
|
||||
<div class="user-name-content">
|
||||
{{if .Comment.CommenterRoleOrganization}}<p class="user-organization">{{.Comment.CommenterRoleOrganization}}</p>{{end}}
|
||||
<p class="user-name"><a href="/users/{{.Comment.CommenterUsername}}"{{if .Comment.CommenterColor}} style="color:{{.Comment.CommenterColor}}"{{end}}>{{.Comment.CommenterNickname}}</a></p>
|
||||
<p class="timestamp-container">
|
||||
<span class="spoiler-status{{if .Comment.Pinned}} spoiler{{end}}">Pinned ·</span>
|
||||
{{if .Post.Privacy}}<span class="spoiler-status spoiler">Private ·</span>{{end}}
|
||||
<span class="spoiler-status{{if .Comment.IsSpoiler}} spoiler{{end}}">Spoilers ·</span>
|
||||
<span class="timestamp">
|
||||
<span class="update" time="{{.Comment.CreatedAtUnix}}000">{{.Comment.CreatedAt}}</span>
|
||||
{{if .Comment.EditedAt}}
|
||||
(Edited <span class="update" time="{{.Comment.EditedAtUnix}}000">{{.Comment.EditedAt}}</span>)
|
||||
{{end}}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">
|
||||
{{if .Comment.IsRMByAdmin}}
|
||||
<p class="deleted-message">
|
||||
Deleted by administrator.<br>
|
||||
Comment ID: #{{.Comment.ID}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{if or (not .Comment.IsRMByAdmin) .Comment.ByMe}}
|
||||
{{if and (eq .Comment.CreatedBy .CurrentUser.ID) (not (eq .Comment.PostType 1))}}
|
||||
<div id="post-edit" class="none">
|
||||
<form data-action="/comments/{{.Comment.ID}}/edit" id="edit-form" method="post">
|
||||
<div class="feeling-selector js-feeling-selector test-feeling-selector"><label class="symbol feeling-button feeling-button-normal checked"><input type="radio" name="feeling_id" value="0"{{if eq .Comment.Feeling 0}} checked{{end}}><span class="symbol-label">normal</span></label><label class="symbol feeling-button feeling-button-happy"><input type="radio" name="feeling_id" value="1"{{if eq .Comment.Feeling 1}} checked{{end}}><span class="symbol-label">happy</span></label><label class="symbol feeling-button feeling-button-like"><input type="radio" name="feeling_id" value="2"{{if eq .Comment.Feeling 2}} checked{{end}}><span class="symbol-label">like</span></label><label class="symbol feeling-button feeling-button-surprised"><input type="radio" name="feeling_id" value="3"{{if eq .Comment.Feeling 3}} checked{{end}}><span class="symbol-label">surprised</span></label><label class="symbol feeling-button feeling-button-frustrated"><input type="radio" name="feeling_id" value="4"{{if eq .Comment.Feeling 4}} checked{{end}}><span class="symbol-label">frustrated</span></label><label class="symbol feeling-button feeling-button-puzzled"><input type="radio" name="feeling_id" value="5"{{if eq .Comment.Feeling 5}} checked{{end}}><span class="symbol-label">puzzled</span></label></div>
|
||||
<div class="textarea-container"><textarea name="body" class="textarea-text textarea" maxlength="2000" placeholder="Edit your comment." data-required>{{.Comment.BodyText}}</textarea></div>
|
||||
<div class="post-form-footer-options">
|
||||
<label class="spoiler-button symbol"><input id="is_spoiler" name="is_spoiler" type="checkbox" value="1"{{if .Comment.IsSpoiler}} checked{{end}}>Spoilers</label>
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<button type="button" class="cancel-button gray-button">Cancel</button>
|
||||
<button type="submit" class="post-button black-button">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{else if .CurrentUser.ID}}
|
||||
<div id="report-violation-page" class="dialog none" data-modal-types="report report-violation" data-is-template="1">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">report chuddery/h1>
|
||||
<div class="window-body">
|
||||
<p class="description">
|
||||
you are about to report to the owner of SOP.epic on regards of breaking the rules</p>
|
||||
<form method="post" action="/comments/{{.Comment.ID}}/violations">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<p class="select-button-label">Violation Type: </p>
|
||||
<select name="type" class="cannot-report-spoiler">
|
||||
<option selected value>Please make a selection.</option>
|
||||
{{range $index, $reason := .Reasons}}
|
||||
{{if $reason.Enabled}}
|
||||
<option value="{{$index}}"{{if $reason.BodyRequired}} data-body-required="1"{{end}}>{{$reason.Name}}</option>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</select>
|
||||
<select name="type" class="can-report-spoiler">
|
||||
<option selected value>Please make a selection.</option>
|
||||
<option value="spoiler" data-body-required="1" data-track-action="Spoiler">Spoiler</option>
|
||||
{{range $index, $reason := .Reasons}}
|
||||
{{if $reason.Enabled}}
|
||||
<option value="{{$index}}"{{if $reason.BodyRequired}} data-body-required="1"{{end}}>{{$reason.Name}}</option>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</select>
|
||||
<textarea name="body" class="textarea" maxlength="100" data-placeholder="Enter a reason for the report."></textarea>
|
||||
<p class="post-id">Reply ID: #{{.Comment.ID}}</p>
|
||||
<div class="form-buttons">
|
||||
<input type="button" class="olv-modal-close-button gray-button" value="Cancel">
|
||||
<input type="submit" class="post-button black-button" value="Submit Report" data-url-id="{{.Comment.ID}}" data-track-action="openReportModal">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div id="the-post">
|
||||
{{if eq .Comment.PostType 1}}
|
||||
<div class="reply-content-memo">
|
||||
<img class="reply-memo" src="{{.Comment.BodyText}}">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="reply-content-text">{{.Comment.Body}}</div>
|
||||
{{end}}
|
||||
{{if .Comment.Image}}
|
||||
{{if eq .Comment.AttachmentType 1}}
|
||||
<div class="screenshot-container">
|
||||
<audio controls preload="none" src="{{.Comment.Image}}"></audio>
|
||||
</div>
|
||||
{{else if eq .Comment.AttachmentType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<video controls preload="none" src="{{.Comment.Image}}"></video>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="screenshot-container still-image">
|
||||
<img src="{{.Comment.Image}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .Comment.URL}}
|
||||
{{if eq .Comment.URLType 1}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe width="490" height="276" src="https://www.youtube.com/embed/{{.Comment.URL}}" frameborder="0" allowfullscreen="true"></iframe>
|
||||
</div>
|
||||
{{else if eq .Comment.URLType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe width="490" height="276" src="https://open.spotify.com/embed/track/{{.Comment.URL}}" frameborder="0"></iframe>
|
||||
</div>
|
||||
{{else if eq .Comment.URLType 3}}
|
||||
<div class="screenshot-container video audio">
|
||||
<iframe class="audio" width="490" height="276" src="https://w.soundcloud.com/player/?url={{.Comment.URL}}&auto_play=false&show_artwork=true&color=8000ff" frameborder="0" allowfullscreen="true"></iframe>
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="url-link">
|
||||
<a class="link-confirm" href="{{.Comment.URL}}" target="_blank">{{.Comment.URL}}</a>
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .URL}}
|
||||
{{if eq .URLType 1}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://www.youtube.com/embed/{{.URL}}" width="490" height="276" frameborder="0" allowfullscreen="true"></iframe>
|
||||
</div>
|
||||
{{else if eq .URLType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://open.spotify.com/embed/track/{{.URL}}" width="490" height="276" frameborder="0" allow="encrypted-media"></iframe>
|
||||
</div>
|
||||
{{else if eq .URLType 3}}
|
||||
<div class="screenshot-container video audio">
|
||||
<iframe class="audio" src="https://w.soundcloud.com/player/?url={{.URL}}&auto_play=false&show_artwork=true&color=8000ff" frameborder="0"></iframe>
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="url-link">
|
||||
<a class="link-confirm" href="{{.URL}}" target="_blank">{{.URL}}</a>
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if not .Comment.IsRMByAdmin}}
|
||||
<div class="post-meta">
|
||||
<button type="button" {{if not .Comment.CanYeah}}disabled{{end}} class="symbol submit yeah-button
|
||||
{{if .Comment.Yeahed}} yeah-added{{end}}
|
||||
" data-feeling="{{.Comment.Feeling}}" data-action="/comments/{{.Comment.ID}}/yeah" data-url-id="{{.Comment.ID}}">
|
||||
<span class="yeah-button-text">
|
||||
{{if .Comment.Yeahed}}
|
||||
{{if eq .Comment.Feeling 6}}
|
||||
Unepic
|
||||
{{else if eq .Comment.Feeling 7}}
|
||||
Unnyeah
|
||||
{{else if eq .Comment.Feeling 8}}
|
||||
Unyes
|
||||
{{else if eq .Comment.Feeling 9}}
|
||||
olv.portal.miitoo.delete
|
||||
{{else}}
|
||||
Unyeah
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Comment.Feeling 2}}
|
||||
Yeah♥
|
||||
{{else if eq .Comment.Feeling 3}}
|
||||
Yeah!?
|
||||
{{else if or (eq .Comment.Feeling 4) (eq .Comment.Feeling 5)}}
|
||||
Yeah...
|
||||
{{else if eq .Comment.Feeling 6}}
|
||||
Epic!
|
||||
{{else if eq .Comment.Feeling 7}}
|
||||
Nyeah~♥
|
||||
{{else if eq .Comment.Feeling 8}}
|
||||
Yes!
|
||||
{{else if eq .Comment.Feeling 9}}
|
||||
olv.portal.miitoo.
|
||||
{{else}}
|
||||
Yeah!
|
||||
{{end}}
|
||||
{{end}}
|
||||
</span>
|
||||
</button>
|
||||
<div class="yeah symbol"><span class="symbol-label">Yeahs</span><span class="yeah-count">{{.Comment.YeahCount}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="yeah-content"{{if not (or (.Yeahs) (.Comment.Yeahed))}} class="none"{{end}}>
|
||||
<a href="/users/{{.CurrentUser.Username}}" class="post-permalink-feeling-icon visitor"{{if not .Comment.Yeahed}} style="display: none;"{{end}}>
|
||||
{{if .CurrentUser.Role.Image}}<img src="{{.CurrentUser.Role.Image}}" class="official-tag">{{end}}
|
||||
<img src="{{.CurrentUser.Avatar}}" class="user-icon">
|
||||
</a>
|
||||
{{range $yeah := .Yeahs}}
|
||||
{{template "yeah_icon.html" $yeah}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,160 @@
|
|||
{{if .AutoPagerize}}
|
||||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}} - sop.epic</title>
|
||||
{{end}}
|
||||
<div id="main-body" class="community-top">
|
||||
<a href="https://twitter.com/MastaMiMilla/status/1591521476368556033/photo/1"> <img src="/assets/img/yaoi1.png"> </a> <img src="/assets/img/scoobydio.png"> <img src= "/assets/img/ash.gif" width="160" height="115"> <img src= "/assets/img/garycolemanlostsoul.png"> <img src= "/assets/img/monkeycar.jpg">
|
||||
<div class="main-column">
|
||||
{{if not .PopularPosts}}
|
||||
<form class="search{{if not .Query}} folded{{end}}">
|
||||
<input type="text" name="q"{{if .Query}} value="{{.Query}}"{{end}} placeholder="Search Posts" maxlength="255"{{if not .Query}} required{{end}}>
|
||||
<input type="submit" value="q" title="Search">
|
||||
</form>
|
||||
{{end}}
|
||||
<div class="post-list-outline">
|
||||
<div id="postsz">
|
||||
<div class="tab-container">
|
||||
<div class="tab2">
|
||||
<a{{if not .PopularPosts}} class="selected"{{end}} href="/communities/{{.Community.ID}}">All Posts</a>
|
||||
<a{{if .PopularPosts}} class="selected"{{end}} href="/communities/{{.Community.ID}}/hot">Popular Posts</a>
|
||||
</div>
|
||||
</div>
|
||||
{{if (and (and .CurrentUser.Username (not .PopularPosts)) (le .Community.Permissions .CurrentUser.Level))}}
|
||||
<form id="post-form" method="post" action="/communities/{{.Community.ID}}/posts"{{if not .Repost.ID}} class="folded"{{end}} data-post-subtype="default" name="test-post-default-form">
|
||||
<input type="hidden" name="community" value="{{.Community.ID}}">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
{{if .Repost.ID}}<input type="hidden" name="repost" value="{{.Repost.ID}}">
|
||||
<p class="repost-notice">You are reposting <a class="topic-title" href="/posts/{{.Repost.ID}}" target="_blank">{{.Repost.Nickname}}'s post ({{.Repost.Text}})</a> along with this submission.</p>{{end}}
|
||||
<div class="feeling-selector js-feeling-selector"><label class="symbol feeling-button feeling-button-normal checked"><input type="radio" name="feeling_id" value="0" checked><span class="symbol-label">normal</span></label><label class="symbol feeling-button feeling-button-happy"><input type="radio" name="feeling_id" value="1"><span class="symbol-label">happy</span></label><label class="symbol feeling-button feeling-button-like"><input type="radio" name="feeling_id" value="2"><span class="symbol-label">like</span></label><label class="symbol feeling-button feeling-button-surprised"><input type="radio" name="feeling_id" value="3"><span class="symbol-label">surprised</span></label><label class="symbol feeling-button feeling-button-frustrated"><input type="radio" name="feeling_id" value="4"><span class="symbol-label">frustrated</span></label><label class="symbol feeling-button feeling-button-puzzled"><input type="radio" name="feeling_id" value="5"><span class="symbol-label">puzzled</span></label></div>
|
||||
<div class="textarea-with-menu active-text">
|
||||
<menu class="textarea-menu">
|
||||
<li><label class="textarea-menu-text"><input type="radio" name="post_type" value="0"></label></li>
|
||||
<li><label class="textarea-menu-memo"><input type="radio" name="post_type" value="1"></label></li>
|
||||
<li><label class="textarea-menu-poll"><input type="radio" name="post_type" value="2"></label></li>
|
||||
<span class="character-count">2000</span>
|
||||
</menu>
|
||||
<div class="textarea-container">
|
||||
<textarea name="body" class="textarea-text textarea" maxlength="2000" placeholder="Share your thoughts in a post to {{.Community.Title}}" data-open-folded-form data-required></textarea>
|
||||
</div>
|
||||
<div class="textarea-memo none">
|
||||
<div id="memo-drawboard-page" class="none">
|
||||
<div class="window-body">
|
||||
<div class="memo-buttons">
|
||||
<button type="button" class="artwork-clear"></button>
|
||||
<button type="button" class="artwork-undo"></button>
|
||||
<button type="button" class="artwork-pencil small selected"></button>
|
||||
<button type="button" class="artwork-eraser small"></button>
|
||||
<button type="button" class="artwork-fill"></button>
|
||||
<input type="text" class="artwork-color">
|
||||
<button type="button" class="artwork-zoom"></button>
|
||||
</div>
|
||||
<div class="memo-canvas">
|
||||
<canvas id="artwork-canvas" zoom="2"></canvas>
|
||||
<canvas id="artwork-canvas-undo"></canvas>
|
||||
<canvas id="artwork-canvas-redo"></canvas>
|
||||
<input type="hidden" name="painting">
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<input class="olv-modal-close-button black-button memo-finish-btn" type="button" value="Save">
|
||||
<button type="button" class="artwork-lock none"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="textarea-poll none">
|
||||
<button type="button" class="delete none" option="option-a"></button><input type="text" class="url-form option" name="option-a" placeholder="Option A" maxlength="64" data-required>
|
||||
<button type="button" class="delete none" option="option-b"></button><input type="text" class="url-form option" name="option-b" placeholder="Option B" maxlength="64" data-required>
|
||||
<button type="button" class="add-option symbol">Add Option</button>
|
||||
</div>
|
||||
</div>
|
||||
{{if not (eq .MaxUploadSize "0")}}
|
||||
<label class="file-button-container">
|
||||
<span class="input-label">Attachment
|
||||
<span>
|
||||
Images, audio and videos are allowed.
|
||||
{{if .MaxUploadSize}}Maximum upload size: {{.MaxUploadSize}}{{end}}
|
||||
</span>
|
||||
</span>
|
||||
<span class="button file-upload-button">Upload</span>
|
||||
<input accept="image/*, audio/*, video/*" type="file" class="file-button none">
|
||||
<input type="hidden" name="image">
|
||||
<input type="hidden" name="attachment_type">
|
||||
<div class="screenshot-container still-image preview-container" style="display: none;">
|
||||
<img class="preview-image none">
|
||||
<video class="preview-video none" controls></video>
|
||||
<audio class="preview-audio none" controls></audio>
|
||||
</div>
|
||||
<script src="/assets/js/upload.js"></script>
|
||||
</label>
|
||||
{{else}}
|
||||
<input type="hidden" name="image">
|
||||
{{end}}
|
||||
<div class="post-form-footer-options">
|
||||
<div class="post-form-footer-option-inner post-form-spoiler js-post-form-spoiler test-post-form-spoiler">
|
||||
<label class="spoiler-button symbol"><input type="checkbox" id="is_spoiler" name="is_spoiler" value="1">Spoilers</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="post-form-privacy">
|
||||
<p>Who should be able to see this post?</p>
|
||||
<select class="post-form-privacy-select" name="privacy">
|
||||
<option value="0"{{if eq .CurrentUser.DefaultPrivacy 0}} selected{{end}}>Everyone</option>
|
||||
<option value="1"{{if eq .CurrentUser.DefaultPrivacy 1}} selected{{end}}>Friends, Following and Followers</option>
|
||||
<option value="2"{{if eq .CurrentUser.DefaultPrivacy 2}} selected{{end}}>Friends and Following</option>
|
||||
<option value="3"{{if eq .CurrentUser.DefaultPrivacy 3}} selected{{end}}>Friends and Followers</option>
|
||||
<option value="4"{{if eq .CurrentUser.DefaultPrivacy 4}} selected{{end}}>Friends Only</option>
|
||||
<option value="5"{{if eq .CurrentUser.DefaultPrivacy 5}} selected{{end}}>Followers and Following</option>
|
||||
<option value="6"{{if eq .CurrentUser.DefaultPrivacy 6}} selected{{end}}>Followers Only</option>
|
||||
<option value="7"{{if eq .CurrentUser.DefaultPrivacy 7}} selected{{end}}>Following Only</option>
|
||||
<option value="8"{{if eq .CurrentUser.DefaultPrivacy 8}} selected{{end}}>Admins Only</option>
|
||||
<option value="9"{{if eq .CurrentUser.DefaultPrivacy 9}} selected{{end}}>Only Me</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="black-button post-button disabled" value="Send" data-community-id="{{.Community.ID}}" data-post-content-type="text" data-post-with-screenshot="nodata" disabled>
|
||||
</div>
|
||||
</form>
|
||||
{{end}}
|
||||
<div class="body-content" id="community-post-list">
|
||||
{{if .PopularPosts}}
|
||||
<div class="pager-button">
|
||||
{{if .PrevDate}}
|
||||
<a class="button back-button symbol" href="/communities/{{.Community.ID}}/hot?date={{.PrevDate}}">
|
||||
<span class="symbol-label">←</span>
|
||||
</a>
|
||||
{{end}}
|
||||
<a class="button selected" href="/communities/{{.Community.ID}}/hot">{{.CurrentDate}}</a>
|
||||
<a class="button next-button symbol" href="/communities/{{.Community.ID}}/hot?date={{.NextDate}}">
|
||||
<span class="symbol-label">→</span>
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<div class="list post-list js-post-list" data-next-page-url="{{if .Posts}}?{{if .PopularPosts}}date={{.CurrentDate}}{{end}}{{if .Query}}&q={{.Query}}{{end}}&offset={{.Offset}}&offset_time={{.OffsetTime}}{{end}}">
|
||||
{{if .Posts}}
|
||||
{{$user_id := .CurrentUser.ID}}
|
||||
{{range $post := .Posts}}
|
||||
{{template "render_post.html" $post}}
|
||||
{{end}}
|
||||
<div class="post-list-loading" style="padding: 20px">
|
||||
<a class="black-button trigger" href="{{if .Posts}}?{{if .PopularPosts}}date={{.CurrentDate}}{{end}}{{if .Query}}&q={{.Query}}{{end}}&offset={{.Offset}}&offset_time={{.OffsetTime}}{{end}}">Load More Posts</a>
|
||||
</div>
|
||||
{{else}}
|
||||
{{if .AutoPagerize}}
|
||||
<div class="no-content no-post-content">
|
||||
<p>This community doesn't have any posts yet.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .AutoPagerize}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -0,0 +1,96 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
{{template "general_sidebar.html" .}}
|
||||
<div class="main-column{{if .IsGroupChat}} conversation{{end}}">
|
||||
{{if .IsGroupChat}}<a href="/conversations/{{.ConversationID}}/edit"><button class="button msg-update">Edit Group</button></a>{{end}}
|
||||
<form class="search{{if not .Query}} folded{{end}}">
|
||||
<input type="text" name="q"{{if .Query}} value="{{.Query}}"{{end}} placeholder="Search Messages" maxlength="255"{{if not .Query}} required{{end}}>
|
||||
<input type="submit" value="q" title="Search">
|
||||
</form>
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">{{.Title}}</h2>
|
||||
<form id="post-form" method="post" action="/messages" class="folded" data-post-subtype="default">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<input type="hidden" name="conversation" value="{{.ConversationID}}">
|
||||
<div class="feeling-selector js-feeling-selector"><label class="symbol feeling-button feeling-button-normal checked"><input type="radio" name="feeling_id" value="0" checked><span class="symbol-label">normal</span></label><label class="symbol feeling-button feeling-button-happy"><input type="radio" name="feeling_id" value="1"><span class="symbol-label">happy</span></label><label class="symbol feeling-button feeling-button-like"><input type="radio" name="feeling_id" value="2"><span class="symbol-label">like</span></label><label class="symbol feeling-button feeling-button-surprised"><input type="radio" name="feeling_id" value="3"><span class="symbol-label">surprised</span></label><label class="symbol feeling-button feeling-button-frustrated"><input type="radio" name="feeling_id" value="4"><span class="symbol-label">frustrated</span></label><label class="symbol feeling-button feeling-button-puzzled"><input type="radio" name="feeling_id" value="5"><span class="symbol-label">puzzled</span></label></div>
|
||||
<div class="textarea-with-menu active-text">
|
||||
<menu class="textarea-menu">
|
||||
<li><label class="textarea-menu-text"><input type="radio" name="post_type" value="0"></label></li>
|
||||
<li><label class="textarea-menu-memo"><input type="radio" name="post_type" value="1"></label></li>
|
||||
<span class="character-count">2000</span>
|
||||
</menu>
|
||||
<div class="textarea-container">
|
||||
<textarea name="body" class="textarea-text textarea" maxlength="2000" placeholder="Write a message here." data-open-folded-form data-required></textarea>
|
||||
</div>
|
||||
<div class="textarea-memo none">
|
||||
<div id="memo-drawboard-page" class="none">
|
||||
<div class="window-body">
|
||||
<div class="memo-buttons">
|
||||
<button type="button" class="artwork-clear"></button>
|
||||
<button type="button" class="artwork-undo"></button>
|
||||
<button type="button" class="artwork-pencil small selected"></button>
|
||||
<button type="button" class="artwork-eraser small"></button>
|
||||
<button type="button" class="artwork-fill"></button>
|
||||
<input type="text" class="artwork-color">
|
||||
<button type="button" class="artwork-zoom"></button>
|
||||
</div>
|
||||
<div class="memo-canvas">
|
||||
<canvas id="artwork-canvas" zoom="2"></canvas>
|
||||
<canvas id="artwork-canvas-undo"></canvas>
|
||||
<canvas id="artwork-canvas-redo"></canvas>
|
||||
<input type="hidden" name="painting">
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<input class="olv-modal-close-button black-button memo-finish-btn" type="button" value="Save">
|
||||
<button type="button" class="artwork-lock none"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="file-button-container">
|
||||
<span class="input-label">Attachment
|
||||
<span>
|
||||
Images, audio and videos are allowed.
|
||||
{{if .MaxUploadSize}}Maximum upload size: {{.MaxUploadSize}}{{end}}
|
||||
</span>
|
||||
</span>
|
||||
<span class="button file-upload-button">Upload</span>
|
||||
<input accept="image/*, audio/*, video/*" type="file" class="file-button none">
|
||||
<input type="hidden" name="image">
|
||||
<input type="hidden" name="attachment_type">
|
||||
<div class="screenshot-container still-image preview-container" style="display:none">
|
||||
<img class="preview-image none">
|
||||
<video class="preview-video none" controls></video>
|
||||
<audio class="preview-audio none" controls></audio>
|
||||
</div>
|
||||
<script src="/assets/js/upload.js"></script>
|
||||
</label>
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="black-button post-button disabled" value="Send" data-post-content-type="text" data-post-with-screenshot="nodata" disabled>
|
||||
</div>
|
||||
</form>
|
||||
<div class="list messages"{{if .Messages}} data-next-page-url="/{{if .IsGroupChat}}conversations/{{.ConversationID}}{{else}}messages/{{.User.Username}}{{end}}?{{if .Query}}q={{.Query}}{{end}}&offset={{.Offset}}&offset_time={{.OffsetTime}}"{{end}}>
|
||||
{{if .Messages}}
|
||||
{{$username := .CurrentUser.Username}}
|
||||
{{range $message := .Messages}}
|
||||
{{template "render_message.html" $message}}
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Offset 20}}
|
||||
<div class="no-content">
|
||||
<p>No messages.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,113 @@
|
|||
{{if eq .Offset 20}}
|
||||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
<meta property="og:description" content="{{.Sidebar.Profile.Comment}}">
|
||||
{{else}}
|
||||
<title>{{.Title}} - Indigo</title>
|
||||
{{end}}
|
||||
<div id="main-body" class="messages">
|
||||
{{template "general_sidebar.html" .}}
|
||||
<div class="main-column">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">
|
||||
{{if .Editing}}<form action="/conversations/{{.ConversationID}}/{{if .ByMe}}delete{{else}}leave{{end}}" method="post"><input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}"><button type="submit" class="button msg-update">{{if .ByMe}}Delete{{else}}Leave{{end}} Group</button></form>{{end}}
|
||||
Users Added
|
||||
</h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<div class="list follow-list">
|
||||
<ul class="list-content-with-icon-and-text arrow-list" id="members">
|
||||
{{if .Members}}
|
||||
{{range $user := .Members}}
|
||||
<li class="trigger" data-href="/users/{{$user.Username}}">
|
||||
<a href="/users/{{$user.Username}}" username="{{$user.Username}}" class="icon-container
|
||||
{{if not $user.HideOnline}}
|
||||
{{if $user.Online}}
|
||||
online
|
||||
{{else}}
|
||||
offline
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if $user.Role.Image}} official-user"><img src="{{$user.Role.Image}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{$user.Avatar}}" class="icon"></a>
|
||||
<div class="toggle-button">
|
||||
<button type="button" class="follow-button button symbol relationship-button none">Add</button>
|
||||
<button type="button" class="button follow-done-button relationship-button symbol">Remove</button>
|
||||
<input class="input" type="hidden" name="user{{$user.Level}}" value="{{$user.Username}}">
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="title">
|
||||
<span class="nick-name"><a href="/users/{{$user.Username}}"{{if $user.Color}} style="color:{{$user.Color}}"{{end}}>{{$user.Nickname}}</a></span>
|
||||
<span class="id-name">{{$user.Username}}</span>
|
||||
</p>
|
||||
<p class="text">{{$user.Comment}}</p>
|
||||
</div>
|
||||
</li>
|
||||
{{end}}
|
||||
<div class="no-content none">
|
||||
<p>You haven't added any users to your group yet.</p>
|
||||
</div>
|
||||
{{else}}
|
||||
{{if eq .Offset 20}}
|
||||
<div class="no-content">
|
||||
<p>You haven't added any users to your group yet.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="black-button post-button disabled" value="{{if .Editing}}Edit{{else}}Create{{end}} Group" disabled>
|
||||
</div>
|
||||
</form>
|
||||
<h2 class="label">Add Friends</h2>
|
||||
<div class="list follow-list">
|
||||
{{end}}
|
||||
<ul class="list-content-with-icon-and-text arrow-list" id="friends" data-next-page-url="{{if .Users}}?&offset={{.Offset}}{{end}}">
|
||||
{{if .Friends}}
|
||||
{{range $user := .Friends}}
|
||||
<li class="trigger" data-href="/users/{{$user.Username}}">
|
||||
<a href="/users/{{$user.Username}}" class="icon-container
|
||||
{{if not $user.HideOnline}}
|
||||
{{if $user.Online}}
|
||||
online
|
||||
{{else}}
|
||||
offline
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if $user.Role.Image}} official-user"><img src="{{$user.Role.Image}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{$user.Avatar}}" class="icon"></a>
|
||||
<div class="toggle-button">
|
||||
<button type="button" class="follow-button button symbol relationship-button">Add</button>
|
||||
<button type="button" class="button follow-done-button relationship-button symbol none">Remove</button>
|
||||
<input class="input" type="hidden" value="{{$user.Username}}">
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="title">
|
||||
<span class="nick-name"><a href="/users/{{$user.Username}}"{{if $user.Color}} style="color:{{$user.Color}}"{{end}}>{{$user.Nickname}}</a></span>
|
||||
<span class="id-name">{{$user.Username}}</span>
|
||||
</p>
|
||||
<p class="text">{{$user.Comment}}</p>
|
||||
</div>
|
||||
</li>
|
||||
{{end}}
|
||||
<div class="no-content none">
|
||||
<p>You've added every friend you can to the group.</p>
|
||||
</div>
|
||||
{{else}}
|
||||
{{if eq .Offset 20}}
|
||||
<div class="no-content">
|
||||
<p>{{if .FriendCount}}You've added every friend you can to the group.{{else}}You don't have any friends. Why don't you make some?{{end}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</ul>
|
||||
{{if eq .Offset 20}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -0,0 +1,102 @@
|
|||
<li id="{{.Comment.ID}}" data-href{{if and .IsSpoiler (not .ByMii)}}-hidden{{end}}="/comments/{{.Comment.ID}}" class="post {{if .ByMe}}my{{else}}other{{end}} trigger{{if and .IsSpoiler (not .ByMii)}} hidden{{end}}">
|
||||
<a href="/users/{{.Comment.CommenterUsername}}" username="{{.Comment.CommenterUsername}}" class="icon-container{{if not .Comment.CommenterHideOnline}} online{{end}}{{if .Comment.CommenterRoleImage}} official-user"><img src="{{.Comment.CommenterRoleImage}}" class="official-tag">{{else}}">{{end}}<img src="{{.Comment.CommenterIcon}}" class="icon"></a>
|
||||
<div class="body">
|
||||
<div class="header">
|
||||
<p class="user-name"><a href="/users/{{.Comment.CommenterUsername}}"{{if .Comment.CommenterColor}} style="color:{{.Comment.CommenterColor}}"{{end}}>{{.Comment.CommenterNickname}}</a></p>
|
||||
<p class="timestamp-container">
|
||||
<a class="timestamp" href="/comments/{{.Comment.ID}}">
|
||||
<span class="update" time="{{.Comment.CreatedAtUnix}}000">{{.Comment.CreatedAt}}</span>
|
||||
{{if .Comment.EditedAt}}
|
||||
(Edited <span class="update" time="{{.Comment.EditedAtUnix}}000">{{.Comment.EditedAt}}</span>)
|
||||
{{end}}
|
||||
</a>
|
||||
<span class="spoiler-status{{if .Comment.IsSpoiler}} spoiler{{end}}"> · Spoilers</span>
|
||||
</p>
|
||||
</div>
|
||||
{{if eq .Comment.PostType 1}}
|
||||
<div class="reply-content-memo">
|
||||
<img class="reply-memo" src="{{.Comment.BodyText}}">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="reply-content-text">{{.Comment.Body}}</div>
|
||||
{{end}}
|
||||
{{if .Comment.Image}}
|
||||
{{if eq .Comment.AttachmentType 1}}
|
||||
<div class="screenshot-container">
|
||||
<audio controls preload="none" src="{{.Comment.Image}}"></audio>
|
||||
</div>
|
||||
{{else if eq .Comment.AttachmentType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<video controls preload="none" src="{{.Comment.Image}}"></video>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="screenshot-container still-image">
|
||||
<img src="{{.Comment.Image}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .Comment.URL}}
|
||||
{{if eq .Comment.URLType 1}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://www.youtube.com/embed/{{.Comment.URL}}" width="490" height="276" frameborder="0" allowfullscreen="true"></iframe>
|
||||
</div>
|
||||
{{else if eq .Comment.URLType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://open.spotify.com/embed/track/{{.Comment.URL}}" width="490" height="276" frameborder="0" allow="encrypted-media"></iframe>
|
||||
</div>
|
||||
{{else if eq .Comment.URLType 3}}
|
||||
<div class="screenshot-container video audio">
|
||||
<iframe class="audio" src="https://w.soundcloud.com/player/?url={{.Comment.URL}}&auto_play=false&show_artwork=true&color=8000ff" frameborder="0"></iframe>
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="url-link">
|
||||
<a class="link-confirm" href="{{.Comment.URL}}" target="_blank">{{.Comment.URL}}</a>
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if and .Comment.IsSpoiler (not .Comment.ByMii)}}
|
||||
<div class="hidden-content">
|
||||
<p>This comment contains spoilers.</p>
|
||||
<button type="button" class="hidden-content-button">View Post</button>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="reply-meta">
|
||||
<button type="button" {{if not .CanYeah}} disabled{{end}} class="symbol submit yeah-button
|
||||
{{if not .CanYeah}} disabled{{end}}
|
||||
" data-is-in-reply-list="1" data-feeling="{{.Comment.Feeling}}" data-action="/comments/{{.Comment.ID}}/yeah" data-url-id="{{.Comment.ID}}"><span class="yeah-button-text">
|
||||
{{if .Comment.Yeahed}}
|
||||
{{if eq .Comment.Feeling 6}}
|
||||
Unepic
|
||||
{{else if eq .Comment.Feeling 7}}
|
||||
Unnyeah
|
||||
{{else if eq .Comment.Feeling 8}}
|
||||
Unyes
|
||||
{{else if eq .Comment.Feeling 9}}
|
||||
olv.portal.miitoo.delete
|
||||
{{else}}
|
||||
Unyeah
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Comment.Feeling 2}}
|
||||
Yeah♥
|
||||
{{else if eq .Comment.Feeling 3}}
|
||||
Yeah!?
|
||||
{{else if or (eq .Comment.Feeling 4) (eq .Comment.Feeling 5)}}
|
||||
Yeah...
|
||||
{{else if eq .Comment.Feeling 6}}
|
||||
Epic!
|
||||
{{else if eq .Comment.Feeling 7}}
|
||||
Nyeah~♥
|
||||
{{else if eq .Comment.Feeling 8}}
|
||||
Yes!
|
||||
{{else if eq .Comment.Feeling 9}}
|
||||
olv.portal.miitoo.
|
||||
{{else}}
|
||||
Yeah!
|
||||
{{end}}
|
||||
{{end}}
|
||||
</span></button>
|
||||
<div class="yeah symbol"><span class="symbol-label">Yeahs</span><span class="yeah-count">0</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
|
@ -0,0 +1,13 @@
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-inner">
|
||||
<div class="link-container">
|
||||
<p><a href="https://archive.org/details/doom1-sw1">play doom in your browser!</a></p>
|
||||
<p><a href="https://www.dell.com/support/incidents-online/en-us/contactus/dynamic">Contact Us</a></p>
|
||||
<p>"To protect the world from devastation!"</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
<div id="sidebar" class="user-sidebar">
|
||||
{{if .CurrentUser.Username}}
|
||||
<div class="sidebar-container">
|
||||
<div id="sidebar-profile-body">
|
||||
<div username="{{.CurrentUser.Username}}" class="icon-container
|
||||
{{if not .CurrentUser.HideOnline}}
|
||||
{{if .CurrentUser.Online}}
|
||||
online
|
||||
{{else}}
|
||||
offline
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .CurrentUser.Role.Image}} official-user"><img src="{{.CurrentUser.Role.Image}}" class="official-tag">{{else}}">{{end}}
|
||||
<a href="/users/{{.CurrentUser.Username}}"><img src="{{.CurrentUser.Avatar}}" alt="{{.CurrentUser.Username}}" class="icon"></a>
|
||||
</div>
|
||||
{{if .CurrentUser.Role.Organization}}<p class="user-organization">{{.CurrentUser.Role.Organization}}</p>{{end}}
|
||||
<a href="/users/{{.CurrentUser.Username}}" class="nick-name"{{if .CurrentUser.Color}} style="color:{{.CurrentUser.Color}}"{{end}}>{{.CurrentUser.Nickname}}</a>
|
||||
<p class="id-name">{{.CurrentUser.Username}}</p>
|
||||
</div>
|
||||
<button class="button" onclick="Olv.Closed.lights()">Toggle Dark Mode</button>
|
||||
<ul id="sidebar-profile-status">
|
||||
<li><a href="/users/{{.CurrentUser.Username}}/friends"><span><span class="number test-friend-count">{{.FriendCount}}</span>Friends</span></a></li>
|
||||
<li><a href="/users/{{.CurrentUser.Username}}/following"><span><span class="number test-following-count">{{.FollowingCount}}</span>Following</span></a></li>
|
||||
<li><a href="/users/{{.CurrentUser.Username}}/followers"><span><span class="number test-follower-count">{{.FollowerCount}}</span>Followers</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="sidebar-container sidebar-setting">
|
||||
<ul>
|
||||
<li><a href="/help/rules" class="sidebar-menu-info symbol"><span>rules</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,88 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" class="os-mac">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>sop.epic</title>
|
||||
<meta http-equiv="content-style-type" content="text/css">
|
||||
<meta http-equiv="content-script-type" content="text/javascript">
|
||||
<meta http-equiv="content-security-policy" content="block-all-mixed-content">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-title" content="sop.epic">
|
||||
<meta name="description" content="SOP.epic is a small scale feature pakced Twitter alternative with no paywalls">
|
||||
<meta property="og:locale" content="en_US">
|
||||
<meta property="og:title" content="{{.Title}}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:image" content="/assets/img/favicon.png">
|
||||
<meta property="og:site_name" content="sop.epic">
|
||||
<meta property="article:published_time" content="None">
|
||||
<link rel="shortcut icon" type="image/png" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/css/offdevice.css">
|
||||
{{if .CurrentUser.Theme}}
|
||||
<style id="theme">
|
||||
:root {
|
||||
--theme: {{index .CurrentUser.ThemeColors 0}};
|
||||
--theme-light: {{index .CurrentUser.ThemeColors 1}};
|
||||
--theme-dark: {{index .CurrentUser.ThemeColors 2}};
|
||||
--theme-darker: {{index .CurrentUser.ThemeColors 3}};
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="/assets/css/color.css">
|
||||
{{end}}
|
||||
<link id="darkness" {{if .CurrentUser.LightMode}}disabled{{end}} rel="stylesheet" type="text/css" href="/assets/css/dark.css">
|
||||
<script src="/assets/js/jslibs.js"></script>
|
||||
<script src="/assets/js/indigo.js"></script>
|
||||
{{if .CurrentUser.WebsocketsEnabled}}
|
||||
<script id="websockets" src="/assets/js/websocket.js"></script>
|
||||
{{else}}
|
||||
<script>var websocketsEnabled = false;</script>
|
||||
{{end}}
|
||||
</head>
|
||||
<body class="{{if not .CurrentUser.Username}}guest"{{else}}" sess-usern="{{.CurrentUser.Username}}"{{end}} csrf-token="{{.CurrentUser.CSRFToken}}">
|
||||
<div id="wrapper" class="{{if not .CurrentUser.Username}}guest{{end}}">
|
||||
<div id="sub-body">
|
||||
<menu id="global-menu">
|
||||
<li id="global-menu-logo"><h1><a href="/"><img src="/assets/img/menu-logo.png" alt="Indigo"></a></h1></li>
|
||||
{{if not .CurrentUser.Username}}
|
||||
<li id="global-menu-login">
|
||||
<a href="/login" class="login">
|
||||
<input type="image" alt="Sign in" src="/assets/img/sign-in.png">
|
||||
</a>
|
||||
</li>
|
||||
{{else}}
|
||||
<li id="global-menu-list">
|
||||
<ul>
|
||||
<li id="global-menu-mymenu">
|
||||
<a href="/users/{{.CurrentUser.Username}}">
|
||||
<span class="icon-container">
|
||||
<img src="{{.CurrentUser.Avatar}}" alt="User Page"{{if .CurrentUser.Role.Image}} class="official-user"><img src="{{.CurrentUser.Role.Image}}" class="official-tag">{{else}}>{{end}}
|
||||
</span>
|
||||
<span>my profile</span>
|
||||
</a>
|
||||
</li>
|
||||
<li id="global-menu-feed"><a href="/activity"><span>timeline!</span></a></li>
|
||||
<li id="global-menu-community"><a href="/communities/1"><span>plaza!</span></a></li>
|
||||
<li id="global-menu-message"><a href="/messages"><span>messages!</span><span class="badge"{{if not .CurrentUser.Notifications.Messages}} style="display: none;"{{end}}>{{.CurrentUser.Notifications.Messages}}</span></a></li>
|
||||
<li id="global-menu-news"><a href="/notifications"><span class="badge"{{if not .CurrentUser.Notifications.Notifications}} style="display: none;"{{end}}>{{.CurrentUser.Notifications.Notifications}}</span><span>notifications!</span></a></li>
|
||||
<li id="global-menu-my-menu"><button class="symbol js-open-global-my-menu open-global-my-menu"></button>
|
||||
<menu id="global-my-menu" class="invisible none">
|
||||
<li><a href="/settings/profile" class="symbol my-menu-profile-setting"><span>profile settings</span></a></li>
|
||||
<li><a href="#" class="symbol my-menu-account-setting"><span>account settings</span></a></li>
|
||||
<li><a href="/help/rules" class="symbol my-menu-guide"><span>SOP rules</span></a></li>
|
||||
<li><a href="/communities/2" class="symbol my-menu-guide"><span>piracy cabal network</span></a></li>
|
||||
<li><a href="/blocked" class="symbol my-menu-block"><span>blocklist</span></a></li>
|
||||
{{if gt .CurrentUser.Level 0}}<li><a href="/admin" class="symbol my-menu-info"><span>$10 mode</span></a></li>{{end}}
|
||||
<li>
|
||||
<form action="/logout" method="post" id="my-menu-logout" class="symbol">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<input type="submit" value="Log out">
|
||||
</form>
|
||||
</li>
|
||||
</menu>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{{end}}
|
||||
</menu>
|
||||
</div>
|
||||
<div id="container">
|
|
@ -0,0 +1,8 @@
|
|||
<div class="post-poll{{if .Votes}} has-votes{{end}}" data-action="/posts/{{.ID}}/vote">
|
||||
<div class="poll-options">
|
||||
<a class="poll-votes">{{.Votes}} vote{{if not (eq .Votes 1.0)}}s{{end}}</a>
|
||||
{{range $option := .Options}}
|
||||
<a class="poll-option{{if $option.Selected}} selected{{end}}" option-id="{{$option.ID}}" votes="{{$option.Votes}}"><div class="poll-background" style="width:{{$option.Percentage}}%"></div><span class="option-name" original="{{$option.Name}}">{{$option.Name}}</span><span class="percentage">{{$option.Percentage}}%<span class="vote-count"> ({{$option.Votes}} vote{{if not (eq .Votes 1.0)}}s{{end}})</span></span></a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,335 @@
|
|||
{{if and (.User.Theme) (not (eq .ProfileOnPage "settings"))}}
|
||||
<style id="theme">
|
||||
:root {
|
||||
--theme: {{index .User.ThemeColors 0}};
|
||||
--theme-light: {{index .User.ThemeColors 1}};
|
||||
--theme-dark: {{index .User.ThemeColors 2}};
|
||||
--theme-darker: {{index .User.ThemeColors 3}};
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="/assets/css/color.css">
|
||||
{{end}}
|
||||
<div id="sidebar" class="user-sidebar{{if eq .ProfileOnPage "main"}} profile-top{{end}}">
|
||||
<div class="sidebar-container">
|
||||
{{if .Profile.FavoritePostID}}
|
||||
<a href="/posts/{{.Profile.FavoritePostID}}" id="sidebar-cover" style="background-image:url({{.Profile.FavoritePostImage}})">
|
||||
<img src="{{.Profile.FavoritePostImage}}" class="sidebar-cover-image">
|
||||
</a>
|
||||
{{end}}
|
||||
<div id="sidebar-profile-body"{{if .Profile.FavoritePostID}} class="with-profile-post-image"{{end}}>
|
||||
<div username="{{.User.Username}}" class="icon-container
|
||||
{{if not .User.HideOnline}}
|
||||
{{if .User.Online}}
|
||||
online
|
||||
{{else}}
|
||||
offline
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .User.Role.Image}}official-user"><img src="{{.User.Role.Image}}" class="official-tag">{{else}}">{{end}}
|
||||
<a href="/users/{{.User.Username}}"><img src="{{.User.Avatar}}" alt="{{.User.Username}}" class="icon"></a>
|
||||
</div>
|
||||
{{if .User.Role.Organization}}<p class="user-organization">{{.User.Role.Organization}}</p>{{end}}
|
||||
<a href="/users/{{.User.Username}}" class="nick-name"{{if .User.Color}} style="color:{{.User.Color}}"{{end}}>{{.User.Nickname}}</a>
|
||||
<p class="id-name">{{.User.Username}}</p>
|
||||
</div>
|
||||
{{if (and (not (eq .User.ID .CurrentUser.ID)) (.CurrentUser.Username))}}
|
||||
<div class="user-action-content">
|
||||
<div class="toggle-button">
|
||||
<button type="button" data-action="/users/{{.User.Username}}/follow" class="follow-button button symbol{{if .IsFollowing}} none{{end}}">Follow</button>
|
||||
<button type="button" data-action="/users/{{.User.Username}}/unfollow" class="unfollow-button button symbol{{if not .IsFollowing}} none{{end}}" data-screen-name="{{.User.Nickname}}">Follow</button>
|
||||
{{if and (eq .FriendStatus 0) (or (eq .Profile.AllowFriend 0) (and (eq .Profile.AllowFriend 1) .IsFollowingMe))}}<button type="button" data-action="/users/{{.User.Username}}/friend_new" class="friend-button create button symbol">Send friend request</button>{{end}}
|
||||
{{if eq .FriendStatus 1}}<button type="button" data-action="/users/{{.User.Username}}/friend_accept" data-screen-name="{{.User.Nickname}}" data-time="{{.RequestTime}}" data-msg="q" class="friend-button accept button symbol">Become friends</button>{{end}}
|
||||
{{if eq .FriendStatus 2}}<button type="button" data-action="/users/{{.User.Username}}/friend_cancel" data-screen-name="{{.User.Nickname}}" class="friend-button unf cancel button symbol">Cancel friend request</button>{{end}}
|
||||
{{if eq .FriendStatus 3}}<button type="button" data-action="/users/{{.User.Username}}/friend_delete" data-screen-name="{{.User.Nickname}}" class="friend-button unf delete button symbol">Friends</button>{{end}}
|
||||
{{if le .User.Level 0}}
|
||||
<div class="report-buttons-content">
|
||||
<button type="button" class="report-button report-user" data-track-label="user" data-track-action="openReportModal" data-track-category="reportViolator" data-modal-open="#report-violator-page">Report Violation</button>
|
||||
<button type="button" class="report-button block-button {{if .User.Blocked}}unblock">Unblock{{else}}block">Block{{end}}</button>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if eq .FriendStatus 0}}
|
||||
<div class="dialog none" data-modal-types="post-friend-request">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">Send friend request to {{.User.Nickname}}</h1>
|
||||
<div class="window-body">
|
||||
<p class="description">
|
||||
Friend Request: <img width="36px" height="36px" src="{{.User.Avatar}}">{{.User.Nickname}}
|
||||
</p>
|
||||
<form method="post">
|
||||
<textarea name="body" class="textarea" maxlength="2000" data-placeholder="Write a friend request here." placeholder="Write a friend request here."></textarea>
|
||||
<div class="form-buttons">
|
||||
<input type="button" class="olv-modal-close-button gray-button" value="Cancel">
|
||||
<input type="submit" value="Send" class="post-button black-button">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else if eq .FriendStatus 1}}
|
||||
<div class="dialog none" data-modal-types="accept-friend-request" data-screen-name="{{.User.Nickname}}" data-reject-action="/users/{{.User.Username}}/friend_reject" data-action="/users/{{.User.Username}}/friend_accept" uuid="{{.Request.ID}}">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">Friend Request from {{.User.Nickname}} at {{.Request.CreatedAt}}</h1>
|
||||
<div class="window-body">
|
||||
<div id="sidebar-profile-body">
|
||||
<div username="{{.User.Username}}" class="icon-container {{if not .User.HideOnline}}{{if .User.Online}}online{{else}}offline{{end}}{{end}}{{if .User.Role.Image}} official-user"><img src="{{.User.RoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<a href="/users/{{.User.Username}}"><img src="{{.User.Avatar}}" alt="{{.User.Nickname}}" class="icon"></a>
|
||||
</div>
|
||||
{{if .User.Role.Organization}}<p class="user-organization">{{.User.Role.Organization}}</p>{{end}}
|
||||
<a href="/users/{{.User.Username}}" class="nick-name"{{if .User.Color}} style="color:{{.User.Color}}"{{end}}>{{.User.Nickname}}</a>
|
||||
<p class="id-name">{{.User.Username}}</p>
|
||||
</div>
|
||||
{{if .Request.Message}}<pre>{{.Request.Message}}</pre>{{end}}
|
||||
<p class="window-body-content">Accept {{.User.Nickname}}'s friend request?</p>
|
||||
<div class="form-buttons three">
|
||||
<button class="olv-modal-close-button gray-button" data-event-type="cancel" type="button">Cancel</button>
|
||||
<button class="cancel-button gray-button" type="button">Reject</button>
|
||||
<button class="ok-button post-button black-button" data-event-type="ok" type="button">Accept</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="report-violator-page" class="dialog none" data-modal-types="report report-violator" data-is-template="1">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">Report this User to Indigo Administration</h1>
|
||||
<div class="window-body">
|
||||
<form method="post" action="/users/{{.User.Username}}/violators">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<p class="description">You are about to report a user for violating the Indigo Rules. This report will be sent to the Indigo administration and not to the user you are reporting.</p>
|
||||
<div class="select-content">
|
||||
<span class="select-button-label">Violation Type: </span>
|
||||
<div class="select-button test-report-type-button">
|
||||
<select name="type">
|
||||
<option selected value>Make a selection.</option>
|
||||
{{range $index, $reason := .Reasons}}
|
||||
{{if $reason.Enabled}}
|
||||
<option value="{{$index}}"{{if $reason.BodyRequired}} data-body-required="1"{{end}}>{{$reason.Name}}</option>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<textarea name="body" class="textarea" maxlength="100" placeholder="Enter a reason for the report here."></textarea>
|
||||
<p class="violator-id">Username: {{.User.Username}}</p>
|
||||
<div class="form-buttons">
|
||||
<input type="button" class="olv-modal-close-button gray-button" value="Cancel">
|
||||
<input type="submit" class="post-button black-button test-report-submit" value="Submit Report" data-community-id data-url-id data-track-label="user" data-title-id data-track-action="openReportModal" data-track-category="reportViolator">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if not .User.Blocked}}
|
||||
<div class="dialog none" data-modal-types="post-block">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">Block {{.User.Nickname}}</h1>
|
||||
<div class="window-body">
|
||||
<div id="sidebar-profile-body">
|
||||
<div username="{{.User.Username}}" class="icon-container{{if not .User.HideOnline}}{{if .User.Online}} online{{else}} offline{{end}}{{end}}
|
||||
{{if .User.Role.Image}} official-user"><img src="{{.User.Role.Image}}" class="official-tag">{{else}}">{{end}}
|
||||
<a href="/users/{{.User.Username}}">
|
||||
<img src="{{.User.Avatar}}" alt="{{.User.Nickname}}" class="icon">
|
||||
</a>
|
||||
</div>
|
||||
<a href="/users/{{.User.Username}}" class="nick-name">{{.User.Nickname}}</a>
|
||||
<p class="id-name">{{.User.Username}}</p>
|
||||
</div>
|
||||
<form method="post" data-action="/users/{{.User.Username}}/block">
|
||||
<p class="window-body-content">Really block this user? You won't be able to see each other's posts or profile.</p>
|
||||
<div class="form-buttons">
|
||||
<input type="button" class="olv-modal-close-button gray-button" value="No">
|
||||
<input type="submit" value="Yes" class="post-button black-button">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="dialog none" data-modal-types="post-unblock">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">Unblock {{.User.Nickname}}</h1>
|
||||
<div class="window-body">
|
||||
<div id="sidebar-profile-body">
|
||||
<div username="{{.User.Username}}" class="icon-container{{if .User.Online}} online{{else}} offline{{end}}
|
||||
{{if .User.Role.Image}} official-user"><img src="{{.User.Role.Image}}" class="official-tag">{{else}}">{{end}}
|
||||
<a href="/users/{{.User.Username}}">
|
||||
<img src="{{.User.Avatar}}" alt="{{.User.Nickname}}" class="icon">
|
||||
</a>
|
||||
</div>
|
||||
<a href="/users/{{.User.Username}}" class="nick-name"{{if .User.Color}} style="color:{{.User.Color}}"{{end}}>{{.User.Nickname}}</a>
|
||||
<p class="id-name">{{.User.Username}}</p>
|
||||
</div>
|
||||
<form method="post" data-action="/users/{{.User.Username}}/unblock">
|
||||
<p class="window-body-content">Unblock this user?</p>
|
||||
<div class="form-buttons">
|
||||
<input type="button" class="olv-modal-close-button gray-button" value="No">
|
||||
<input type="submit" value="Yes" class="post-button black-button">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else if .CurrentUser.Username}}
|
||||
<div id="edit-profile-settings"><a class="button symbol" href="/settings/profile">Profile Settings</a></div>
|
||||
<button class="button" onclick="Olv.Closed.lights()">Toggle dark mode</button>
|
||||
{{end}}
|
||||
<ul id="sidebar-profile-status">
|
||||
<li><a href="/users/{{.User.Username}}/friends" class="
|
||||
{{if .ProfileOnPage}}
|
||||
{{if eq .ProfileOnPage "friends"}}selected{{end}}
|
||||
{{end}}
|
||||
"><span><span class="number test-following-count">{{$.Profile.FriendCount}}</span>Friends</span></a></li>
|
||||
<li><a href="/users/{{.User.Username}}/following" class="
|
||||
{{if .ProfileOnPage}}
|
||||
{{if eq .ProfileOnPage "following"}}selected{{end}}
|
||||
{{end}}
|
||||
"><span><span class="number test-following-count">{{$.Profile.FollowingCount}}</span>Following</span></a></li>
|
||||
<li><a href="/users/{{.User.Username}}/followers" class="
|
||||
{{if .ProfileOnPage}}
|
||||
{{if eq .ProfileOnPage "followers"}}selected{{end}}
|
||||
{{end}}
|
||||
"><span><span class="number test-follower-count">{{$.Profile.FollowerCount}}</span>Followers</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sidebar-setting sidebar-container">
|
||||
<div class="sidebar-post-menu">
|
||||
<a href="/users/{{.User.Username}}/posts" class="sidebar-menu-post with-count symbol{{if .ProfileOnPage}}{{if eq .ProfileOnPage "posts"}} selected{{end}}{{end}}"><span>All posts</span><span class="post-count"><span class="test-post-count">{{$.Profile.PostCount}}</span></a>
|
||||
<a href="/users/{{.User.Username}}/comments" class="sidebar-menu-replies with-count symbol{{if .ProfileOnPage}}{{if eq .ProfileOnPage "comments"}} selected{{end}}{{end}}"><span>All comments</span><span class="post-count"><span class="test-reply-count">{{$.Profile.CommentCount}}</span></a>
|
||||
<a href="/users/{{.User.Username}}/yeahs" class="sidebar-menu-empathies with-count symbol{{if .ProfileOnPage}}{{if eq .ProfileOnPage "yeahs"}} selected{{end}}{{end}}"><span>Yeahs given</span><span class="post-count"><span class="test-empathy-count">{{$.Profile.YeahCount}}</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-container sidebar-profile">
|
||||
{{if .Profile.Comment}}
|
||||
<div class="profile-comment">
|
||||
<div class="js-truncated-text">
|
||||
{{.Profile.Comment}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="user-data">
|
||||
{{if .Profile.Region}}
|
||||
<div class="data-content">
|
||||
<h4><span>Region</span></h4>
|
||||
<div class="note">
|
||||
<span>{{.Profile.Region}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Profile.NNID}}
|
||||
{{if (or (eq .Profile.NNIDVisibility 0) (or (or (and (eq .Profile.NNIDVisibility 1) (eq .FriendStatus 3)) (and (eq .Profile.NNIDVisibility 1) (eq .Profile.User .CurrentUser.ID))) (and (eq .Profile.NNIDVisibility 2) (eq .Profile.User .CurrentUser.ID))))}}
|
||||
<div class="data-content">
|
||||
<h4><span>NNID</span></h4>
|
||||
<div class="note">
|
||||
<span>{{.Profile.NNID}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .Profile.Twitter}}
|
||||
<div class="data-content">
|
||||
<h4><span>Twitter</span></h4>
|
||||
<div class="note">
|
||||
<span><a href="https://twitter.com/{{.Profile.Twitter}}" target="_blank">@{{.Profile.Twitter}}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Profile.Discord}}
|
||||
<div class="data-content">
|
||||
<h4><span>DiscordTag</span></h4>
|
||||
<div class="note">
|
||||
<span>{{.Profile.Discord}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Profile.SwitchCode}}
|
||||
<div class="data-content">
|
||||
<h4><span>Switch FC</span></h4>
|
||||
<div class="note">
|
||||
<span>{{.Profile.SwitchCode}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Profile.PSN}}
|
||||
<div class="data-content">
|
||||
<h4><span>PlayStation Network</span></h4>
|
||||
<div class="note">
|
||||
<span>{{.Profile.PSN}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Profile.YouTube}}
|
||||
<div class="data-content">
|
||||
<h4><span>URL</span></h4>
|
||||
<div class="note">
|
||||
<span><a href="{{.Profile.YouTube}}" target="_blank">{{.Profile.YouTube}}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Profile.Steam}}
|
||||
<div class="data-content">
|
||||
<h4><span>Steam</span></h4>
|
||||
<div class="note">
|
||||
<span>{{.Profile.Steam}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="data-content">
|
||||
<h4><span>User ID</span></h4>
|
||||
<div class="note">
|
||||
<span>#{{.User.ID}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-content">
|
||||
<h4><span>Joined At</span></h4>
|
||||
<div class="note">
|
||||
<span class="update" time="{{.Profile.CreatedAtUnix}}000">{{.Profile.CreatedAt}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{if not .User.HideLastSeen}}
|
||||
<div class="data-content">
|
||||
<h4><span>Last seen</span></h4>
|
||||
<div class="note">
|
||||
<span class="update" time="{{.User.LastSeenUnix}}000">{{.User.LastSeen}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Profile.Gender}}
|
||||
<div class="data-content">
|
||||
<h4><span>Gender</span></h4>
|
||||
<div class="note">
|
||||
<span>{{.Profile.Gender}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{if .FavoriteCommunities}}
|
||||
<div class="sidebar-container sidebar-favorite-community">
|
||||
<h4><a href="/users/{{.User.Username}}/favorites" class="favorite-community-button symbol"><span>Favorite Communities</span></a></h4>
|
||||
<ul>
|
||||
{{range $community := .FavoriteCommunities}}
|
||||
<li class="favorite-community">
|
||||
<a href="/communities/{{$community.ID}}">
|
||||
<span class="icon-container">
|
||||
<img class="icon" src="{{$community.Icon}}">
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
|
@ -0,0 +1,126 @@
|
|||
<li id="{{.ID}}"{{if not .IsRMByAdmin}} data-href{{if and .IsSpoiler (not .ByMii)}}-hidden{{end}}="/comments/{{.ID}}"{{end}} class="post {{if .ByMe}}my{{else}}other{{end}}{{if not .IsRMByAdmin}} trigger{{end}}{{if and (or .IsSpoiler .IsRMByAdmin) (not .ByMii)}} hidden{{end}}">
|
||||
<a href="/users/{{.CommenterUsername}}" username="{{.CommenterUsername}}" class="icon-container
|
||||
{{if not .CommenterHideOnline}}
|
||||
{{if .CommenterOnline}}
|
||||
online
|
||||
{{else}}
|
||||
offline
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .CommenterRoleImage}} official-user"><img src="{{.CommenterRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{.CommenterIcon}}" class="icon">
|
||||
</a>
|
||||
<div class="body">
|
||||
<div class="header">
|
||||
<p class="user-name"><a href="/users/{{.CommenterUsername}}"{{if .CommenterColor}} style="color:{{.CommenterColor}}"{{end}}>{{.CommenterNickname}}</a></p>
|
||||
<p class="timestamp-container">
|
||||
<a class="timestamp"{{if not .IsRMByAdmin}} href="/comments/{{.ID}}"{{end}}>
|
||||
<span class="update" time="{{.CreatedAtUnix}}000">{{.CreatedAt}}</span>
|
||||
{{if .EditedAt}}
|
||||
(Edited <span class="update" time="{{.EditedAtUnix}}000">{{.EditedAt}}</span>)
|
||||
{{end}}
|
||||
</a>
|
||||
<span class="spoiler-status{{if .Pinned}} spoiler{{end}}"> · Pinned</span>
|
||||
<span class="spoiler-status{{if .IsSpoiler}} spoiler{{end}}"> · Spoilers</span>
|
||||
</p>
|
||||
</div>
|
||||
{{if .IsRMByAdmin}}
|
||||
<p class="deleted-message">
|
||||
Deleted by administrator.<br>
|
||||
Comment ID: #{{.ID}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{if or (not .IsRMByAdmin) .ByMii}}
|
||||
{{if eq .PostType 1}}
|
||||
<div class="reply-content-memo">
|
||||
<img class="reply-memo" src="{{.BodyText}}">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="reply-content-text">{{.Body}}</div>
|
||||
{{end}}
|
||||
{{if .Image}}
|
||||
{{if eq .AttachmentType 1}}
|
||||
<a class="screenshot-container">
|
||||
<audio controls preload="none" src="{{.Image}}"></audio>
|
||||
</a>
|
||||
{{else if eq .AttachmentType 2}}
|
||||
<div class="screenshot-container still-image">
|
||||
<video controls preload="none" src="{{.Image}}"></video>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="screenshot-container still-image">
|
||||
<img src="{{.Image}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .URL}}
|
||||
{{if eq .URLType 1}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://www.youtube.com/embed/{{.URL}}" width="490" height="276" frameborder="0" allowfullscreen="true"></iframe>
|
||||
</div>
|
||||
{{else if eq .URLType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://open.spotify.com/embed/track/{{.URL}}" width="490" height="276" frameborder="0" allow="encrypted-media"></iframe>
|
||||
</div>
|
||||
{{else if eq .URLType 3}}
|
||||
<div class="screenshot-container video audio">
|
||||
<iframe class="audio" src="https://w.soundcloud.com/player/?url={{.URL}}&auto_play=false&show_artwork=true&color=8000ff" frameborder="0"></iframe>
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="url-link">
|
||||
<a class="link-confirm" href="{{.URL}}" target="_blank">{{.URL}}</a>
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if and .IsSpoiler (not .ByMii)}}
|
||||
<div class="hidden-content">
|
||||
<p>This comment contains spoilers.</p>
|
||||
<button type="button" class="hidden-content-button">View Comment</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if not .IsRMByAdmin}}
|
||||
<div class="reply-meta">
|
||||
<button type="button" {{if not .CanYeah}} disabled{{end}} class="symbol submit yeah-button
|
||||
{{if .Yeahed}} yeah-added{{end}}
|
||||
{{if not .CanYeah}} disabled{{end}}
|
||||
" data-is-in-reply-list="1" data-feeling="{{.Feeling}}" data-action="/comments/{{.ID}}/yeah" data-url-id="{{.ID}}">
|
||||
<span class="yeah-button-text">
|
||||
{{if .Yeahed}}
|
||||
{{if eq .Feeling 6}}
|
||||
Unepic
|
||||
{{else if eq .Feeling 7}}
|
||||
Unnyeah
|
||||
{{else if eq .Feeling 8}}
|
||||
Unyes
|
||||
{{else if eq .Feeling 9}}
|
||||
olv.portal.miitoo.delete
|
||||
{{else}}
|
||||
Unyeah
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Feeling 2}}
|
||||
Yeah♥
|
||||
{{else if eq .Feeling 3}}
|
||||
Yeah!?
|
||||
{{else if or (eq .Feeling 4) (eq .Feeling 5)}}
|
||||
Yeah...
|
||||
{{else if eq .Feeling 6}}
|
||||
Epic!
|
||||
{{else if eq .Feeling 7}}
|
||||
Nyeah~♥
|
||||
{{else if eq .Feeling 8}}
|
||||
Yes!
|
||||
{{else if eq .Feeling 9}}
|
||||
olv.portal.miitoo.
|
||||
{{else}}
|
||||
Yeah!
|
||||
{{end}}
|
||||
{{end}}
|
||||
</span>
|
||||
</button>
|
||||
<div class="yeah symbol"><span class="symbol-label">Yeahs</span><span class="yeah-count">{{.YeahCount}}</span></div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</li>
|
|
@ -0,0 +1,29 @@
|
|||
<div class="recent-reply-content">
|
||||
{{if gt .CommentCount 1}}<div class="recent-reply-read-more-container" tabindex="0">View all comments ({{.CommentCount}})</div>{{end}}
|
||||
<div tabindex="0" class="recent-reply trigger">
|
||||
<a href="/users/{{.CommentPreview.CommenterUsername}}" username="{{.CommentPreview.CommenterUsername}}" class="icon-container{{if not .CommentPreview.CommenterHideOnline}} {{if .CommentPreview.CommenterOnline}}online{{else}}offline{{end}}{{end}}
|
||||
{{if .CommentPreview.CommenterRoleImage}} official-user"><img src="{{.CommentPreview.CommenterRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{.CommentPreview.CommenterIcon}}" class="icon">
|
||||
</a>
|
||||
<p class="user-name"><a href="/users/{{.CommentPreview.CommenterUsername}}"{{if .CommentPreview.CommenterColor}} style="color:{{.CommentPreview.CommenterColor}}"{{end}}>{{.CommentPreview.CommenterNickname}}</a></p>
|
||||
<p class="timestamp-container">
|
||||
<a class="timestamp" href="/comments/{{.CommentPreview.ID}}">
|
||||
<span class="update" time="{{.CommentPreview.CreatedAtUnix}}000">{{.CommentPreview.CreatedAt}}</span>
|
||||
{{if .CommentPreview.EditedAt}}
|
||||
(Edited <span class="update" time="{{.CommentPreview.EditedAtUnix}}000">{{.CommentPreview.EditedAt}}</span>)
|
||||
{{end}}
|
||||
</a>
|
||||
</p>
|
||||
<div class="body">
|
||||
<div class="post-content">
|
||||
{{if eq .CommentPreview.PostType 1}}
|
||||
<div class="recent-reply-content-memo">
|
||||
<img src="{{.CommentPreview.BodyText}}">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="recent-reply-content-text">{{.CommentPreview.Body}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,56 @@
|
|||
<div class="post scroll {{if .ByMe}}my{{else}}other{{end}}" id="{{.ID}}">
|
||||
<a href="/users/{{.ByUsername}}" username="{{.ByUsername}}" class="icon-container {{if not .ByHideOnline}}{{if .ByOnline}}online{{else}}offline{{end}}{{end}}{{if .ByRoleImage}} official-user"><img src="{{.ByRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{.ByAvatar}}" class="icon">
|
||||
</a>
|
||||
<p class="timestamp-container">
|
||||
<span class="timestamp{{if .DateUnix}} update" time="{{.DateUnix}}000{{end}}">{{.Date}}</span>
|
||||
{{if .ByMe}}
|
||||
<button type="button" class="symbol button edit-button rm-post-button" data-action="/messages/{{.ID}}/delete">
|
||||
<span class="symbol-label">Delete</span>
|
||||
</button>
|
||||
{{end}}
|
||||
</p>
|
||||
<div class="post-body">
|
||||
{{if eq .PostType 1}}
|
||||
<div class="post-content-memo">
|
||||
<img class="post-memo" src="{{.BodyText}}">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="post-content-text">{{.Body}}</div>
|
||||
{{end}}
|
||||
{{if .Image}}
|
||||
{{if eq .AttachmentType 1}}
|
||||
<div class="screenshot-container">
|
||||
<audio controls preload="none" src="{{.Image}}"></audio>
|
||||
</div>
|
||||
{{else if eq .AttachmentType 2}}
|
||||
<div class="screenshot-container still-image">
|
||||
<video controls preload="none" src="{{.Image}}"></video>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="screenshot-container still-image">
|
||||
<img src="{{.Image}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .URL}}
|
||||
{{if eq .URLType 1}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://www.youtube.com/embed/{{.URL}}" width="490" height="276" frameborder="0" allowfullscreen="true"></iframe>
|
||||
</div>
|
||||
{{else if eq .URLType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://open.spotify.com/embed/track/{{.URL}}" width="490" height="276" frameborder="0" allow="encrypted-media"></iframe>
|
||||
</div>
|
||||
{{else if eq .URLType 3}}
|
||||
<div class="screenshot-container video audio">
|
||||
<iframe class="audio" src="https://w.soundcloud.com/player/?url={{.URL}}&auto_play=false&show_artwork=true&color=8000ff" frameborder="0"></iframe>
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="url-link">
|
||||
<a class="link-confirm" href="{{.URL}}" target="_blank">{{.URL}}</a>
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,160 @@
|
|||
{{if eq .Type 4}}
|
||||
<div class="post-list-outline no-content repost-error">
|
||||
<a href="/posts/{{.ID}}">There are more posts in this thread. Click to view...</a>
|
||||
</div>
|
||||
{{else if and (eq .Type 3) (not .CommunityName)}}
|
||||
<div class="post-list-outline no-content repost-error">
|
||||
<p>The post could not be found.</p>
|
||||
</div>
|
||||
{{else if and (eq .Type 3) .IsRMByAdmin}}
|
||||
<div class="no-content track-error" data-track-error="deleted">
|
||||
<div>
|
||||
<p class="deleted-message">
|
||||
Deleted by administrator.<br>
|
||||
Post ID: #{{.ID}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div id="{{.ID}}"{{if not (eq .Type 3)}}{{if not .IsRMByAdmin}} data-href{{if and .IsSpoiler (not .ByMe)}}-hidden{{end}}="/{{if gt .CommentCount -1}}posts{{else}}comments{{end}}/{{.ID}}"{{end}}{{end}} class="post post-subtype-default{{if not .IsRMByAdmin}} trigger{{end}}{{if .Pinned}} pinned{{end}}{{if and (or .IsSpoiler .IsRMByAdmin) (not .ByMe)}} hidden{{end}}{{if gt .Type 1}} post-list-outline{{end}}{{if eq .Type 3}} repost{{if .Image}} with-image{{end}}{{end}}"{{if not .IsRMByAdmin}} tabindex="0"{{end}}>
|
||||
{{if not (eq .Type 0)}}
|
||||
<p class="community-container">
|
||||
{{if .MigrationImage}}
|
||||
<a{{if .MigrationURL}} href="{{.MigrationURL}}{{.MigratedID}}"{{end}} class="post-migration-label{{if gt .Type 2}} from-repost{{end}}">
|
||||
<img src="{{.MigrationImage}}">
|
||||
</a>
|
||||
{{end}}
|
||||
<a{{if not .CommunityRM}} href="/{{if gt .CommentCount -1}}communities{{else}}posts{{end}}/{{.CommunityID}}"{{end}}>
|
||||
<img src="{{.CommunityIcon}}" class="community-icon">
|
||||
{{.CommunityName}}
|
||||
</a>
|
||||
</p>
|
||||
{{end}}
|
||||
<a href="/users/{{.PosterUsername}}" username="{{.PosterUsername}}" class="icon-container {{if not .PosterHideOnline}}{{if .PosterOnline}}online{{else}}offline{{end}}{{end}}
|
||||
{{if .PosterRoleImage}} official-user"><img src="{{.PosterRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{.PosterIcon}}" class="icon">
|
||||
</a>
|
||||
<p class="user-name"><a href="/users/{{.PosterUsername}}"{{if .PosterColor}} style="color:{{.PosterColor}}"{{end}}>{{.PosterNickname}}</a></p>
|
||||
<p class="timestamp-container">
|
||||
<span class="spoiler-status{{if .Pinned}} spoiler{{end}}">Pinned ·</span>
|
||||
{{if .Privacy}}<span class="spoiler-status spoiler">Private ·</span>{{end}}
|
||||
<span class="spoiler-status{{if .IsSpoiler}} spoiler{{end}}">Spoilers ·</span>
|
||||
<a class="timestamp"{{if not .IsRMByAdmin}} href="/{{if gt .CommentCount -1}}posts{{else}}comments{{end}}/{{.ID}}"{{end}}>
|
||||
<span class="update" time="{{.CreatedAtUnix}}000">{{.CreatedAt}}</span>
|
||||
{{if .EditedAt}}
|
||||
(Edited <span class="update" time="{{.EditedAtUnix}}000">{{.EditedAt}}</span>)
|
||||
{{end}}
|
||||
</a>
|
||||
</p>
|
||||
<div class="body post-content">
|
||||
{{if .IsRMByAdmin}}
|
||||
<p class="deleted-message">
|
||||
Deleted by administrator.<br>
|
||||
{{if eq .CommentCount -1}}Commen{{else}}Pos{{end}}t ID: #{{.ID}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{if or (not .IsRMByAdmin) .ByMe}}
|
||||
{{if eq .PostType 1}}
|
||||
<div class="post-content-memo">
|
||||
<img class="post-memo" src="{{.BodyText}}">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="post-content-text">{{.Body}}</div>
|
||||
{{end}}
|
||||
{{if .Image}}
|
||||
{{if eq .AttachmentType 1}}
|
||||
<a class="screenshot-container">
|
||||
<audio controls preload="none" src="{{.Image}}"></audio>
|
||||
</a>
|
||||
{{else if eq .AttachmentType 2}}
|
||||
<div class="screenshot-container still-image">
|
||||
<video controls preload="none" src="{{.Image}}"></video>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="screenshot-container still-image">
|
||||
<img src="{{.Image}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .URL}}
|
||||
{{if eq .URLType 1}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://www.youtube.com/embed/{{.URL}}" frameborder="0" allowfullscreen="true"></iframe>
|
||||
</div>
|
||||
{{else if eq .URLType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe src="https://open.spotify.com/embed/track/{{.URL}}" frameborder="0" allow="encrypted-media"></iframe>
|
||||
</div>
|
||||
{{else if eq .URLType 3}}
|
||||
<div class="screenshot-container video audio">
|
||||
<iframe class="audio" src="https://w.soundcloud.com/player/?url={{.URL}}&auto_play=false&show_artwork=true&color=8000ff" frameborder="0"></iframe>
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="url-link">
|
||||
<a class="link-confirm" href="{{.URL}}" target="_blank">{{.URL}}</a>
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if and .IsSpoiler (not .ByMe)}}
|
||||
<div class="hidden-content">
|
||||
<p>This post contains spoilers.</p>
|
||||
<button type="button" class="hidden-content-button">View Post</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if eq .PostType 2}}
|
||||
{{template "poll.html" .Poll}}
|
||||
{{end}}
|
||||
{{if .RepostID}}
|
||||
{{template "render_post.html" .Repost}}
|
||||
{{end}}
|
||||
{{if and (lt .Type 3) (not .IsRMByAdmin)}}
|
||||
<div class="post-meta">
|
||||
<button type="button" {{if not .CanYeah}}disabled{{end}} class="symbol submit yeah-button
|
||||
{{if not .CanYeah}} disabled{{end}}
|
||||
{{if .Yeahed}} yeah-added{{end}}
|
||||
" data-feeling="{{.Feeling}}" data-action="/posts/{{.ID}}/yeah" data-url-id="{{.ID}}">
|
||||
<span class="yeah-button-text">
|
||||
{{if .Yeahed}}
|
||||
{{if eq .Feeling 6}}
|
||||
Unepic
|
||||
{{else if eq .Feeling 7}}
|
||||
Unnyeah
|
||||
{{else if eq .Feeling 8}}
|
||||
Unyes
|
||||
{{else if eq .Feeling 9}}
|
||||
olv.portal.miitoo.delete
|
||||
{{else}}
|
||||
Unyeah
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Feeling 2}}
|
||||
Yeah♥
|
||||
{{else if eq .Feeling 3}}
|
||||
Yeah!?
|
||||
{{else if or (eq .Feeling 4) (eq .Feeling 5)}}
|
||||
Yeah...
|
||||
{{else if eq .Feeling 6}}
|
||||
Epic!
|
||||
{{else if eq .Feeling 7}}
|
||||
Nyeah~♥
|
||||
{{else if eq .Feeling 8}}
|
||||
Yes!
|
||||
{{else if eq .Feeling 9}}
|
||||
olv.portal.miitoo.
|
||||
{{else}}
|
||||
Yeah!
|
||||
{{end}}
|
||||
{{end}}
|
||||
</span>
|
||||
</button>
|
||||
<div class="yeah symbol"><span class="symbol-label">Yeahs</span><span class="yeah-count">{{.YeahCount}}</span></div>
|
||||
{{if gt .CommentCount -1}}<div class="reply symbol"><span class="symbol-label">Comments</span><span class="reply-count">{{.CommentCount}}</span></div>{{end}}
|
||||
</div>
|
||||
{{if .CommentPreview.BodyText}}
|
||||
{{template "render_comment_preview.html" .}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
|
@ -0,0 +1,144 @@
|
|||
<div id="{{.ID}}"{{if not .IsRMByAdmin}} data-href{{if and .IsSpoiler (not .ByMe)}}-hidden{{end}}="/posts/{{.ID}}"{{end}} class="post{{if not .IsRMByAdmin}} trigger{{end}}{{if .Image}} with-image{{end}}{{if and (or .IsSpoiler .IsRMByAdmin) (not .ByMe)}} hidden{{end}}"{{if not .IsRMByAdmin}} tabindex="0"{{end}}>
|
||||
<p class="community-container">
|
||||
{{if .MigrationImage}}
|
||||
<a{{if .MigrationURL}} href="{{.MigrationURL}}{{.MigratedID}}"{{end}} class="post-migration-label">
|
||||
<img src="{{.MigrationImage}}">
|
||||
</a>
|
||||
{{end}}
|
||||
<a{{if not .CommunityRM}} href="/communities/{{.CommunityID}}"{{end}}>
|
||||
<img src="{{.CommunityIcon}}" class="community-icon">
|
||||
{{.CommunityName}}
|
||||
</a>
|
||||
</p>
|
||||
<div class="body">
|
||||
<div class="post-content">
|
||||
<a href="/users/{{.PosterUsername}}" username="{{.PosterUsername}}" class="icon-container
|
||||
{{if not .PosterHideOnline}}
|
||||
{{if .PosterOnline}}
|
||||
online
|
||||
{{else}}
|
||||
offline
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .PosterRoleImage}} official-user"><img src="{{.PosterRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{.PosterIcon}}" class="icon">
|
||||
</a>
|
||||
<p class="user-name"><a href="/users/{{.PosterUsername}}"{{if .PosterColor}} style="color:{{.PosterColor}}"{{end}}>{{.PosterNickname}}</a></p>
|
||||
<p class="timestamp-container">
|
||||
{{if .Pinned}}
|
||||
<span class="spoiler">Pinned</span> ·
|
||||
{{end}}
|
||||
{{if .Privacy}}
|
||||
<span class="spoiler">Private</span> ·
|
||||
{{end}}
|
||||
{{if .IsSpoiler}}
|
||||
<span class="spoiler">Spoilers</span> ·
|
||||
{{end}}
|
||||
<a class="timestamp"{{if not .IsRMByAdmin}} href="/posts/{{.ID}}"{{end}}>
|
||||
<span class="update" time="{{.CreatedAtUnix}}000">{{.CreatedAt}}</span>
|
||||
{{if .EditedAt}}
|
||||
(Edited <span class="update" time="{{.EditedAtUnix}}000">{{.EditedAt}}</span>)
|
||||
{{end}}
|
||||
</a>
|
||||
</p>
|
||||
{{if .IsRMByAdmin}}
|
||||
<p class="deleted-message">
|
||||
Deleted by administrator.<br>
|
||||
Post ID: #{{.ID}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{if or (not .IsRMByAdmin) .ByMe}}
|
||||
{{if .Image}}
|
||||
{{if eq .AttachmentType 1}}
|
||||
<a class="screenshot-container">
|
||||
<audio controls preload="none" src="{{.Image}}"></audio>
|
||||
</a>
|
||||
{{else if eq .AttachmentType 2}}
|
||||
<div class="screenshot-container still-image">
|
||||
<video controls preload="none" src="{{.Image}}"></video>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="screenshot-container still-image">
|
||||
<img src="{{.Image}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .URL}}
|
||||
{{if eq .URLType 1}}
|
||||
<a href="/posts/{{.ID}}" class="screenshot-container video">
|
||||
<img height="48" src="https://i.ytimg.com/vi/{{.URL}}/default.jpg">
|
||||
</a>
|
||||
{{else}}
|
||||
<p class="url-link">
|
||||
<a class="link-confirm" href="{{if eq .URLType 2}}https://open.spotify.com/track/{{.URL}}{{else}}{{.URL}}{{end}}" target="_blank">{{if eq .URLType 2}}https://open.spotify.com/track/{{end}}{{.URL}}</a>
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if eq .PostType 1}}
|
||||
<div class="post-content-memo">
|
||||
<img class="post-memo" src="{{.BodyText}}">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="post-content-text">{{.Body}}</div>
|
||||
{{end}}
|
||||
{{if and .IsSpoiler (not .ByMe)}}
|
||||
<div class="hidden-content">
|
||||
<p>This post contains spoilers.
|
||||
<button type="button" class="hidden-content-button">View Post</button>
|
||||
</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if eq .PostType 2}}
|
||||
{{template "poll.html" .Poll}}
|
||||
{{end}}
|
||||
{{if .RepostID}}
|
||||
{{template "render_post.html" .Repost}}
|
||||
{{end}}
|
||||
{{if not .IsRMByAdmin}}
|
||||
<div class="post-meta">
|
||||
<button type="button" {{if not .CanYeah}} disabled{{end}} class="symbol submit yeah-button
|
||||
{{if .Yeahed}} yeah-added{{end}}
|
||||
{{if not .CanYeah}} disabled{{end}}
|
||||
" data-feeling="{{.Feeling}}" data-action="/posts/{{.ID}}/yeah" data-community-id data-url-id="{{.ID}}">
|
||||
<span class="yeah-button-text">
|
||||
{{if .Yeahed}}
|
||||
{{if eq .Feeling 6}}
|
||||
Unepic
|
||||
{{else if eq .Feeling 7}}
|
||||
Unnyeah
|
||||
{{else if eq .Feeling 8}}
|
||||
Unyes
|
||||
{{else if eq .Feeling 9}}
|
||||
olv.portal.miitoo.delete
|
||||
{{else}}
|
||||
Unyeah
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Feeling 2}}
|
||||
Yeah♥
|
||||
{{else if eq .Feeling 3}}
|
||||
Yeah!?
|
||||
{{else if or (eq .Feeling 4) (eq .Feeling 5)}}
|
||||
Yeah...
|
||||
{{else if eq .Feeling 6}}
|
||||
Epic!
|
||||
{{else if eq .Feeling 7}}
|
||||
Nyeah~♥
|
||||
{{else if eq .Feeling 8}}
|
||||
Yes!
|
||||
{{else if eq .Feeling 9}}
|
||||
olv.portal.miitoo.
|
||||
{{else}}
|
||||
Yeah!
|
||||
{{end}}
|
||||
{{end}}
|
||||
</span>
|
||||
</button>
|
||||
<div class="yeah symbol"><span class="symbol-label">Yeahs</span><span class="yeah-count">{{.YeahCount}}</span></div>
|
||||
<div class="reply symbol"><span class="symbol-label">Comments</span><span class="reply-count">{{.CommentCount}}</span></div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
<a href="/users/{{.Username}}" id="{{.ID}}" class="post-permalink-feeling-icon">
|
||||
{{if .Role}}<img src="{{.Role}}" class="official-tag">{{end}}
|
||||
<img src="{{.Avatar}}" class="user-icon">
|
||||
</a>
|
|
@ -0,0 +1,15 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
<meta property="og:description" content="I AM ERROR. haha get it funny gamer reference that only true gamers will understand">
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
<div class="no-content">
|
||||
<p>{{.Error}}</p><br>
|
||||
<img src="https://upload.wikimedia.org/wikipedia/en/c/cc/Wojak_cropped.jpg">
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,74 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
{{template "general_sidebar.html" .}}
|
||||
<div class="main-column messages">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">Friend Requests</h2>
|
||||
<div id="notification-tab-container" class="tab-container">
|
||||
<div class="tab2">
|
||||
<a class="tab-icon-my-news{{if .Notify}} notify{{end}}" href="/notifications">
|
||||
<span class="symbol nf"></span>
|
||||
<span>Updates</span>
|
||||
</a>
|
||||
<a class="tab-icon-my-news selected" href="/notifications/friend_requests">
|
||||
<span class="symbol fr"></span>
|
||||
<span>Friend Requests</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list news-list">
|
||||
{{if .FriendRequests}}
|
||||
{{$username := .CurrentUser.Username}}
|
||||
{{range $request := .FriendRequests}}
|
||||
<div class="dialog none" data-modal-types="accept-friend-request" data-screen-name="{{$request.ByNickname}}" data-reject-action="/users/{{$request.ByUsername}}/friend_reject" data-action="/users/{{$request.ByUsername}}/friend_accept" uuid="{{$request.ID}}">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">Friend Request from {{$request.ByNickname}} at <span class="update" time="{{$request.CreatedAtUnix}}000">{{$request.CreatedAt}}</span></h1>
|
||||
<div class="window-body">
|
||||
<div id="sidebar-profile-body">
|
||||
<div username="{{$request.ByUsername}}" class="icon-container {{if not $request.ByHideOnline}}{{if $request.ByOnline}}online{{else}}offline{{end}}{{end}}{{if $request.ByRoleImage}} official-user"><img src="{{$request.ByRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<a href="/users/{{$request.ByUsername}}"><img src="{{$request.ByAvatar}}" alt="{{$request.ByNickname}}" class="icon"></a>
|
||||
</div>
|
||||
{{if $request.ByRoleOrganization}}<p class="user-organization">{{$request.ByRoleOrganization}}</p>{{end}}
|
||||
<a href="/users/{{$request.ByUsername}}" class="nick-name"{{if $request.ByColor}} style="color:{{$request.ByColor}}"{{end}}>{{$request.ByNickname}}</a>
|
||||
<p class="id-name">{{$request.ByUsername}}</p>
|
||||
</div>
|
||||
{{if $request.Message}}<pre>{{$request.Message}}</pre>{{end}}
|
||||
<p class="window-body-content">Accept {{$request.ByNickname}}'s friend request?</p>
|
||||
<div class="form-buttons three">
|
||||
<button class="olv-modal-close-button gray-button" data-event-type="cancel" type="button">Cancel</button>
|
||||
<button class="cancel-button gray-button" type="button">Reject</button>
|
||||
<button class="ok-button post-button black-button" data-event-type="ok" type="button">Accept</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="news-list-content{{if not $request.Read}} notify{{end}} trigger" tabindex="0" id="{{$request.ID}}" data-href="/users/{{$request.ByUsername}}">
|
||||
<div username="{{$request.ByUsername}}" class="icon-container {{if $request.ByOnline}}online{{else}}offline{{end}}{{if $request.ByRoleImage}} official-user"><img src="{{$request.ByRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<a href="/users/{{$request.ByUsername}}"><img src="{{$request.ByAvatar}}" alt="{{$request.ByNickname}}" class="icon"></a>
|
||||
</div>
|
||||
<div class="body">
|
||||
<a href="/users/{{$request.ByUsername}}" class="nick-name">{{$request.ByNickname}}</a>
|
||||
<br>
|
||||
<span class="timestamp update" time="{{$request.CreatedAtUnix}}000">{{$request.Date}}</span>
|
||||
<button class="button received-request-button" type="button">View friend request</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="no-content">
|
||||
<p>You have no friend requests.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,29 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
<meta property="og:description" content="Before joining Indigo, it's highly recommended that you read these rules so as to not cause any issues.">
|
||||
{{else}}
|
||||
<title>rules</title>
|
||||
{{end}}
|
||||
<div id="main-body" class="profile-top">
|
||||
{{template "general_sidebar.html" .}}
|
||||
<div class="main-column" id="help">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">about SOP.epic</h2>
|
||||
<div id="guide" class="help-content">
|
||||
<h2>rules</h2>
|
||||
<p>
|
||||
1: there are hardly any except please dont post porn or gore or your the location of your uncles meth lab<br>
|
||||
2: dont post malware or a site that easily can give you malware in the piracy cabal network (exception is if you explicitly state you need an adblock or something)
|
||||
</p>
|
||||
<h2>this site looks like ass! the background hurts my eyes!</h2>
|
||||
<p>you can change it in the profile settings!!!!</p>
|
||||
<h2>this place seems vaguely familiar...</h2>
|
||||
<p>running heavily modified <a href= "https://github.com/PF2M/Indigo">Indigo</a></p>
|
||||
<img src="/assets/img/dancingjesus.gif">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,35 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
<meta property="og:description" content="SOP.epic is a feature packed twitter alternative with no paywalling of basic features and easily customizable themes!">
|
||||
{{else}}
|
||||
<title>{{.Title}} - sop.epic</title>
|
||||
{{end}}
|
||||
|
||||
<style>
|
||||
h1 {color:black;}
|
||||
p {color:black;}
|
||||
</style>
|
||||
|
||||
<div id="main-body">
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<h1>Welcome to SOP.epic!</h1><br>
|
||||
<p>SOP.epic (pronounced ess-oh-pee) is a feature packed twitter alternative with no paywalling of basic features and easily customizable themes!<p>
|
||||
<img src="assets/img/weedchan.png"><br>
|
||||
|
||||
<p>awesome art:<br><br>
|
||||
|
||||
<img src="assets/img/art/lg_16150364_img_20221203_065249.png" width="430" height="330"> by rem
|
||||
|
||||
<img src="assets/img/art/ketamine.png" width="430" height="384"> by minitt
|
||||
</div>
|
||||
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,47 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
{{template "general_sidebar.html" .}}
|
||||
<div class="main-column messages">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">Messages<a href="/conversations/create"><button class="button msg-update">Create Group</button></a></h2>
|
||||
<div class="list">
|
||||
<ul class="list-content-with-icon-and-text arrow-list"{{if .Conversations}} data-next-page-url="/messages?offset={{.Offset}}&offset_time={{.OffsetTime}}"{{end}}>
|
||||
{{if .Conversations}}
|
||||
{{$user_id := .CurrentUser.ID}}
|
||||
{{range $conversation := .Conversations}}
|
||||
<li class="trigger{{if (and (not $conversation.Read) (not (eq $conversation.CreatedBy $user_id)))}} notify{{end}}" data-href="/{{if not (eq $conversation.Target 0)}}messages{{else}}conversations{{end}}/{{$conversation.Username}}">
|
||||
<a href="/{{if eq $conversation.Target 0}}conversations{{else}}users{{end}}/{{$conversation.Username}}" username="{{$conversation.Username}}" class="icon-container {{if not $conversation.HideOnline}}{{if $conversation.Online}}online{{else}}offline{{end}}{{end}}
|
||||
{{if $conversation.RoleImage}} official-user"><img src="{{$conversation.RoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{$conversation.Icon}}" class="icon">
|
||||
</a>
|
||||
<div class="body">
|
||||
<p class="title">
|
||||
<span class="nick-name">
|
||||
<a href="/{{if eq $conversation.Target 0}}conversations{{else}}users{{end}}/{{$conversation.Username}}"{{if $conversation.Color}} style="color:{{$conversation.Color}}"{{end}}>{{$conversation.Nickname}}</a>
|
||||
</span>
|
||||
{{if not (eq $conversation.Target 0)}}<span class="id-name">{{$conversation.Username}}</span>{{end}}
|
||||
</p>
|
||||
{{if $conversation.Date}}<span class="timestamp update" time="{{$conversation.DateUnix}}000">{{$conversation.Date}}</span>{{end}}
|
||||
<p class="text {{if eq $conversation.CreatedBy $user_id}}my{{else}}other{{end}}{{if and ($conversation.BodyText) (eq $conversation.PostType 0)}}">{{$conversation.BodyText}}{{else if eq $conversation.PostType 1}} type-memo">(handwritten){{else if $conversation.Image}} type-memo">(attachment){{else}} placeholder">You haven't exchanged messages with this {{if eq $conversation.Target 0}}group{{else}}user{{end}} yet.{{end}}</p>
|
||||
</div>
|
||||
</li>
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Offset 20}}
|
||||
<div class="no-content">
|
||||
<p>No messages.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,73 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
{{else}}
|
||||
<title>{{.Title}}</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
{{template "general_sidebar.html" .}}
|
||||
<div class="main-column messages">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">My notifications</h2>
|
||||
<div id="notification-tab-container" class="tab-container">
|
||||
<div class="tab2">
|
||||
<a class="tab-icon-my-news selected" href="/notifications">
|
||||
<span class="symbol nf"></span>
|
||||
<span>Updates</span>
|
||||
</a>
|
||||
<a class="tab-icon-my-news{{if .Notify}} notify{{end}}" href="/notifications/friend_requests">
|
||||
<span class="symbol fr"></span>
|
||||
<span>Friend Requests</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list news-list">
|
||||
{{if .Notifs}}
|
||||
{{$username := .CurrentUser.Username}}
|
||||
{{range $notif := .Notifs}}
|
||||
<div class="news-list-content{{if not $notif.Read}} notify{{end}} trigger" tabindex="0" id="{{$notif.ID}}" data-href="
|
||||
{{if or (or (eq $notif.Type 0) (eq $notif.Type 2)) (or (eq $notif.Type 3) (eq $notif.Type 7))}}
|
||||
/posts/{{$notif.Post.Int64}}
|
||||
{{else if eq $notif.Type 1}}
|
||||
/comments/{{$notif.Post.Int64}}
|
||||
{{else if eq $notif.Type 7}}
|
||||
/news/fuck
|
||||
{{else}}
|
||||
/users/{{$username}}/followers
|
||||
{{end}}
|
||||
">
|
||||
<a href="/users/{{$notif.ByUsername}}" username="{{$notif.ByUsername}}" class="icon-container {{if not $notif.ByHideOnline}}{{if $notif.ByOnline}}online{{else}}offline{{end}}{{end}}
|
||||
{{if $notif.ByRoleImage}} official-user"><img src="{{$notif.ByRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{$notif.ByAvatar}}" class="icon">
|
||||
</a>
|
||||
<div class="body">
|
||||
<!-- i'm sorry mom -->
|
||||
{{if eq $notif.Type 4}}Followed by {{end}}<a href="/users/{{$notif.ByUsername}}" class="nick-name"{{if $notif.ByColor}} style="color:{{$notif.ByColor}}"{{end}}>{{$notif.ByNickname}}</a>{{if $notif.MergedCount}}{{if eq $notif.MergedCount 1}} and <a href="/users/{{index $notif.MergedUsername 0}}" class="nick-name"{{if index $notif.MergedColor 0}} style="color:{{index $notif.MergedColor 0}}"{{end}}>{{index $notif.MergedNickname 0}}</a>{{else if eq $notif.MergedCount 2}}, <a href="/users/{{index $notif.MergedUsername 0}}" class="nick-name"{{if index $notif.MergedColor 0}} style="color:{{index $notif.MergedColor 0}}"{{end}}>{{index $notif.MergedNickname 0}}</a>, and <a href="/users/{{index $notif.MergedUsername 1}}" class="nick-name"{{if index $notif.MergedColor 1}} style="color:{{index $notif.MergedColor 1}}"{{end}}>{{index $notif.MergedNickname 1}}</a>{{else if eq $notif.MergedCount 3}}, <a href="/users/{{index $notif.MergedUsername 0}}" class="nick-name"{{if (index $notif.MergedColor 0)}} style="color:{{index $notif.MergedColor 0}}"{{end}}>{{index $notif.MergedNickname 0}}</a>, <a href="/users/{{index $notif.MergedUsername 1}}" class="nick-name"{{if index $notif.MergedColor 1}} style="color:{{index $notif.MergedColor 1}}"{{end}}>{{index $notif.MergedNickname 1}}</a>, and <a href="/users/{{index $notif.MergedUsername 2}}" class="nick-name"{{if index $notif.MergedColor 2}} style="color:{{index $notif.MergedColor 2}}"{{end}}>{{index $notif.MergedNickname 2}}</a>{{else if eq $notif.MergedCount 4}}, <a href="/users/{{index $notif.MergedUsername 0}}" class="nick-name"{{if index $notif.MergedColor 0}} style="color:{{index $notif.MergedColor 0}}"{{end}}>{{index $notif.MergedNickname 0}}</a>, <a href="/users/{{index $notif.MergedUsername 1}}" class="nick-name"{{if index $notif.MergedColor 1}} style="color:{{index $notif.MergedColor 1}}"{{end}}>{{index $notif.MergedNickname 1}}</a>, <a href="/users/{{index $notif.MergedUsername 2}}" class="nick-name"{{if index $notif.MergedColor 2}} style="color:{{index $notif.MergedColor 2}}"{{end}}>{{index $notif.MergedNickname 2}}</a>, and 1 other{{else}}, <a href="/users/{{index $notif.MergedUsername 0}}" class="nick-name"{{if index $notif.MergedColor 0}} style="color:{{index $notif.MergedColor 0}}"{{end}}>{{index $notif.MergedNickname 0}}</a>, <a href="/users/{{index $notif.MergedUsername 1}}" class="nick-name"{{if index $notif.MergedColor 1}} style="color:{{index $notif.MergedColor 1}}"{{end}}>{{index $notif.MergedNickname 1}}</a>, <a href="/users/{{index $notif.MergedUsername 2}}" class="nick-name"{{if index $notif.MergedColor 2}} style="color:{{index $notif.MergedColor 2}}"{{end}}>{{index $notif.MergedNickname 2}}</a>, and {{$notif.MergedOthers}} others{{end}}{{end}}
|
||||
{{if eq $notif.Type 0}}
|
||||
gave <a href="/posts/{{$notif.Post.Int64}}" class="link">your post ({{$notif.PostText}})</a> a Yeah.
|
||||
{{else if eq $notif.Type 1}}
|
||||
gave <a href="/comments/{{$notif.Post.Int64}}" class="link">your comment ({{$notif.PostText}})</a> a Yeah.
|
||||
{{else if eq $notif.Type 2}}
|
||||
commented on <a href="/posts/{{$notif.Post.Int64}}" class="link">your post ({{$notif.PostText}})</a>.
|
||||
{{else if eq $notif.Type 3}}
|
||||
commented on <a href="/posts/{{$notif.Post.Int64}}" class="link">{{$notif.ByNickname}}'s post ({{$notif.PostText}})</a>.
|
||||
{{else if eq $notif.Type 7}}
|
||||
reposted <a href="/posts/{{$notif.Post.Int64}}" class="link">your post ({{$notif.PostText}})</a>.
|
||||
{{else if eq $notif.Type 8}}
|
||||
<br>You have received a notification from the Indigo Administration.
|
||||
{{end}}
|
||||
<span class="timestamp update" time="{{$notif.DateUnix}}000">{{$notif.Date}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="no-content">
|
||||
<p>No notifications.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|
|
@ -0,0 +1,352 @@
|
|||
{{if .Pjax}}
|
||||
{{template "header.html" .}}
|
||||
<meta property="og:profile:username" content="{{.Post.PosterUsername}}">
|
||||
<meta property="og:description" content="{{if .Post.Body}}{{.Post.Body}}{{else}}View {{.Post.PosterNickname}}'s post on Indigo.{{end}}">
|
||||
{{if .Post.Image}}<meta property="og:{{if eq .Post.AttachmentType 1}}audio{{else if eq .Post.AttachmentType 2}}video{{else}}image{{end}}" content="{{.Post.Image}}">{{end}}
|
||||
{{else}}
|
||||
<title>{{.Title}} - sop.epic</title>
|
||||
{{end}}
|
||||
<div id="main-body">
|
||||
<div class="main-column replyform-bottom">
|
||||
<div class="post-list-outline">
|
||||
<section id="post-content" class="post post-subtype-default">
|
||||
<header class="community-container">
|
||||
{{if .Post.MigrationImage}}
|
||||
<a{{if .Post.MigrationURL}} href="{{.Post.MigrationURL}}{{.Post.MigratedID}}"{{end}} class="post-migration-label">
|
||||
<img src="{{.Post.MigrationImage}}">
|
||||
</a>
|
||||
{{end}}
|
||||
<h1 class="community-container-heading">
|
||||
<a{{if not .Community.RM}} href="/communities/{{.Community.ID}}"{{end}}>
|
||||
<img src="{{.Community.Icon}}" class="community-icon">
|
||||
{{.Community.Title}}
|
||||
</a>
|
||||
</h1>
|
||||
</header>
|
||||
{{if and .CurrentUser.Username (not .Post.IsRMByAdmin)}}
|
||||
<div class="edit-buttons-content">
|
||||
{{if or (eq .Post.CreatedBy .CurrentUser.ID) (gt .CurrentUser.Level 0)}}<button type="button" class="symbol button edit-button rm-post-button" data-action="/posts/{{.Post.ID}}/delete"><span class="symbol-label">Delete</span></button>{{end}}
|
||||
{{if and (eq .Post.CreatedBy .CurrentUser.ID) (not (eq .Post.PostType 1))}}<button type="button" class="symbol button edit-button edit-post-button"><span class="symbol-label">Edit</span></button>{{end}}
|
||||
{{if and (.Post.Image) (eq .Post.AttachmentType 0)}}<button type="button" class="symbol button edit-button profile-post-button{{if .IsFavorite}} done{{end}}" data-action="/posts/{{.Post.ID}}/{{if .IsFavorite}}un{{end}}favorite"><span class="symbol-label">Set as Favorite Post</span></button>{{end}}
|
||||
<button type="button" class="symbol button edit-button repost-button" post="{{.Post.ID}}"><span class="symbol-label">Repost</span></button>
|
||||
</div>
|
||||
{{if not (eq .Post.CreatedBy .CurrentUser.ID)}}<div class="report-buttons-content" style="float:right"><button type="button" class="report-button" data-modal-open="#report-violation-page" data-screen-name="{{.Post.PosterNickname}}" data-support-text="#{{.Post.ID}}" data-action="/posts/{{.Post.ID}}/violations" data-can-report-spoiler="{{if .Post.IsSpoiler}}0{{else}}1{{end}}" data-track-action="openReportModal" data-track-category="reportViolation">Report Violation</button></div>{{end}}
|
||||
{{end}}
|
||||
<div class="user-content">
|
||||
<a href="/users/{{.Post.PosterUsername}}" username="{{.Post.PosterUsername}}" class="icon-container
|
||||
{{if not .Post.PosterHideOnline}}
|
||||
{{if .Post.PosterOnline}}
|
||||
online
|
||||
{{else}}
|
||||
offline
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .Post.PosterRoleImage}} official-user"><img src="{{.Post.PosterRoleImage}}" class="official-tag">{{else}}">{{end}}
|
||||
<img src="{{.Post.PosterIcon}}" class="icon">
|
||||
</a>
|
||||
<div class="user-name-content">
|
||||
{{if .Post.PosterRoleOrganization}}<p class="user-organization">{{.Post.PosterRoleOrganization}}</p>{{end}}
|
||||
<p class="user-name">
|
||||
<a href="/users/{{.Post.PosterUsername}}"{{if .Post.PosterColor}} style="color:{{.Post.PosterColor}}"{{end}}>{{.Post.PosterNickname}}</a>
|
||||
<span class="user-id">{{.Post.PosterUsername}}</span>
|
||||
</p>
|
||||
<p class="timestamp-container">
|
||||
<span class="timestamp">
|
||||
<span class="update" time="{{.Post.CreatedAtUnix}}000">{{.Post.CreatedAt}}</span>
|
||||
{{if .Post.EditedAt}}
|
||||
(Edited <span class="update" time="{{.Post.EditedAtUnix}}000">{{.Post.EditedAt}}</span>)
|
||||
{{end}}
|
||||
</span>
|
||||
<span class="spoiler-status{{if .Post.Pinned}} spoiler{{end}}">· Pinned</span>
|
||||
{{if .Post.Privacy}}
|
||||
<span class="spoiler-status spoiler">· Private ({{if eq .Post.Privacy 1}}Friends, Following and Followers{{else if eq .Post.Privacy 2}}Friends and Following{{else if eq .Post.Privacy 3}}Friends and Followers{{else if eq .Post.Privacy 4}}Friends Only{{else if eq .Post.Privacy 5}}Followers and Following{{else if eq .Post.Privacy 6}}Followers Only{{else if eq .Post.Privacy 7}}Following Only{{else if eq .Post.Privacy 8}}Admins Only{{else}}Only Me{{end}})</span>
|
||||
{{end}}
|
||||
<span class="spoiler-status{{if .Post.IsSpoiler}} spoiler{{end}}">· Spoilers</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">
|
||||
{{if eq .Post.CreatedBy .CurrentUser.ID}}
|
||||
<div id="post-edit" class="none">
|
||||
<form data-action="/posts/{{.Post.ID}}/edit" id="edit-form" method="post">
|
||||
<div class="feeling-selector js-feeling-selector test-feeling-selector"><label class="symbol feeling-button feeling-button-normal checked"><input type="radio" name="feeling_id" value="0"{{if eq .Post.Feeling 0}} checked{{end}}><span class="symbol-label">normal</span></label><label class="symbol feeling-button feeling-button-happy"><input type="radio" name="feeling_id" value="1"{{if eq .Post.Feeling 1}} checked{{end}}><span class="symbol-label">happy</span></label><label class="symbol feeling-button feeling-button-like"><input type="radio" name="feeling_id" value="2"{{if eq .Post.Feeling 2}} checked{{end}}><span class="symbol-label">like</span></label><label class="symbol feeling-button feeling-button-surprised"><input type="radio" name="feeling_id" value="3"{{if eq .Post.Feeling 3}} checked{{end}}><span class="symbol-label">surprised</span></label><label class="symbol feeling-button feeling-button-frustrated"><input type="radio" name="feeling_id" value="4"{{if eq .Post.Feeling 4}} checked{{end}}><span class="symbol-label">frustrated</span></label><label class="symbol feeling-button feeling-button-puzzled"><input type="radio" name="feeling_id" value="5"{{if eq .Post.Feeling 5}} checked{{end}}><span class="symbol-label">puzzled</span></label></div>
|
||||
<div class="textarea-container">
|
||||
<textarea name="body" class="textarea-text textarea " maxlength="2000" placeholder="Edit your post." data-required>{{.Post.BodyText}}</textarea>
|
||||
</div>
|
||||
<div class="post-form-footer-options">
|
||||
<label class="spoiler-button symbol"><input id="is_spoiler" name="is_spoiler" type="checkbox" value="1"{{if .Post.IsSpoiler}} checked{{end}}>Spoilers</label>
|
||||
</div>
|
||||
<div class="post-form-privacy">
|
||||
<p>Who should be able to see this post?</p>
|
||||
<select class="post-form-privacy-select" name="privacy">
|
||||
<option value="0"{{if eq .Post.Privacy 0}} selected{{end}}>Everyone</option>
|
||||
<option value="1"{{if eq .Post.Privacy 1}} selected{{end}}>Friends, Following and Followers</option>
|
||||
<option value="2"{{if eq .Post.Privacy 2}} selected{{end}}>Friends and Following</option>
|
||||
<option value="3"{{if eq .Post.Privacy 3}} selected{{end}}>Friends and Followers</option>
|
||||
<option value="4"{{if eq .Post.Privacy 4}} selected{{end}}>Friends Only</option>
|
||||
<option value="5"{{if eq .Post.Privacy 5}} selected{{end}}>Followers and Following</option>
|
||||
<option value="6"{{if eq .Post.Privacy 6}} selected{{end}}>Followers Only</option>
|
||||
<option value="7"{{if eq .Post.Privacy 7}} selected{{end}}>Following Only</option>
|
||||
<option value="8"{{if eq .Post.Privacy 8}} selected{{end}}>Admins Only</option>
|
||||
<option value="9"{{if eq .Post.Privacy 9}} selected{{end}}>Only Me</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<button type="button" class="cancel-button gray-button">Cancel</button>
|
||||
<button type="submit" class="post-button black-button">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{else if and (.CurrentUser.Username) (not (eq .Post.PostType 1))}}
|
||||
<div id="report-violation-page" class="dialog none" data-modal-types="report report-violation" data-is-template="1">
|
||||
<div class="dialog-inner">
|
||||
<div class="window">
|
||||
<h1 class="window-title">report chuddery</h1>
|
||||
<div class="window-body">
|
||||
<p class="description">you are about to report to the owner of SOP.epic on regards of breaking the rules</p>
|
||||
<form method="post" action="/posts/{{.Post.ID}}/violations">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<p class="select-button-label">Violation Type: </p>
|
||||
<select name="type" class="cannot-report-spoiler">
|
||||
<option selected value>Please make a selection.</option>
|
||||
{{range $index, $reason := .Reasons}}
|
||||
{{if $reason.Enabled}}
|
||||
<option value="{{$index}}"{{if $reason.BodyRequired}} data-body-required="1"{{end}}>{{$reason.Name}}</option>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</select>
|
||||
<select name="type" class="can-report-spoiler">
|
||||
<option selected value>Please make a selection.</option>
|
||||
<option value="spoiler" data-body-required="1" data-track-action="Spoiler">Spoiler</option>
|
||||
{{range $index, $reason := .Reasons}}
|
||||
{{if $reason.Enabled}}
|
||||
<option value="{{$index}}"{{if $reason.BodyRequired}} data-body-required="1"{{end}}>{{$reason.Name}}</option>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</select>
|
||||
<textarea name="body" class="textarea" maxlength="100" data-placeholder="Enter a reason for the report."></textarea>
|
||||
<p class="post-id">Post ID: #{{.Post.ID}}</p>
|
||||
<div class="form-buttons">
|
||||
<input type="button" class="olv-modal-close-button gray-button" value="Cancel">
|
||||
<input type="submit" class="post-button black-button" value="Submit Report" data-url-id="{{.Post.ID}}" data-track-action="openReportModal">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div id="the-post">
|
||||
{{if .Post.IsRMByAdmin}}
|
||||
<p class="deleted-message">
|
||||
Deleted by administrator.<br>
|
||||
Post ID: #{{.Post.ID}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{if or (not .Post.IsRMByAdmin) (eq .Post.CreatedBy .CurrentUser.ID)}}
|
||||
{{if eq .Post.PostType 1}}
|
||||
<div class="post-content-memo">
|
||||
<img class="post-memo" src="{{.Post.BodyText}}">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="post-content-text">{{.Post.Body}}</div>
|
||||
{{end}}
|
||||
{{if .Post.Image}}
|
||||
{{if eq .Post.AttachmentType 1}}
|
||||
<div class="screenshot-container">
|
||||
<audio controls preload="none" src="{{.Post.Image}}"></audio>
|
||||
</div>
|
||||
{{else if eq .Post.AttachmentType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<video controls preload="none" src="{{.Post.Image}}"></video>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="screenshot-container still-image">
|
||||
<img src="{{.Post.Image}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .Post.URL}}
|
||||
{{if eq .Post.URLType 1}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe width="490" height="276" src="https://www.youtube.com/embed/{{.Post.URL}}" frameborder="0" allowfullscreen="true"></iframe>
|
||||
</div>
|
||||
{{else if eq .Post.URLType 2}}
|
||||
<div class="screenshot-container video">
|
||||
<iframe width="490" height="276" src="https://open.spotify.com/embed/track/{{.Post.URL}}" frameborder="0" allow="encrypted-media"></iframe>
|
||||
</div>
|
||||
{{else if eq .Post.URLType 3}}
|
||||
<div class="screenshot-container video audio">
|
||||
<iframe class="audio" src="https://w.soundcloud.com/player/?url={{.Post.URL}}&auto_play=false&show_artwork=true&color=8000ff" frameborder="0"></iframe>
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="url-link">
|
||||
<a class="link-confirm" href="{{.Post.URL}}" target="_blank">{{.Post.URL}}</a>
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if eq .Post.PostType 2}}
|
||||
{{template "poll.html" .Post.Poll}}
|
||||
{{end}}
|
||||
{{if .Post.RepostID}}
|
||||
{{template "render_post.html" .Post.Repost}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if not .Post.IsRMByAdmin}}
|
||||
<div class="post-meta">
|
||||
<button type="button" {{if (or (eq .Post.CreatedBy .CurrentUser.ID) (not .CurrentUser.Username))}}disabled{{end}} class="symbol submit yeah-button
|
||||
{{if .Post.Yeahed}} yeah-added{{end}}
|
||||
{{if not .Post.CanYeah}} disabled{{end}}
|
||||
" data-feeling="{{.Post.Feeling}}" data-action="/posts/{{.Post.ID}}/yeah" data-url-id="{{.Post.ID}}">
|
||||
<span class="yeah-button-text">
|
||||
{{if .Post.Yeahed}}
|
||||
{{if eq .Post.Feeling 6}}
|
||||
Unlike
|
||||
{{else if eq .Post.Feeling 7}}
|
||||
Unnyeah
|
||||
{{else if eq .Post.Feeling 8}}
|
||||
Unyes
|
||||
{{else if eq .Post.Feeling 9}}
|
||||
olv.portal.miitoo.delete
|
||||
{{else}}
|
||||
Unyeah
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if eq .Post.Feeling 2}}
|
||||
Yeah♥
|
||||
{{else if eq .Post.Feeling 3}}
|
||||
Yeah!?
|
||||
{{else if or (eq .Post.Feeling 4) (eq .Post.Feeling 5)}}
|
||||
Yeah...
|
||||
{{else if eq .Post.Feeling 6}}
|
||||
Epic!
|
||||
{{else if eq .Post.Feeling 7}}
|
||||
Nyeah~♥
|
||||
{{else if eq .Post.Feeling 8}}
|
||||
Yes!
|
||||
{{else if eq .Post.Feeling 9}}
|
||||
olv.portal.miitoo.
|
||||
{{else}}
|
||||
Like
|
||||
{{end}}
|
||||
{{end}}
|
||||
</span>
|
||||
</button>
|
||||
<div class="yeah symbol"><span class="symbol-label">Likes</span><span class="yeah-count">{{.Post.YeahCount}}</span></div>
|
||||
<div class="reply symbol"><span class="symbol-label">Comments</span><span class="reply-count">{{.Post.CommentCount}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div id="yeah-content"{{if not (or (.Yeahs) (.Post.Yeahed))}} class="none"{{end}}>
|
||||
<a href="/users/{{.CurrentUser.Username}}" class="post-permalink-feeling-icon visitor"{{if not .Post.Yeahed}} style="display: none;"{{end}}>
|
||||
{{if .CurrentUser.Role.Image}}<img src="{{.CurrentUser.Role.Image}}" class="official-tag">{{end}}
|
||||
<img src="{{.CurrentUser.Avatar}}" class="user-icon">
|
||||
</a>
|
||||
{{range $yeah := .Yeahs}}
|
||||
{{template "yeah_icon.html" $yeah}}
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="reply-content">
|
||||
<h2 class="reply-label">Comments</h2>
|
||||
<div class="no-reply-content{{if or .Comments .PinnedComments}} none{{end}}">
|
||||
<div>
|
||||
<p>This post has no comments.</p>
|
||||
</div>
|
||||
</div>
|
||||
{{if gt .Post.CommentCount 20}}
|
||||
<button data-fragment-url="/posts/{{.Post.ID}}/comments" class="more-button all-replies-button" data-reply-count="{{.Post.CommentCount}}">
|
||||
<span class="symbol">Show all comments ({{.Post.CommentCount}})</span>
|
||||
</button>
|
||||
{{end}}
|
||||
<ul class="list reply-list test-reply-list">
|
||||
{{range $comment := .PinnedComments}}
|
||||
{{template "render_comment.html" $comment}}
|
||||
{{end}}
|
||||
{{range $comment := .Comments}}
|
||||
{{template "render_comment.html" $comment}}
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
<h2 class="reply-label">Add a Comment</h2>
|
||||
{{if (and (.CurrentUser.Username) (not .IsBlocked))}}
|
||||
<form id="reply-form" class="for-identified-user" method="post" action="/posts/{{.Post.ID}}/comments">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="{{.CurrentUser.CSRFToken}}">
|
||||
<div class="feeling-selector js-feeling-selector"><label class="symbol feeling-button feeling-button-normal checked"><input type="radio" name="feeling_id" value="0" checked><span class="symbol-label">normal</span></label><label class="symbol feeling-button feeling-button-happy"><input type="radio" name="feeling_id" value="1"><span class="symbol-label">happy</span></label><label class="symbol feeling-button feeling-button-like"><input type="radio" name="feeling_id" value="2"><span class="symbol-label">like</span></label><label class="symbol feeling-button feeling-button-surprised"><input type="radio" name="feeling_id" value="3"><span class="symbol-label">surprised</span></label><label class="symbol feeling-button feeling-button-frustrated"><input type="radio" name="feeling_id" value="4"><span class="symbol-label">frustrated</span></label><label class="symbol feeling-button feeling-button-puzzled"><input type="radio" name="feeling_id" value="5"><span class="symbol-label">puzzled</span></label></div>
|
||||
<div class="textarea-with-menu active-text">
|
||||
<menu class="textarea-menu">
|
||||
<li><label class="textarea-menu-text"><input type="radio" name="post_type" value="0"></label></li>
|
||||
<li><label class="textarea-menu-memo"><input type="radio" name="post_type" value="1"></label></li>
|
||||
<span class="character-count">2000</span>
|
||||
</menu>
|
||||
<div class="textarea-container">
|
||||
<textarea name="body" class="textarea-text textarea" maxlength="2000" placeholder="Add a comment here." data-open-folded-form data-required></textarea>
|
||||
</div>
|
||||
<div class="textarea-memo none">
|
||||
<div id="memo-drawboard-page" class="none">
|
||||
<div class="window-body">
|
||||
<div class="memo-buttons">
|
||||
<button type="button" class="artwork-clear"></button>
|
||||
<button type="button" class="artwork-undo"></button>
|
||||
<button type="button" class="artwork-pencil small selected"></button>
|
||||
<button type="button" class="artwork-eraser small"></button>
|
||||
<button type="button" class="artwork-fill"></button>
|
||||
<input type="text" class="artwork-color">
|
||||
<button type="button" class="artwork-zoom"></button>
|
||||
</div>
|
||||
<div class="memo-canvas">
|
||||
<canvas id="artwork-canvas" zoom="2"></canvas>
|
||||
<canvas id="artwork-canvas-undo"></canvas>
|
||||
<canvas id="artwork-canvas-redo"></canvas>
|
||||
<input type="hidden" name="painting">
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<input class="olv-modal-close-button black-button memo-finish-btn" type="button" value="Save">
|
||||
<button type="button" class="artwork-lock none"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="file-button-container">
|
||||
<span class="input-label">Attachment <span>Images, audio and videos are allowed.
|
||||
{{if .MaxUploadSize}}Maximum upload size: {{.MaxUploadSize}}{{end}}
|
||||
</span></span>
|
||||
<span class="button file-upload-button">Upload</span>
|
||||
<input accept="image/*, audio/*, video/*" type="file" class="file-button none">
|
||||
<input type="hidden" name="image">
|
||||
<input type="hidden" name="attachment_type">
|
||||
<div class="screenshot-container still-image preview-container" style="display: none;">
|
||||
<img class="preview-image none">
|
||||
<video class="preview-video none" controls></video>
|
||||
<audio class="preview-audio none" controls></audio>
|
||||
</div>
|
||||
<script src="/assets/js/upload.js"></script>
|
||||
</label>
|
||||
<div class="post-form-footer-options">
|
||||
<div class="post-form-footer-option-inner post-form-spoiler js-post-form-spoiler test-post-form-spoiler">
|
||||
<label class="spoiler-button symbol"><input type="checkbox" id="is_spoiler" name="is_spoiler" value="1">NSFW or spoiler</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="black-button reply-button disabled" value="Send" data-community-id="{{.Community.ID}}" data-url-id="{{.Post.ID}}" data-post-content-type="text" data-post-with-screenshot="nodata" disabled>
|
||||
</div>
|
||||
</form>
|
||||
{{else if .IsBlocked}}
|
||||
<div class="cannot-reply">
|
||||
<p>You cannot comment on this post.</p>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="guest-message">
|
||||
<p>You must sign in to post a comment.<br><br>Sign in with an SOP.epic account to connect to users around the world through games, topics and messages.</p>
|
||||
<a href="/signup" class="arrow-button"><span>Sign Up</span></a>
|
||||
<a href="/login" class="arrow-button"><span>Log In</span></a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pjax}}
|
||||
{{template "footer.html"}}
|
||||
{{end}}
|