aboutsummaryrefslogtreecommitdiff
path: root/juick-www/src
diff options
context:
space:
mode:
Diffstat (limited to 'juick-www/src')
-rw-r--r--juick-www/src/main/assets/embed.js336
-rw-r--r--juick-www/src/main/assets/logo.pngbin2447 -> 0 bytes
-rw-r--r--juick-www/src/main/assets/logo@2x.pngbin4822 -> 0 bytes
-rw-r--r--juick-www/src/main/assets/scripts.js802
-rw-r--r--juick-www/src/main/assets/style.css951
-rw-r--r--juick-www/src/main/java/com/juick/Application.java25
-rw-r--r--juick-www/src/main/java/com/juick/service/CloudflareCache.java55
-rw-r--r--juick-www/src/main/java/com/juick/service/cloudflare/FilesPayload.java20
-rw-r--r--juick-www/src/main/java/com/juick/www/HelpService.java69
-rw-r--r--juick-www/src/main/java/com/juick/www/WebApp.java71
-rw-r--r--juick-www/src/main/java/com/juick/www/configuration/EmbeddedAPIConfig.java19
-rw-r--r--juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java37
-rw-r--r--juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java140
-rw-r--r--juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java123
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/AnythingFilter.java64
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/AppSiteAssociation.java49
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/Help.java93
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/Login.java50
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java595
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/NewMessage.java263
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/Settings.java269
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/SignUp.java156
-rw-r--r--juick-www/src/main/resources/errors.properties3
-rw-r--r--juick-www/src/main/resources/errors_ru.properties3
m---------juick-www/src/main/resources/help0
-rw-r--r--juick-www/src/main/resources/messages.properties79
-rw-r--r--juick-www/src/main/resources/messages_ru.properties77
-rw-r--r--juick-www/src/main/resources/static/favicon.pngbin244 -> 0 bytes
-rw-r--r--juick-www/src/main/resources/static/logo.pngbin1184 -> 0 bytes
-rw-r--r--juick-www/src/main/resources/static/tagscloud.pngbin42316 -> 0 bytes
-rw-r--r--juick-www/src/main/resources/templates/layouts/content.html50
-rw-r--r--juick-www/src/main/resources/templates/layouts/default.html16
-rw-r--r--juick-www/src/main/resources/templates/layouts/minimal.html10
-rw-r--r--juick-www/src/main/resources/templates/views/404.html11
-rw-r--r--juick-www/src/main/resources/templates/views/blog.html25
-rw-r--r--juick-www/src/main/resources/templates/views/blog_tags.html10
-rw-r--r--juick-www/src/main/resources/templates/views/help.html10
-rw-r--r--juick-www/src/main/resources/templates/views/index.html29
-rw-r--r--juick-www/src/main/resources/templates/views/login.html144
-rw-r--r--juick-www/src/main/resources/templates/views/login_success.html13
-rw-r--r--juick-www/src/main/resources/templates/views/macros/tags.html5
-rw-r--r--juick-www/src/main/resources/templates/views/partial/footer.html16
-rw-r--r--juick-www/src/main/resources/templates/views/partial/homecolumn.html25
-rw-r--r--juick-www/src/main/resources/templates/views/partial/message.html76
-rw-r--r--juick-www/src/main/resources/templates/views/partial/navigation.html40
-rw-r--r--juick-www/src/main/resources/templates/views/partial/settings_tabs.html6
-rw-r--r--juick-www/src/main/resources/templates/views/partial/tagcolumn.html33
-rw-r--r--juick-www/src/main/resources/templates/views/partial/tags.html3
-rw-r--r--juick-www/src/main/resources/templates/views/partial/usercolumn.html89
-rw-r--r--juick-www/src/main/resources/templates/views/partial/usertags.html3
-rw-r--r--juick-www/src/main/resources/templates/views/pm_inbox.html35
-rw-r--r--juick-www/src/main/resources/templates/views/pm_sent.html33
-rw-r--r--juick-www/src/main/resources/templates/views/post.html19
-rw-r--r--juick-www/src/main/resources/templates/views/post_success.html19
-rw-r--r--juick-www/src/main/resources/templates/views/settings_about.html20
-rw-r--r--juick-www/src/main/resources/templates/views/settings_auth-email.html9
-rw-r--r--juick-www/src/main/resources/templates/views/settings_main.html151
-rw-r--r--juick-www/src/main/resources/templates/views/settings_password.html17
-rw-r--r--juick-www/src/main/resources/templates/views/settings_privacy.html9
-rw-r--r--juick-www/src/main/resources/templates/views/settings_result.html9
-rw-r--r--juick-www/src/main/resources/templates/views/signup.html41
-rw-r--r--juick-www/src/main/resources/templates/views/test.html2
-rw-r--r--juick-www/src/main/resources/templates/views/thread.html170
-rw-r--r--juick-www/src/main/resources/templates/views/users.html17
-rw-r--r--juick-www/src/test/java/com/juick/WebAppTests.java423
65 files changed, 0 insertions, 5937 deletions
diff --git a/juick-www/src/main/assets/embed.js b/juick-www/src/main/assets/embed.js
deleted file mode 100644
index 25c37142..00000000
--- a/juick-www/src/main/assets/embed.js
+++ /dev/null
@@ -1,336 +0,0 @@
-
-function insertAfter(newNode, referenceNode) {
- referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
-}
-
-function setContent(containerNode, ...newNodes) {
- removeAllFrom(containerNode);
- newNodes.forEach(n => containerNode.appendChild(n));
- return containerNode;
-}
-
-function removeAllFrom(fromNode) {
- for (let c; c = fromNode.lastChild; ) { fromNode.removeChild(c); }
-}
-
-function htmlEscape(html) {
- let textarea = document.createElement('textarea');
- textarea.textContent = html;
- return textarea.innerHTML;
-}
-
-// rules :: [{pr: number, re: RegExp, with: string}]
-// rules :: [{pr: number, re: RegExp, with: Function}]
-// rules :: [{pr: number, re: RegExp, brackets: true, with: [string, string]}]
-// rules :: [{pr: number, re: RegExp, brackets: true, with: [string, string, Function]}]
-function formatText(txt, rules) {
- let idCounter = 0;
- function nextId() { return idCounter++; }
- function ft(txt, rules) {
- let matches = rules.map(r => { r.re.lastIndex = 0; return [r, r.re.exec(txt)]; })
- .filter(([,m]) => m !== null)
- .sort(([r1,m1],[r2,m2]) => (r1.pr - r2.pr) || (m1.index - m2.index));
- if (matches && matches.length > 0) {
- let [rule, match] = matches[0];
- let subsequentRules = rules.filter(r => r.pr >= rule.pr);
- let idStr = `<>(${nextId()})<>`;
- let outerStr = txt.substring(0, match.index) + idStr + txt.substring(rule.re.lastIndex);
- let innerStr = (rule.brackets)
- ? (() => { let [l ,r ,f] = rule.with; return l + ft((f ? f(match[1]) : match[1]), subsequentRules) + r; })()
- : match[0].replace(rule.re, rule.with);
- return ft(outerStr, subsequentRules).replace(idStr, innerStr);
- }
- return txt;
- }
- return ft(htmlEscape(txt), rules); // idStr above relies on the fact the text is escaped
-}
-
-function fixWwwLink(url) {
- return url.replace(/^(?!([a-z]+:)?\/\/)/i, '//');
-}
-
-function makeNewNode(embedType, aNode, reResult) {
- const withClasses = el => {
- if (embedType.className) {
- el.classList.add(...embedType.className.split(' '));
- }
- return el;
- };
- return embedType.makeNode(aNode, reResult, withClasses(document.createElement('div')));
-}
-
-function makeIframe(src, w, h, scrolling='no') {
- let iframe = document.createElement('iframe');
- iframe.style.width = w;
- iframe.style.height = h;
- iframe.frameBorder = 0;
- iframe.scrolling = scrolling;
- iframe.setAttribute('allowFullScreen', '');
- iframe.src = src;
- iframe.innerHTML = 'Cannot show iframes.';
- return iframe;
-}
-
-function makeResizableToRatio(element, ratio) {
- element.dataset['ratio'] = ratio;
- makeResizable(element, w => w * element.dataset['ratio']);
-}
-
-// calcHeight :: Number -> Number -- calculate element height for a given width
-function makeResizable(element, calcHeight) {
- const setHeight = el => {
- if (document.body.contains(el) && (el.offsetWidth > 0)) {
- el.style.height = (calcHeight(el.offsetWidth)).toFixed(2) + 'px';
- }
- };
- window.addEventListener('resize', () => setHeight(element));
- setHeight(element);
-}
-
-function extractDomain(url) {
- const domainRe = /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/i;
- return domainRe.exec(url)[1];
-}
-
-function urlReplace(match, p1, p2, p3) {
- let isBrackets = (p1 !== undefined);
- return (isBrackets)
- ? `<a href="${fixWwwLink(p2 || p3)}">${p1}</a>`
- : `<a href="${fixWwwLink(match)}">${extractDomain(match)}</a>`;
-}
-
-function urlReplaceInCode(match, p1, p2, p3) {
- let isBrackets = (p1 !== undefined);
- return (isBrackets)
- ? `<a href="${fixWwwLink(p2 || p3)}">${match}</a>`
- : `<a href="${fixWwwLink(match)}">${match}</a>`;
-}
-
-function messageReplyReplace(messageId) {
- return function(match, mid, rid) {
- let replyPart = (rid && rid != '0') ? '#' + rid : '';
- return `<a href="/${mid || messageId}${replyPart}">${match}</a>`;
- };
-}
-
-/**
- * Given "txt" message in unescaped plaintext with Juick markup, this function
- * returns escaped formatted HTML string.
- *
- * @param {string} txt
- * @param {string} messageId - current message id
- * @param {boolean} isCode
- * @returns {string}
- */
-function juickFormat(txt, messageId, isCode) {
- const urlRe = /(?:\[([^\]\[]+)\](?:\[([^\]]+)\]|\(((?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[-\w+*&@#/%=~|$?!:;,.])*(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[\w+*&@#/%=~|$]))\))|\b(?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[-\w+*&@#/%=~|$?!:;,.])*(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[\w+*&@#/%=~|$]))/gi;
- const bqReplace = m => m.replace(/^(?:>|&gt;)\s?/gmi, '');
- return (isCode)
- ? formatText(txt, [
- { pr: 1, re: urlRe, with: urlReplaceInCode },
- { pr: 1, re: /\B(?:#(\d+))?(?:\/(\d+))?\b/g, with: messageReplyReplace(messageId) },
- { pr: 1, re: /\B@([\w-]+)\b/gi, with: '<a href="/$1">@$1</a>' },
- ])
- : formatText(txt, [
- { pr: 0, re: /((?:^(?:>|&gt;)\s?[\s\S]+?$\n?)+)/gmi, brackets: true, with: ['<q>', '</q>', bqReplace] },
- { pr: 1, re: urlRe, with: urlReplace },
- { pr: 1, re: /\B(?:#(\d+))?(?:\/(\d+))?\b/g, with: messageReplyReplace(messageId) },
- { pr: 1, re: /\B@([\w-]+)\b/gi, with: '<a href="/$1">@$1</a>' },
- { pr: 2, re: /\B\*([^\n]+?)\*((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<b>', '</b>'] },
- { pr: 2, re: /\B\/([^\n]+?)\/((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<i>', '</i>'] },
- { pr: 2, re: /\b\_([^\n]+?)\_((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<span class="u">', '</span>'] },
- { pr: 3, re: /\n/g, with: '<br/>' },
- ]);
-}
-
-function getEmbeddableLinkTypes() {
- return [
- {
- name: 'Jpeg and png images',
- id: 'embed_jpeg_and_png_images',
- className: 'picture compact',
- ctsDefault: false,
- re: /\.(jpe?g|png|svg)(:[a-zA-Z]+)?(?:\?[\w&;\?=]*)?$/i,
- makeNode: function(aNode, reResult, div) {
- div.innerHTML = `<a href="${aNode.href}"><img src="${aNode.href}"></a>`;
- return div;
- }
- },
- {
- name: 'Gif images',
- id: 'embed_gif_images',
- className: 'picture compact',
- ctsDefault: true,
- re: /\.gif(:[a-zA-Z]+)?(?:\?[\w&;\?=]*)?$/i,
- makeNode: function(aNode, reResult, div) {
- div.innerHTML = `<a href="${aNode.href}"><img src="${aNode.href}"></a>`;
- return div;
- }
- },
- {
- name: 'Video (webm, mp4, ogv)',
- id: 'embed_webm_and_mp4_videos',
- className: 'video compact',
- ctsDefault: false,
- re: /\.(webm|mp4|m4v|ogv)(?:\?[\w&;\?=]*)?$/i,
- makeNode: function(aNode, reResult, div) {
- div.innerHTML = `<video src="${aNode.href}" title="${aNode.href}" controls></video>`;
- return div;
- }
- },
- {
- name: 'Audio (mp3, ogg, weba, opus, m4a, oga, wav)',
- id: 'embed_sound_files',
- className: 'audio singleColumn',
- ctsDefault: false,
- re: /\.(mp3|ogg|weba|opus|m4a|oga|wav)(?:\?[\w&;\?=]*)?$/i,
- makeNode: function(aNode, reResult, div) {
- div.innerHTML = `<audio src="${aNode.href}" title="${aNode.href}" controls></audio>`;
- return div;
- }
- },
- {
- name: 'YouTube videos (and playlists)',
- id: 'embed_youtube_videos',
- className: 'youtube resizableV singleColumn',
- ctsDefault: false,
- re: /^(?:https?:)?\/\/(?:www\.|m\.|gaming\.)?(?:youtu(?:(?:\.be\/|be\.com\/(?:v|embed)\/)([-\w]+)|be\.com\/watch)((?:(?:\?|&(?:amp;)?)(?:\w+=[-\.\w]*[-\w]))*)|youtube\.com\/playlist\?list=([-\w]*)(&(amp;)?[-\w\?=]*)?)/i,
- makeNode: function(aNode, reResult, div) {
- let [url, v, args, plist] = reResult;
- let iframeUrl;
- if (plist) {
- iframeUrl = '//www.youtube-nocookie.com/embed/videoseries?list=' + plist;
- } else {
- let pp = {}; args.replace(/^\?/, '')
- .split('&')
- .map(s => s.split('='))
- .forEach(z => pp[z[0]] = z[1]);
- let embedArgs = { rel: '0' };
- if (pp.t) {
- const tre = /^(?:(\d+)|(?:(\d+)h)?(?:(\d+)m)?(\d+)s|(?:(\d+)h)?(\d+)m|(\d+)h)$/i;
- let [, t, h, m, s, h1, m1, h2] = tre.exec(pp.t);
- embedArgs['start'] = (+t) || ((+(h || h1 || h2 || 0))*60*60 + (+(m || m1 || 0))*60 + (+(s || 0)));
- }
- if (pp.list) {
- embedArgs['list'] = pp.list;
- }
- v = v || pp.v;
- let argsStr = Object.keys(embedArgs)
- .map(k => `${k}=${embedArgs[k]}`)
- .join('&');
- iframeUrl = `//www.youtube-nocookie.com/embed/${v}?${argsStr}`;
- }
- let iframe = makeIframe(iframeUrl, '100%', '360px');
- iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0);
- return setContent(div, iframe);
- }
- },
- {
- name: 'Vimeo videos',
- id: 'embed_vimeo_videos',
- className: 'vimeo resizableV',
- ctsDefault: false,
- re: /^(?:https?:)?\/\/(?:www\.)?(?:player\.)?vimeo\.com\/(?:video\/|album\/[\d]+\/video\/)?([\d]+)/i,
- makeNode: function(aNode, reResult, div) {
- let iframe = makeIframe('//player.vimeo.com/video/' + reResult[1], '100%', '360px');
- iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0);
- return setContent(div, iframe);
- }
- }
- ];
-}
-
-function embedLink(aNode, linkTypes, container, afterNode) {
- let anyEmbed = false;
- let linkId = (aNode.href.replace(/^https?:/i, '').replace(/\'/gi,''));
- let sameEmbed = container.querySelector(`*[data-linkid='${linkId}']`); // do not embed the same thing twice
- if (sameEmbed === null) {
- anyEmbed = [].some.call(linkTypes, function(linkType) {
- let reResult = linkType.re.exec(aNode.href);
- if (reResult) {
- if (linkType.match && (linkType.match(aNode, reResult) === false)) { return false; }
- let newNode = makeNewNode(linkType, aNode, reResult);
- if (!newNode) { return false; }
- newNode.setAttribute('data-linkid', linkId);
- if (afterNode) {
- insertAfter(newNode, afterNode);
- } else {
- container.appendChild(newNode);
- }
- aNode.classList.add('embedLink');
- return true;
- }
- });
- }
- return anyEmbed;
-}
-
-function embedLinks(aNodes, container) {
- let anyEmbed = false;
- let embeddableLinkTypes = getEmbeddableLinkTypes();
- Array.from(aNodes).forEach(aNode => {
- let isEmbedded = embedLink(aNode, embeddableLinkTypes, container);
- anyEmbed = anyEmbed || isEmbedded;
- });
- return anyEmbed;
-}
-
-/**
- * Embed all the links inside element "x" that match to "allLinksSelector".
- * All the embedded media is placed inside "div.embedContainer".
- * "div.embedContainer" is inserted before an element matched by "beforeNodeSelector"
- * if not present. Existing container is used otherwise.
- *
- * @param {Element} x
- * @param {string} beforeNodeSelector
- * @param {string} allLinksSelector
- */
-function embedLinksToX(x, beforeNodeSelector, allLinksSelector) {
- let isCtsPost = false;
- let allLinks = x.querySelectorAll(allLinksSelector);
-
- let existingContainer = x.querySelector('div.embedContainer');
- if (existingContainer) {
- embedLinks(allLinks, existingContainer);
- } else {
- let embedContainer = document.createElement('div');
- embedContainer.className = 'embedContainer';
-
- let anyEmbed = embedLinks(allLinks, embedContainer);
- if (anyEmbed) {
- let beforeNode = x.querySelector(beforeNodeSelector);
- x.insertBefore(embedContainer, beforeNode);
- }
- }
-}
-
-function embedLinksToArticles() {
- let beforeNodeSelector = 'nav.l';
- let allLinksSelector = 'p:not(.ir) a, pre a';
- Array.from(document.querySelectorAll('#content article')).forEach(article => {
- embedLinksToX(article, beforeNodeSelector, allLinksSelector);
- });
-}
-
-function embedLinksToPost() {
- let beforeNodeSelector = '.msg-txt + *';
- let allLinksSelector = '.msg-txt a';
- Array.from(document.querySelectorAll('#content .msg-cont')).forEach(msg => {
- embedLinksToX(msg, beforeNodeSelector, allLinksSelector);
- });
-}
-
-/**
- * Embed all the links in all messages/replies on the page.
- */
-function embedAll() {
- if (document.querySelector('#content article[data-mid]')) {
- embedLinksToArticles();
- } else {
- embedLinksToPost();
- }
-}
-
-exports.embedAll = embedAll;
-exports.embedLinksToX = embedLinksToX;
-exports.format = juickFormat;
diff --git a/juick-www/src/main/assets/logo.png b/juick-www/src/main/assets/logo.png
deleted file mode 100644
index 4e0f6d56..00000000
--- a/juick-www/src/main/assets/logo.png
+++ /dev/null
Binary files differ
diff --git a/juick-www/src/main/assets/logo@2x.png b/juick-www/src/main/assets/logo@2x.png
deleted file mode 100644
index 6febeaf9..00000000
--- a/juick-www/src/main/assets/logo@2x.png
+++ /dev/null
Binary files differ
diff --git a/juick-www/src/main/assets/scripts.js b/juick-www/src/main/assets/scripts.js
deleted file mode 100644
index 0d9a8846..00000000
--- a/juick-www/src/main/assets/scripts.js
+++ /dev/null
@@ -1,802 +0,0 @@
-require('whatwg-fetch');
-require('element-closest');
-require('classlist.js');
-require('url-search-params-polyfill');
-let Awesomplete = require('awesomplete');
-import * as killy from './embed';
-
-if (!('remove' in Element.prototype)) { // Firefox <23
- Element.prototype.remove = function () {
- if (this.parentNode) {
- this.parentNode.removeChild(this);
- }
- };
-}
-
-NodeList.prototype.forEach = Array.prototype.forEach;
-HTMLCollection.prototype.forEach = Array.prototype.forEach;
-
-NodeList.prototype.filter = Array.prototype.filter;
-HTMLCollection.prototype.filter = Array.prototype.filter;
-
-Element.prototype.selectText = function () {
- let d = document;
- if (d.body.createTextRange) {
- let range = d.body.createTextRange();
- range.moveToElementText(this);
- range.select();
- } else if (window.getSelection) {
- let selection = window.getSelection();
- let rangeSel = d.createRange();
- rangeSel.selectNodeContents(this);
- selection.removeAllRanges();
- selection.addRange(rangeSel);
- }
-};
-
-function autosize(el) {
- let offset = (!window.opera)
- ? (el.offsetHeight - el.clientHeight)
- : (el.offsetHeight + parseInt(window.getComputedStyle(el, null).getPropertyValue('border-top-width')));
-
- let resize = function (el) {
- el.style.height = 'auto';
- el.style.height = (el.scrollHeight + offset) + 'px';
- };
-
- if (el.addEventListener) {
- el.addEventListener('input', () => resize(el));
- } else if (el.attachEvent) {
- el.attachEvent('onkeyup', () => resize(el));
- }
-}
-
-function evilIcon(name) {
- return `<div class="icon icon--${name}"><svg class="icon__cnt"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#${name}-icon"></use></svg></div>`;
-}
-
-/* eslint-disable only-ascii/only-ascii */
-const translations = {
- 'en': {
- 'message.inReplyTo': 'in reply to',
- 'message.reply': 'Reply',
- 'message.likeThisMessage?': 'Recommend this message?',
- 'postForm.pleaseInputMessageText': 'Please input message text',
- 'postForm.upload': 'Upload',
- 'postForm.newMessage': 'New message...',
- 'postForm.imageLink': 'Link to image',
- 'postForm.imageFormats': 'JPG/PNG, up to 10 MB',
- 'postForm.or': 'or',
- 'postForm.tags': 'Tags (space separated)',
- 'postForm.submit': 'Send',
- 'comment.writeComment': 'Write a comment...',
- 'shareDialog.linkToMessage': 'Link to message',
- 'shareDialog.messageNumber': 'Message number',
- 'shareDialog.share': 'Share',
- 'loginDialog.pleaseIntroduceYourself': 'Please introduce yourself',
- 'loginDialog.registeredAlready': 'Registered already?',
- 'loginDialog.username': 'Username',
- 'loginDialog.password': 'Password',
- 'loginDialog.facebook': 'Login with Facebook',
- 'loginDialog.vk': 'Login with VK',
- 'error.error': 'Error'
- },
- 'ru': {
- 'message.inReplyTo': 'в ответ на',
- 'message.reply': 'Ответить',
- 'message.likeThisMessage?': 'Рекомендовать это сообщение?',
- 'postForm.pleaseInputMessageText': 'Пожалуйста, введите текст сообщения',
- 'postForm.upload': 'загрузить',
- 'postForm.newMessage': 'Новое сообщение...',
- 'postForm.imageLink': 'Ссылка на изображение',
- 'postForm.imageFormats': 'JPG/PNG, до 10Мб',
- 'postForm.or': 'или',
- 'postForm.tags': 'Теги (через пробел)',
- 'postForm.submit': 'Отправить',
- 'comment.writeComment': 'Написать комментарий...',
- 'shareDialog.linkToMessage': 'Ссылка на сообщение',
- 'shareDialog.messageNumber': 'Номер сообщения',
- 'shareDialog.share': 'Поделиться',
- 'loginDialog.pleaseIntroduceYourself': 'Пожалуйста, представьтесь',
- 'loginDialog.registeredAlready': 'Уже зарегистрированы?',
- 'loginDialog.username': 'Имя пользователя',
- 'loginDialog.password': 'Пароль',
- 'loginDialog.facebook': 'Войти через Facebook',
- 'loginDialog.vk': 'Войти через ВКонтакте',
- 'error.error': 'Ошибка'
- }
-};
-/* eslint-enable only-ascii/only-ascii */
-
-function getLang() {
- return (window.navigator.languages && window.navigator.languages[0])
- || window.navigator.userLanguage
- || window.navigator.language;
-}
-function i18n(key, lang = undefined) {
- const fallbackLang = 'ru';
- lang = lang || getLang().split('-')[0];
- return (translations[lang] && translations[lang][key])
- || translations[fallbackLang][key]
- || key;
-}
-
-var ws,
- pageTitle;
-
-function initWS() {
- let url = (window.location.protocol === 'https:' ? 'wss' : 'ws') + ':'
- + '//api.juick.com/ws/';
- let hash = document.getElementById('body').getAttribute('data-hash');
- if (hash) {
- url += '?hash=' + hash;
- } else {
- let content = document.getElementById('content');
- if (content) {
- let pageMID = content.getAttribute('data-mid');
- if (pageMID) {
- url += pageMID;
- }
- }
- }
-
- ws = new WebSocket(url);
- ws.onopen = function () {
- console.log('online');
- if (!document.querySelector('#wsthread')) {
- var d = document.createElement('div');
- d.id = 'wsthread';
- d.addEventListener('click', nextReply);
- document.querySelector('body').appendChild(d);
- pageTitle = document.title;
- }
- };
- ws.onclose = function () {
- console.log('offline');
- ws = false;
- setTimeout(function () {
- initWS();
- }, 2000);
- };
- ws.onmessage = function (msg) {
- if (msg.data == ' ') {
- ws.send(' ');
- } else {
- try {
- var jsonMsg = JSON.parse(msg.data);
- console.log('data: ' + msg.data);
- if (jsonMsg.service) {
- return;
- }
- wsIncomingReply(jsonMsg);
- } catch (err) {
- console.log(err);
- }
- }
- };
- setInterval(wsSendKeepAlive, 90000);
-}
-
-function wsSendKeepAlive() {
- if (ws) {
- ws.send(' ');
- }
-}
-
-function wsShutdown() {
- if (ws) {
- ws.onclose = function () { };
- ws.close();
- }
-}
-
-function wsIncomingReply(msg) {
- let content = document.getElementById('content');
- if (!content) { return; }
- let pageMID = content.getAttribute('data-mid');
- if (!pageMID || pageMID != msg.mid) { return; }
- let msgNum = '/' + msg.rid;
- if (msg.replyto > 0) {
- msgNum += ` ${i18n('message.inReplyTo')} <a href="#${msg.replyto}">/${msg.replyto}</a>`;
- }
- let photoDiv = (msg.attach == null) ? '' : `
- <div class="msg-media"><a href="//i.juick.com/p/${msg.mid}-${msg.rid}.${msg.attach}">
- <img src="//i.juick.com/photos-512/${msg.mid}-${msg.rid}.${msg.attach}"/></a>
- </div>`;
- let msgContHtml = `
- <div class="msg-cont">
- <div class="msg-header">
- <a href="/${msg.user.uname}/">${msg.user.uname}</a>:
- <div class="msg-avatar">
- <a href="/${msg.user.uname}/"><img src="//i.juick.com/a/${msg.user.uid}.png" alt="${msg.user.uname}"/></a>
- </div>
- <div class="msg-ts">
- <a href="/m/${msg.mid}#${msg.rid}" title="${msg.timestamp}GMT">${msg.timestamp}</a>
- </div>
- </div>
- <div class="msg-txt">${killy.format(msg.body, msg.mid, false)}</div>${photoDiv}
- <div class="msg-links">${msgNum} &middot; <a class="msg-reply-link" href="#">${i18n('message.reply')}</a></div>
- <div class="msg-comment-target msg-comment-hidden"></div>
- </div>`;
-
- let li = document.createElement('li');
- li.setAttribute('class', 'msg reply-new');
- li.setAttribute('id', msg.rid);
- li.innerHTML = msgContHtml;
- li.addEventListener('click', newReply);
- li.addEventListener('mouseover', newReply);
- li.querySelector('a.msg-reply-link').addEventListener('click', function (e) {
- showCommentForm(msg.mid, msg.rid);
- e.preventDefault();
- });
-
- killy.embedLinksToX(li.querySelector('.msg-cont'), '.msg-links', '.msg-txt a');
-
- document.getElementById('replies').appendChild(li);
-
- updateRepliesCounter();
-}
-
-function newReply(e) {
- var li = e.target;
- li.classList.remove('reply-new');
- li.removeEventListener('click', e);
- li.removeEventListener('mouseover', e);
- updateRepliesCounter();
-}
-
-function nextReply() {
- var li = document.querySelector('#replies>li.reply-new');
- if (li) {
- li.classList.remove('reply-new');
- li.removeEventListener('click', this);
- li.children[0].scrollIntoView();
- updateRepliesCounter();
- }
-}
-
-function updateRepliesCounter() {
- var replies = document.querySelectorAll('#replies>li.reply-new').length;
- var wsthread = document.getElementById('wsthread');
- if (replies) {
- wsthread.textContent = replies;
- wsthread.style.display = 'block';
- document.title = '[' + replies + '] ' + pageTitle;
- } else {
- wsthread.style.display = 'none';
- document.title = pageTitle;
- }
-}
-
-/******************************************************************************/
-/******************************************************************************/
-/******************************************************************************/
-
-function postformListener(formEl, ev) {
- if (ev.ctrlKey && (ev.keyCode == 10 || ev.keyCode == 13)) {
- let form = formEl.closest('form');
- if (!form.onsubmit || form.onsubmit()) {
- form.submit();
- }
- }
-}
-function closeDialogListener(ev) {
- ev = ev || window.event;
- if (ev.keyCode == 27) {
- closeDialog();
- }
-}
-
-function newMessage(evt) {
- document.querySelectorAll('#newmessage .dialogtxt').forEach(t => {
- t.remove();
- });
- if (document.querySelector('#newmessage textarea').value.length == 0
- && document.querySelector('#newmessage .img').value.length == 0
- && !document.querySelector('#newmessage input[type="file"]')) {
- document.querySelector('#newmessage').insertAdjacentHTML('afterbegin', `<p class="dialogtxt">${i18n('postForm.pleaseInputMessageText')}</p>`);
- evt.preventDefault();
- }
-}
-
-function showCommentForm(mid, rid) {
- let reply = document.getElementById(rid);
- let formTarget = reply.querySelector('div.msg-cont .msg-comment-target');
- if (formTarget) {
- let formHtml = `
- <form action="/comment" method="POST" enctype="multipart/form-data">
- <input type="hidden" name="mid" value="${mid}">
- <input type="hidden" name="rid" value="${rid}">
- <div class="msg-comment">
- <div class="ta-wrapper">
- <textarea name="body" rows="1" class="reply" placeholder="${i18n('comment.writeComment')}"></textarea>
- <div class="attach-photo">${evilIcon('ei-camera')}</div>
- </div>
- <input type="submit" value="OK">
- </div>
- </form>`;
- formTarget.insertAdjacentHTML('afterend', formHtml);
- formTarget.remove();
-
- let form = reply.querySelector('form');
- let submitButton = form.querySelector('input[type="submit"]');
-
- let attachButton = form.querySelector('.msg-comment .attach-photo');
- attachButton.addEventListener('click', e => attachCommentPhoto(e.target));
-
- let textarea = form.querySelector('.msg-comment textarea');
- textarea.addEventListener('keypress', e => postformListener(e.target, e));
- autosize(textarea);
-
- let validateMessage = () => {
- let len = textarea.value.length;
- if (len > 4096) { return 'Message is too long'; }
- return '';
- };
- form.addEventListener('submit', e => {
- let validationResult = validateMessage();
- if (validationResult) {
- e.preventDefault();
- alert(validationResult);
- return false;
- }
- submitButton.disabled = true;
- });
- }
- reply.querySelector('.msg-comment textarea').focus();
-}
-
-function attachInput() {
- let inp = document.createElement('input');
- inp.setAttribute('type', 'file');
- inp.setAttribute('name', 'attach');
- inp.setAttribute('accept', 'image/jpeg,image/png');
- inp.style.visibility = 'hidden';
- return inp;
-}
-
-function attachCommentPhoto(div) {
- let input = div.querySelector('input');
- if (input) {
- input.remove();
- div.classList.remove('attach-photo-active');
- } else {
- let newInput = attachInput();
- newInput.addEventListener('change', function () {
- div.classList.add('attach-photo-active');
- });
- newInput.click();
- div.appendChild(newInput);
- }
-}
-
-function attachMessagePhoto(div) {
- var f = div.closest('form'),
- finput = f.querySelector('input[type="file"]');
- if (!finput) {
- var inp = attachInput();
- inp.style.float = 'left';
- inp.style.width = 0;
- inp.style.height = 0;
- inp.addEventListener('change', function () {
- div.textContent = i18n('postForm.upload') + ' (✓)';
- });
- f.appendChild(inp);
- inp.click();
- } else {
- finput.remove();
- div.textContent = i18n('postForm.upload');
- }
-}
-
-function showMessageLinksDialog(mid, rid) {
- let hlink = window.location.protocol + '//juick.com/' + mid;
- let mlink = '#' + mid;
- if (rid > 0) {
- hlink += '#' + rid;
- mlink += '/' + rid;
- }
- let hlinkenc = encodeURIComponent(hlink);
- let html = `
- <div class="dialogshare">
- ${i18n('shareDialog.linkToMessage')}: <div onclick="this.selectText()" class="dialogl">${hlink}</div>
- ${i18n('shareDialog.messageNumber')}: <div onclick="this.selectText()" class="dialogl">${mlink}</div>
- ${i18n('shareDialog.share')}:
- <ul>
- <li><a href="https://www.facebook.com/sharer/sharer.php?u=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-facebook')}</a></li>
- <li><a href="https://twitter.com/intent/tweet?url=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-twitter')}</a></li>
- <li><a href="https://vk.com/share.php?url=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-vk')}</a></li>
- </ul>
- </div>`;
-
- openDialog(html);
-}
-
-function showPhotoDialog(fname) {
- let width = window.innerWidth;
- let height = window.innerHeight;
- let minDimension = (width < height) ? width : height;
- if (minDimension < 640) {
- return true; // no dialog, open the link
- } else if (minDimension < 1280) {
- openDialog(`<a href="//i.juick.com/p/${fname}"><img src="//i.juick.com/photos-1024/${fname}"/></a>`, true);
- return false;
- } else {
- openDialog(`<a href="//i.juick.com/p/${fname}"><img src="//i.juick.com/p/${fname}"/></a>`, true);
- return false;
- }
-}
-
-function openPostDialog() {
- let newmessageTemplate = `
- <form id="newmessage" action="/post" method="post" enctype="multipart/form-data">
- <textarea name="body" placeholder="${i18n('postForm.newMessage')}"></textarea>
- <div>
- <input class="img" name="img" placeholder="${i18n('postForm.imageLink')} (${i18n('postForm.imageFormats')})"/>
- ${i18n('postForm.or')} <a href="#">${i18n('postForm.upload')}</a><br/>
- <input id="tags_input" class="tags" name="tags" placeholder="${i18n('postForm.tags')}"/><br/>
- <input type="submit" class="subm" value="${i18n('postForm.submit')}"/>
- </div>
- </form>
- `;
- return openDialog(newmessageTemplate);
-}
-
-function openDialog(html, image) {
- var dialogHtml = `
- <div id="dialogt">
- <div id="dialogb"></div>
- <div id="dialogw">
- <div id="dialog_header">
- <div id="dialogc">${evilIcon('ei-close')}</div>
- </div>
- ${html}
- </div>
- </div>`;
- let body = document.querySelector('body');
- body.classList.add('dialog-opened');
- body.insertAdjacentHTML('afterbegin', dialogHtml);
- if (image) {
- let header = document.querySelector('#dialog_header');
- header.classList.add('header_image');
- }
- document.addEventListener('keydown', closeDialogListener);
- document.querySelector('#dialogb').addEventListener('click', closeDialog);
- document.querySelector('#dialogc').addEventListener('click', closeDialog);
-}
-
-function closeDialog() {
- let draft = document.querySelector('#newmessage textarea');
- if (draft) {
- window.draft = draft.value;
- }
- document.querySelector('body').classList.remove('dialog-opened');
- document.querySelector('#dialogb').remove();
- document.querySelector('#dialogt').remove();
-}
-
-function openSocialWindow(a) {
- var w = window.open(a.href, 'juickshare', 'width=640,height=400');
- if (window.focus) { w.focus(); }
- return false;
-}
-
-function checkUsername() {
- var uname = document.querySelector('#username').textContent,
- style = document.querySelector('#username').style;
- fetch('//api.juick.com/users?uname=' + uname)
- .then(function () {
- style.background = '#FFCCCC';
- })
- .catch(function () {
- style.background = '#CCFFCC';
- });
-}
-
-/******************************************************************************/
-
-function openDialogLogin() {
- let html = `
- <div class="dialoglogin">
- <p>${i18n('loginDialog.pleaseIntroduceYourself')}:</p>
- <a href="/_fblogin" id="signfb">${evilIcon('ei-sc-facebook')}${i18n('loginDialog.facebook')}</a>
- <a href="/_vklogin" id="signvk">${evilIcon('ei-sc-vk')}${i18n('loginDialog.vk')}</a>
- <p>${i18n('loginDialog.registeredAlready')}</p>
- <form action="/login" method="POST">
- <input class="signinput" type="text" name="username" placeholder="${i18n('loginDialog.username')}"/><br/>
- <input class="signinput" type="password" name="password" placeholder="${i18n('loginDialog.password')}"/><br/>
- <input class="signsubmit" type="submit" value="OK"/>
- </form>
- </div>`;
- openDialog(html);
- return false;
-}
-
-/******************************************************************************/
-
-function resultMessage(str) {
- var result = document.createElement('p');
- result.textContent = str;
- return result;
-}
-
-function likeMessage(e, mid) {
- if (confirm(i18n('message.likeThisMessage?'))) {
- fetch('//api.juick.com/like?mid=' + mid
- + '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
- method: 'POST',
- credentials: 'same-origin'
- })
- .then(function (response) {
- if (response.ok) {
- e.closest('article').appendChild(resultMessage('OK!'));
- }
- })
- .catch(function () {
- e.closest('article').appendChild(resultMessage(i18n('error.error')));
- });
- }
- return false;
-}
-
-/******************************************************************************/
-
-function setPopular(e, mid, popular) {
- fetch('//api.juick.com/messages/set_popular?mid=' + mid
- + '&popular=' + popular
- + '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
- credentials: 'same-origin'
- })
- .then(function () {
- e.closest('article').append(resultMessage('OK!'));
- });
- return false;
-}
-
-function setPrivacy(e, mid) {
- fetch('//api.juick.com/messages/set_privacy?mid=' + mid
- + '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
- credentials: 'same-origin'
- })
- .then(function () {
- e.closest('article').append(resultMessage('OK!'));
- });
- return false;
-}
-
-function getTags() {
- fetch('//api.juick.com/tags?hash=' + document.getElementById('body').getAttribute('data-hash'), {
- credentials: 'same-origin'
- })
- .then(response => {
- return response.json();
- })
- .then(json => {
- let tags = json.map(t => t.tag);
- let input = document.getElementById('tags_input');
- new Awesomplete(input, { list: tags });
- });
- return false;
-}
-
-function addTag(tag) {
- document.forms['postmsg'].body.value = '*' + tag + ' ' + document.forms['postmsg'].body.value;
- return false;
-}
-
-/******************************************************************************/
-
-function ready(fn) {
- if (document.readyState != 'loading') {
- fn();
- } else {
- document.addEventListener('DOMContentLoaded', fn);
- }
-}
-
-ready(function () {
- document.querySelectorAll('textarea').forEach((ta) => {
- autosize(ta);
- });
-
- var insertPMButtons = function (e) {
- e.target.classList.add('narrowpm');
- e.target.parentNode.insertAdjacentHTML('afterend', '<input type="submit" value="OK"/>');
- e.target.removeEventListener('click', insertPMButtons);
- e.preventDefault();
- };
- document.querySelectorAll('textarea.replypm').forEach(function (e) {
- e.addEventListener('click', insertPMButtons);
- e.addEventListener('keypress', function (e) {
- postformListener(e.target, e);
- });
- });
- document.querySelectorAll('#postmsg textarea').forEach(function (e) {
- e.addEventListener('keypress', function (e) {
- postformListener(e.target, e);
- });
- });
-
- var content = document.getElementById('content');
- if (content) {
- var pageMID = content.getAttribute('data-mid');
- if (pageMID > 0) {
- document.querySelectorAll('li.msg').forEach(li => {
- let showReplyFormBtn = li.querySelector('.a-thread-comment');
- if (showReplyFormBtn) {
- showReplyFormBtn.addEventListener('click', function (e) {
- showCommentForm(pageMID, li.id);
- e.preventDefault();
- });
- }
- });
- let opMessage = document.querySelector('.msgthread');
- if (opMessage) {
- let replyTextarea = opMessage.querySelector('textarea.reply');
- if (replyTextarea) {
- replyTextarea.addEventListener('focus', e => showCommentForm(pageMID, 0));
- replyTextarea.addEventListener('keypress', e => postformListener(e.target, e));
- if (!window.location.hash) {
- replyTextarea.focus();
- }
- }
- }
- }
- }
-
- var postmsg = document.getElementById('postmsg');
- if (postmsg) {
- document.querySelectorAll('a').filter(t => t.href.indexOf('?') >= 0).forEach(t => {
- t.addEventListener('click', e => {
- let params = new URLSearchParams(t.href.slice(t.href.indexOf('?') + 1));
- if (params.has('tag')) {
- addTag(params.get('tag'));
- e.preventDefault();
- }
- });
- });
- }
-
- document.querySelectorAll('.msg-menu').forEach(function (el) {
- el.addEventListener('click', function (e) {
- var reply = e.target.closest('li');
- var rid = reply ? parseInt(reply.id) : 0;
- var message = e.target.closest('section');
- var mid = message.getAttribute('data-mid') || e.target.closest('article').getAttribute('data-mid');
- showMessageLinksDialog(mid, rid);
- e.preventDefault();
- });
- });
- document.querySelectorAll('.l .a-privacy').forEach(function (e) {
- e.addEventListener('click', function (e) {
- setPrivacy(
- e.target,
- e.target.closest('article').getAttribute('data-mid'));
- e.preventDefault();
- });
- });
- document.querySelectorAll('.ir a[data-fname], .msg-media a[data-fname]').forEach(function (el) {
- el.addEventListener('click', function (e) {
- let fname = e.target.closest('[data-fname]').getAttribute('data-fname');
- if (!showPhotoDialog(fname)) {
- e.preventDefault();
- }
- });
- });
- document.querySelectorAll('.social a').forEach(function (e) {
- e.addEventListener('click', function (e) {
- openSocialWindow(e.target);
- e.preventDefault();
- });
- });
- var username = document.getElementById('username');
- if (username) {
- username.addEventListener('blur', function () {
- checkUsername();
- });
- }
-
- document.querySelectorAll('.l .a-like').forEach(function (e) {
- e.addEventListener('click', function (e) {
- likeMessage(
- e.target,
- e.target.closest('article').getAttribute('data-mid'));
- e.preventDefault();
- });
- });
- document.querySelectorAll('.a-login').forEach(function (el) {
- el.addEventListener('click', function (e) {
- openDialogLogin();
- e.preventDefault();
- });
- });
- var unfoldall = document.getElementById('unfoldall');
- if (unfoldall) {
- unfoldall.addEventListener('click', function (e) {
- document.querySelectorAll('#replies>li').forEach(function (e) {
- e.style.display = 'block';
- });
- document.querySelectorAll('#replies .msg-comments').forEach(function (e) {
- e.style.display = 'none';
- });
- e.preventDefault();
- });
- }
- document.querySelectorAll('article').forEach(function (article) {
- if (Array.prototype.some.call(
- article.querySelectorAll('.msg-tags a'),
- function (a) {
- return a.textContent === 'NSFW';
- }
- )) {
- article.classList.add('nsfw');
- }
- });
- initWS();
-
- window.addEventListener('pagehide', wsShutdown);
-
- killy.embedAll();
- var elSelector = 'header',
- elClassHidden = 'header--hidden',
- elClassBackground = 'header--background',
- throttleTimeout = 500,
- element = document.querySelector(elSelector);
-
- if (element) {
-
- var dHeight = 0,
- wHeight = 0,
- wScrollCurrent = 0,
- wScrollBefore = 0,
- wScrollDiff = 0,
-
- throttle = function (delay, fn) {
- var last, deferTimer;
- return function () {
- var context = this, args = arguments, now = +new Date;
- if (last && now < last + delay) {
- clearTimeout(deferTimer);
- deferTimer = setTimeout(
- function () {
- last = now;
- fn.apply(context, args);
- },
- delay);
- } else {
- last = now;
- fn.apply(context, args);
- }
- };
- };
-
- window.addEventListener('scroll', throttle(throttleTimeout, function () {
- dHeight = document.body.offsetHeight;
- wHeight = window.innerHeight;
- wScrollCurrent = window.pageYOffset;
- wScrollDiff = wScrollBefore - wScrollCurrent;
-
- if (wScrollCurrent <= 0) {
- // scrolled to the very top; element sticks to the top
- element.classList.remove(elClassHidden);
- element.classList.remove(elClassBackground);
- } else if (wScrollDiff > 0 && element.classList.contains(elClassHidden)) {
- // scrolled up; element slides in
- element.classList.remove(elClassHidden);
- element.classList.add(elClassBackground);
- } else if (wScrollDiff < 0) {
- // scrolled down
- if (wScrollCurrent + wHeight >= dHeight && element.classList.contains(elClassHidden)) {
- // scrolled to the very bottom; element slides in
- element.classList.remove(elClassHidden);
- element.classList.add(elClassBackground);
- } else {
- // scrolled down; element slides out
- element.classList.add(elClassHidden);
- }
- }
-
- wScrollBefore = wScrollCurrent;
- }));
- }
-});
diff --git a/juick-www/src/main/assets/style.css b/juick-www/src/main/assets/style.css
deleted file mode 100644
index e08a9a09..00000000
--- a/juick-www/src/main/assets/style.css
+++ /dev/null
@@ -1,951 +0,0 @@
-/* #region generic */
-
-html,
-body,
-div,
-h1,
-h2,
-ul,
-li,
-p,
-form,
-input,
-textarea,
-pre {
- margin: 0;
- padding: 0;
-}
-html,
-input,
-textarea {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
- font-size: 12pt;
- -webkit-font-smoothing: subpixel-antialiased;
-}
-h1,
-h2 {
- font-weight: normal;
-}
-ul {
- list-style-type: none;
-}
-a {
- color: #069;
- text-decoration: none;
-}
-img,
-hr {
- border: none;
-}
-hr {
- background: #CCC;
- height: 1px;
- margin: 10px 0;
-}
-pre {
- background: #222;
- color: #0f0;
- overflow-x: auto;
- padding: 6px;
- white-space: pre;
-}
-pre::selection {
- background: #0f0;
- color: #222;
-}
-pre::-moz-selection {
- background: #0f0;
- color: #222;
-}
-.u {
- text-decoration: underline;
-}
-
-/* #endregion */
-
-/* #region overall layout */
-
-html {
- background: #f8f8f8;
- color: #222;
-}
-#wrapper {
- margin: 0 auto;
- width: 1000px;
- margin-top: 52px;
-}
-#column {
- float: left;
- margin-left: 10px;
- overflow: hidden;
- padding-top: 10px;
- width: 240px;
-}
-#content {
- float: right;
- margin: 12px 0 0 0;
- width: 728px;
-}
-#minimal_content {
- margin: 0 auto;
- min-width: 310px;
- width: auto;
-}
-*::selection {
- background: #006699;
- color: #fff;
-}
-body > header {
- position: fixed;
- top: 0;
- width: 100%;
- z-index: 10;
- transition-duration: 0.5s;
- transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
- transition-property: transform;
-}
-@supports (backdrop-filter: blur(10px)) {
- body > header--background {
- background: rgba(255, 255, 255, 0.8);
- backdrop-filter: blur(10px);
- }
-}
-#header_wrapper {
- margin: 0 auto;
- width: 1000px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex-wrap: wrap;
- padding: 4px;
-}
-.header--background {
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.28);
- background: #fff;
-}
-.header--hidden {
- transform: translateY(-100%);
-}
-#footer {
- clear: both;
- color: #999;
- font-size: 10pt;
- margin: 40px;
- padding: 10px 0;
-}
-
-@media screen and (max-width: 850px) {
- body {
- text-size-adjust: 100%;
- }
- body,
- #wrapper,
- #topwrapper,
- #content,
- #footer {
- float: none;
- margin: 0 auto;
- min-width: 310px;
- width: auto;
- }
- #wrapper {
- margin-top: 50px;
- }
- body > header {
- margin-bottom: 15px;
- }
- #column {
- float: none;
- margin: 0 10px;
- padding-top: 0;
- width: auto;
- }
-}
-
-/* #endregion */
-
-/* #region header internals */
-
-#logo {
- height: 36px;
- width: 110px;
-}
-#logo a {
- background: url("logo@2x.png") no-repeat;
- background-size: cover;
- border: 0;
- display: block;
- height: 36px;
- overflow: hidden;
- text-indent: 100%;
- white-space: nowrap;
- width: 110px;
-}
-#global {
- display: flex;
-}
-#global a {
- color: #888;
- display: inline-block;
- font-size: 13pt;
- padding: 14px 6px;
-}
-#global li {
- display: inline-block;
-}
-#ctitle a {
- padding: 14px;
-}
-#global li:hover,
-#ctitle a:hover,
-.l a:hover {
- background-color: #fff;
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.16);
- cursor: pointer;
- transition: box-shadow 0.2s ease-in;
-}
-#search input {
- background: #FFF;
- border: 1px solid #ccc;
- outline: none !important;
- padding: 4px;
- -webkit-appearance: none;
- border-radius: 0;
-}
-
-/* #endregion */
-
-/* #region left column internals */
-
-.toolbar {
- border-top: 1px solid #CCC;
-}
-
-#column ul,
-#column p,
-#column hr {
- margin: 10px 0;
-}
-#column li > a {
- display: block;
- height: 100%;
- padding: 6px;
-}
-#column li > a:hover {
- background-color: #fff;
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.16);
- transition: background-color 0.2s ease-in;
-}
-#column .margtop {
- margin-top: 15px;
-}
-
-#column .tags {
- background: #fff;
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.16);
- line-height: 140%;
- padding: 6px;
- text-align: justify;
-}
-#column .inp {
- background: #fff;
- border: 1px solid #ddddd5;
- outline: none !important;
- padding: 4px;
- width: 222px;
-}
-#column .tags h4 {
- background: #eee;
- border: 1px solid #eee;
- color: #888;
- display: block;
- text-align: center;
-}
-#ctitle {
- font-size: 14pt;
-}
-#ctitle img {
- margin-right: 5px;
- vertical-align: middle;
- width: 48px;
-}
-#ustats li {
- font-size: 10pt;
- margin: 3px 0;
-}
-#column table.iread {
- width: 100%;
-}
-#column table.iread td {
- text-align: center;
-}
-#column table.iread img {
- height: 48px;
- width: 48px;
-}
-
-/* #endregion */
-
-/* #region main content */
-#content > p,
-#content > h1,
-#content > h2,
-#minimal_content > p,
-#minimal_content > h1,
-#minimal_content > h2 {
- margin: 1em 0;
-}
-.page {
- background: #eee;
- padding: 6px;
- text-align: center;
-}
-
-.page a {
- color: #888;
-}
-
-/* #endregion */
-
-/* #region article, message internals */
-
-article {
- background: #fff;
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.16);
- line-height: 140%;
- margin-bottom: 10px;
- padding: 20px;
-}
-article time {
- color: #999;
- font-size: 10pt;
-}
-article p {
- clear: left;
- margin: 5px 0 15px 0;
- word-wrap: break-word;
- overflow-wrap: break-word;
-}
-article .ir {
- text-align: center;
-}
-article .ir a {
- cursor: zoom-in;
- display: block;
-}
-article .ir img {
- max-width: 100%;
-}
-article > nav.l,
-.msg-cont > nav.l {
- border-top: 1px solid #eee;
- display: flex;
- justify-content: space-around;
- font-size: 10pt;
-}
-article > nav.l a,
-.msg-cont > nav.l a {
- color: #888;
- margin-right: 15px;
-}
-article .likes {
- padding-left: 20px;
-}
-article .replies {
- margin-left: 18px;
-}
-article .tags {
- margin-top: 3px;
-}
-.msg-tags {
- margin-top: 12px;
- min-height: 1px;
-}
-article .tags > a,
-.badge,
-.msg-tags > a {
- background: #eee;
- border: 1px solid #eee;
- color: #888;
- display: inline-block;
- font-size: 10pt;
- margin-bottom: 5px;
- margin-right: 5px;
- padding: 0 10px;
-}
-.l .msg-button {
- align-items: center;
- display: flex;
- flex-basis: 0;
- flex-direction: column;
- flex-grow: 1;
- padding-top: 12px;
-}
-.l .msg-button-icon {
- font-weight: bold;
-}
-.msgthread {
- margin-bottom: 0;
-}
-.msg-avatar {
- float: left;
- height: 48px;
- margin-right: 10px;
- width: 48px;
-}
-.msg-avatar img {
- height: 48px;
- vertical-align: top;
- width: 48px;
-}
-.msg-cont {
- background: #FFF;
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.16);
- line-height: 140%;
- margin-bottom: 12px;
- padding: 20px;
- width: 640px;
-}
-.reply-new .msg-cont {
- border-right: 5px solid #0C0;
-}
-.msg-ts {
- font-size: small;
- vertical-align: top;
-}
-.msg-ts,
-.msg-ts > a {
- color: #999;
-}
-.msg-txt {
- clear: both;
- margin: 0 0 12px;
- padding-top: 10px;
- word-wrap: break-word;
- overflow-wrap: break-word;
-}
-.msg-media {
- text-align: center;
-}
-.msg-links {
- color: #999;
- font-size: small;
- margin: 5px 0 0 0;
-}
-.msg-comments {
- color: #AAA;
- font-size: small;
- margin-top: 10px;
- overflow: hidden;
- text-indent: 10px;
-}
-.ta-wrapper {
- border: 1px solid #DDD;
- display: flex;
- flex-grow: 1;
-}
-.msg-comment {
- display: flex;
- width: 100%;
- margin-top: 10px;
-}
-.msg-comment-hidden {
- display: none;
-}
-.msg-comment textarea {
- border: 0;
- flex-grow: 1;
- outline: none !important;
- padding: 4px;
- resize: vertical;
- vertical-align: top;
-}
-.attach-photo {
- cursor: pointer;
-}
-.attach-photo-active {
- color: green;
-}
-.msg-comment input {
- align-self: flex-start;
- background: #EEE;
- border: 1px solid #CCC;
- color: #999;
- margin: 0 0 0 6px;
- position: -webkit-sticky;
- position: sticky;
- top: 70px;
- vertical-align: top;
- width: 50px;
-}
-.msg-recomms {
- color: #AAA;
- font-size: small;
- margin-top: 10px;
- overflow: hidden;
- text-indent: 10px;
-}
-#replies .msg-txt,
-#private-messages .msg-txt {
- margin: 0;
-}
-.title2 {
- background: #fff;
- margin: 20px 0;
- padding: 10px 20px;
- width: 640px;
-}
-.title2-right {
- float: right;
- line-height: 24px;
-}
-#content .title2 h2 {
- font-size: x-large;
- margin: 0;
-}
-
-@media screen and (max-width: 850px) {
- #header_wrapper {
- width: auto;
- }
- #global {
- justify-content: space-around;
- flex-grow: 1;
- }
- #search {
- padding: 4px;
- }
- article {
- overflow: auto;
- }
- article p {
- margin: 10px 0 8px 0;
- }
- .msg,
- .msg-cont {
- min-width: 280px;
- width: auto;
- }
- .msg-cont {
- margin: 8px 0;
- }
- .msg-media {
- overflow: auto;
- }
- .title2 h2 {
- font-size: large;
- }
- .msg-comment {
- flex-direction: column;
- }
- .msg-comment input {
- align-self: flex-end;
- margin: 6px 0 0 0;
- width: 100px;
- }
-}
-
-@media screen and (max-width: 480px) {
- #wrapper {
- margin-top: 104px;
- }
- #search {
- display: none;
- }
- #global a {
- padding: 14px 2px;
- font-size: 11pt;
- }
- .msg-cont > nav.l,
- article > nav.l {
- font-size: 9pt;
- }
- .msg-txt {
- padding-top: 5px;
- }
- .title2 {
- font-size: 11pt;
- width: auto;
- }
- #content .title2 h2 {
- font-size: 11pt;
- }
- .title2-right {
- line-height: initial;
- }
-}
-
-/* #endregion */
-
-/* #region user-generated texts */
-
-q:before,
-q:after {
- content: "";
-}
-q,
-blockquote {
- border-left: 3px solid #CCC;
- color: #666;
- display: block;
- margin: 10px 0 10px 10px;
- padding-left: 10px;
-}
-
-/* #endregion */
-
-/* #region new message form internals */
-
-#newmessage {
- background: #E5E5E0;
- margin-bottom: 20px;
- padding: 15px;
-}
-#newmessage textarea {
- border: 1px solid #CCC;
- box-sizing: border-box;
- margin: 0 0 5px 0;
- margin-top: 20px;
- max-height: 6em;
- min-width: 280px;
- padding: 4px;
- width: 100%;
-}
-#newmessage input {
- border: 1px solid #CCC;
- margin: 5px 0;
- padding: 2px 4px;
-}
-#newmessage .img {
- width: 500px;
-}
-#newmessage .tags {
- width: 500px;
-}
-#newmessage .subm {
- background: #EEEEE5;
- width: 150px;
-}
-@media screen and (max-width: 850px) {
- #newmessage .img,
- #newmessage .tags {
- width: 100%;
- }
-}
-
-/* #endregion */
-
-/* #region user lists */
-
-.users {
- margin: 10px 0;
- width: 100%;
- display: flex;
- flex-wrap: wrap;
-}
-.users > span {
- overflow: hidden;
- padding: 6px 0;
- width: 200px;
-}
-.users img {
- height: 32px;
- margin-right: 6px;
- vertical-align: middle;
- width: 32px;
-}
-
-/* #endregion */
-
-/* #region signup form */
-
-.signup-h1 > img {
- margin-right: 10px;
- vertical-align: middle;
-}
-.signup-h1 {
- font-size: x-large;
- margin: 20px 0 10px 0;
-}
-.signup-h2 {
- font-size: large;
- margin: 10px 0 5px 0;
-}
-.signup-hr {
- margin: 20px 0;
-}
-
-/* #endregion */
-
-/* #region PM */
-
-.newpm {
- margin: 20px 60px 30px 60px;
-}
-.newpm textarea {
- resize: vertical;
- width: 100%;
-}
-.newpm-send input {
- width: 100px;
-}
-
-/* #endregion */
-
-/* #region popup dialog (lightbox) */
-
-#dialogb {
- background: #222;
- height: 100%;
- left: 0;
- opacity: 0.6;
- position: fixed;
- top: 0;
- width: 100%;
- z-index: 10;
-}
-#dialogt {
- height: 100%;
- left: 0;
- position: fixed;
- top: 0;
- width: 100%;
- z-index: 10;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-#dialogw {
- z-index: 11;
- max-width: 96%;
- max-height: calc(100% - 100px);
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
-}
-#dialogw a {
- display: block;
-}
-#dialogw img {
- max-height: 100%;
- max-height: 90vh;
- max-width: 100%;
-}
-#dialog_header {
- width: 100%;
- height: 44px;
- position: fixed;
- display: flex;
- flex-direction: row-reverse;
- align-items: center;
-}
-.header_image {
- background: rgba(0, 0, 0, 0.28);
-}
-#dialogc {
- cursor: pointer;
- color: #ccc;
- padding-right: 6px;
-}
-.dialoglogin {
- background: #fff;
- padding: 25px;
- width: 300px;
-}
-.dialog-opened {
- overflow: hidden;
-}
-#signfb,
-#signvk {
- display: block;
- line-height: 32px;
- margin: 10px 0;
- text-decoration: none;
- width: 100%;
-}
-#signvk {
- margin-bottom: 30px;
-}
-.dialoglogin form {
- margin-top: 7px;
-}
-.signinput,
-.signsubmit {
- border: 1px solid #CCC;
- margin: 3px 0;
- padding: 3px;
-}
-.signinput {
- width: 292px;
-}
-.signsubmit {
- width: 70px;
-}
-.dialogshare {
- background: #fff;
- min-width: 300px;
- overflow: auto;
- padding: 20px;
-}
-.dialogl {
- background: #fff;
- border: 1px solid #DDD;
- margin: 3px 0 20px;
- padding: 5px;
-}
-.dialogshare li {
- float: left;
- margin: 5px 10px 0 0;
-}
-.dialogshare a {
- display: block;
-}
-.dialogtxt {
- background: #fff;
- padding: 20px;
-}
-
-@media screen and (max-width: 480px) {
- .dialog-opened {
- position: fixed;
- width: 100%;
- }
-}
-
-/* #endregion */
-
-/* #region misc */
-
-#wsthread {
- background: #CCC;
- bottom: 20px;
- cursor: pointer;
- display: none;
- padding: 5px 10px;
- position: fixed;
- right: 20px;
-}
-.sharenew {
- display: inline-block;
- line-height: 32px;
- min-height: 32px;
- min-width: 200px;
- padding: 0 12px 0 37px;
-}
-.icon {
- margin-top: -2px;
- vertical-align: middle;
-}
-.icon--ei-link {
- margin-top: -1px;
-}
-.icon--ei-comment {
- margin-top: -5px;
-}
-.newmessage {
- /* textarea on the /post page */
- border: 1px solid #DDD;
- padding: 2px;
- resize: vertical;
- width: 100%;
-}
-
-/* #endregion */
-
-/* #region footer internals */
-
-#footer-social {
- float: left;
-}
-#footer-social a {
- border: 0;
- display: inline-block;
-}
-#footer-left {
- margin-left: 286px;
- margin-right: 350px;
-}
-#footer-right {
- float: right;
-}
-
-@media screen and (max-width: 850px) {
- #footer {
- margin: 0 10px;
- }
- #footer div {
- float: none;
- margin: 10px 0;
- }
-}
-
-/* #endregion */
-
-/* #region settings */
-
-fieldset {
- border: 1px dotted #ccc;
- margin-top: 25px;
-}
-
-/* #endregion */
-
-/* #region embeds */
-
-.embedContainer {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- padding: 0;
- margin: 30px -3px 15px -3px;
-}
-.embedContainer > * {
- box-sizing: border-box;
- flex-grow: 1;
- margin: 3px;
- min-width: 49%;
-}
-.embedContainer > .compact {
- flex-grow: 0;
-}
-.embedContainer .picture img {
- display: block;
-}
-.embedContainer img,
-.embedContainer video {
- max-width: 100%;
- max-height: 80vh;
-}
-.embedContainer > .audio,
-.embedContainer > .youtube {
- min-width: 90%;
-}
-.embedContainer audio {
- width: 100%;
-}
-.embedContainer iframe {
- overflow: hidden;
- resize: vertical;
- display: block;
-}
-
-/* #endregion */
-
-/* #region nsfw */
-
-article.nsfw .embedContainer img,
-article.nsfw .embedContainer video,
-article.nsfw .embedContainer iframe,
-article.nsfw .ir img {
- opacity: 0.1;
-}
-article.nsfw .embedContainer img:hover,
-article.nsfw .embedContainer video:hover,
-article.nsfw .embedContainer iframe:hover,
-article.nsfw .ir img:hover {
- opacity: 1;
-}
-
-/* #endregion */
diff --git a/juick-www/src/main/java/com/juick/Application.java b/juick-www/src/main/java/com/juick/Application.java
deleted file mode 100644
index 47c16754..00000000
--- a/juick-www/src/main/java/com/juick/Application.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.juick;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.builder.SpringApplicationBuilder;
-import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.transaction.annotation.EnableTransactionManagement;
-
-@SpringBootApplication
-@EnableTransactionManagement
-@ComponentScan(basePackages = {"com.juick.www", "com.juick.service"})
-public class Application extends SpringBootServletInitializer {
-
- @Override
- protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
- setRegisterErrorPageFilter(false);
- return builder.sources(Application.class);
- }
-
- public static void main(String[] args) {
- SpringApplication.run(Application.class, args);
- }
-}
diff --git a/juick-www/src/main/java/com/juick/service/CloudflareCache.java b/juick-www/src/main/java/com/juick/service/CloudflareCache.java
deleted file mode 100644
index 64e048dc..00000000
--- a/juick-www/src/main/java/com/juick/service/CloudflareCache.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.juick.service;
-
-import com.juick.service.cloudflare.FilesPayload;
-import com.juick.User;
-import com.juick.server.component.UserUpdatedEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationListener;
-import org.springframework.http.*;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
-import org.springframework.web.client.RestTemplate;
-import org.springframework.web.util.UriComponents;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import java.util.Arrays;
-
-public class CloudflareCache implements ApplicationListener<UserUpdatedEvent> {
- private static final Logger logger = LoggerFactory.getLogger(CloudflareCache.class);
- @Value("${cloudflare_auth_email:email@example.com}")
- private String authEmail;
- @Value("${cloudflare_api_key:secret}")
- private String apiKey;
- @Value("${cloudflare_zone_id:zoneid}")
- private String zoneId;
- private final static String baseUri = "https://api.cloudflare.com/client/v4";
- @Override
- public void onApplicationEvent(UserUpdatedEvent event) {
- User user = event.getUser();
- logger.debug("Purging Cloudflare cache for {} avatar", user.getName());
- UriComponents uriComponents = UriComponentsBuilder.fromUriString("{baseUri}/zones/{zoneId}/purge_cache")
- .buildAndExpand(baseUri, zoneId);
- HttpHeaders httpHeaders = new HttpHeaders();
- httpHeaders.add("X-Auth-Email", authEmail);
- httpHeaders.add("X-Auth-Key", apiKey);
- httpHeaders.setContentType(MediaType.APPLICATION_JSON);
- FilesPayload payload = new FilesPayload();
- payload.setFiles(Arrays.asList(
- String.format("http://i.juick.com/a/%d.png", user.getUid()),
- String.format("http://i.juick.com/as/%d.png", user.getUid()),
- String.format("http://i.juick.com/a/%d.jpg", user.getUid()),
- String.format("http://i.juick.com/as/%d.jpg", user.getUid()),
- String.format("https://i.juick.com/a/%d.png", user.getUid()),
- String.format("https://i.juick.com/as/%d.png", user.getUid()),
- String.format("https://i.juick.com/a/%d.jpg", user.getUid()),
- String.format("https://i.juick.com/as/%d.jpg", user.getUid())
- ));
- RestTemplate api = new RestTemplate();
- api.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
- ResponseEntity<String> response = api.exchange(uriComponents.toUri(),
- HttpMethod.DELETE,
- new HttpEntity<>(payload, httpHeaders), String.class);
- logger.info("Cloudflare response: {}", response.getBody());
- }
-}
diff --git a/juick-www/src/main/java/com/juick/service/cloudflare/FilesPayload.java b/juick-www/src/main/java/com/juick/service/cloudflare/FilesPayload.java
deleted file mode 100644
index 35d9e72f..00000000
--- a/juick-www/src/main/java/com/juick/service/cloudflare/FilesPayload.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.juick.service.cloudflare;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class FilesPayload {
- private List<String> files;
-
- public FilesPayload() {
- files = new ArrayList<>();
- }
-
- public List<String> getFiles() {
- return files;
- }
-
- public void setFiles(List<String> files) {
- this.files = files;
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/HelpService.java b/juick-www/src/main/java/com/juick/www/HelpService.java
deleted file mode 100644
index 8e56916f..00000000
--- a/juick-www/src/main/java/com/juick/www/HelpService.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.www;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.cache.annotation.Cacheable;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.regex.Pattern;
-
-/**
- * Created by aalexeev on 12/11/16.
- */
-public class HelpService {
- private static final Pattern LANG_PATTERN = Pattern.compile("[a-z]{2}");
-
- private static final Pattern PAGE_PATTERN = Pattern.compile("[a-zA-Z0-9\\-_]+");
-
- private final String helpPath;
-
-
- public HelpService(String helpPath) {
- this.helpPath = helpPath;
- }
-
- @Cacheable("help")
- public String getHelp(final String page, final String lang) {
- if (canBePage(page) && canBeLang(lang)) {
- String path = StringUtils.joinWith("/", helpPath, lang, page + ".md");
-
- try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path)) {
- if (is != null)
- return IOUtils.toString(is, StandardCharsets.UTF_8);
- } catch (IOException e) {
- }
- }
- return null;
- }
-
- public boolean canBePage(final String anything) {
- return anything != null && PAGE_PATTERN.matcher(anything).matches();
- }
-
- public boolean canBeLang(final String anything) {
- return anything != null && LANG_PATTERN.matcher(anything).matches();
- }
-
- public String getHelpPath() {
- return helpPath;
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/WebApp.java b/juick-www/src/main/java/com/juick/www/WebApp.java
deleted file mode 100644
index 4e8b3a11..00000000
--- a/juick-www/src/main/java/com/juick/www/WebApp.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.www;
-
-import com.juick.Tag;
-import com.juick.service.TagService;
-import org.springframework.stereotype.Component;
-import org.springframework.web.servlet.resource.ResourceUrlProvider;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Stream;
-
-/**
- *
- * @author Ugnich Anton
- */
-@Component
-public class WebApp {
- @Inject
- private TagService tagService;
- @Inject
- private ResourceUrlProvider resourceUrlProvider;
-
- public List<Tag> parseTags(String tagsStr) {
- List<Tag> tags = new ArrayList<>();
- if (tagsStr != null && !tagsStr.isEmpty()) {
- Stream<String> tagsList = Arrays.stream(tagsStr.split("[ \\,]"))
- .distinct().map( t -> {
- if (t.startsWith("*")) {
- t = t.substring(1);
- }
- if (t.length() > 64) {
- t = t.substring(0, 64);
- }
- return t;
- });
- tags = tagService.getTags(tagsList, true);
- while (tags.size() > 5) {
- tags.remove(5);
- }
- }
- return tags;
- }
-
- public String getStyleUrl() {
- return resourceUrlProvider.getForLookupPath("/style.css");
- }
-
- public String getScriptsUrl() {
- return resourceUrlProvider.getForLookupPath("/scripts.js");
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/configuration/EmbeddedAPIConfig.java b/juick-www/src/main/java/com/juick/www/configuration/EmbeddedAPIConfig.java
deleted file mode 100644
index 0541ac25..00000000
--- a/juick-www/src/main/java/com/juick/www/configuration/EmbeddedAPIConfig.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.juick.www.configuration;
-
-import com.juick.server.configuration.PostConfig;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.builder.SpringApplicationBuilder;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@ConditionalOnClass(name = "com.juick.server.api.Post")
-public class EmbeddedAPIConfig {
- @Bean
- public ConfigurableApplicationContext apiContext() {
- return new SpringApplicationBuilder()
- .sources(PostConfig.class)
- .run("--server.port=8081", "--spring.jmx.enabled=false");
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java b/juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java
deleted file mode 100644
index 68ff28d2..00000000
--- a/juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.www.configuration;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import ru.sape.Sape;
-
-/**
- * Created by vitalyster on 29.03.2017.
- */
-@Configuration
-public class SapeConfiguration {
- @Value("${sape_user:secret}")
- private String token;
-
- @Bean
- public Sape sape() {
- return new Sape(token, "juick.com", 2000, 3600);
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java b/juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java
deleted file mode 100644
index b13d03f5..00000000
--- a/juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.www.configuration;
-
-import com.juick.service.UserService;
-import com.juick.service.security.HashParamAuthenticationFilter;
-import com.juick.service.security.JuickUserDetailsService;
-import com.juick.service.security.entities.JuickUser;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.builders.WebSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.core.userdetails.UserDetailsService;
-import org.springframework.security.web.authentication.RememberMeServices;
-import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
-import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
-
-import javax.annotation.Resource;
-
-/**
- * Created by aalexeev on 11/21/16.
- */
-@EnableWebSecurity
-public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
- @Value("${auth_remember_me_key:secret}")
- private String rememberMeKey;
- @Value("${web_domain:localhost}")
- private String webDomain;
- @Resource
- private UserService userService;
-
- private final String COOKIE_NAME = "juick-remember-me";
-
- @Bean("userDetailsService")
- @Override
- public UserDetailsService userDetailsServiceBean() throws Exception {
- return super.userDetailsServiceBean();
- }
-
- @Override
- public UserDetailsService userDetailsService() {
- return new JuickUserDetailsService(userService);
- }
-
- @Bean("authenticationManager")
- @Override
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.addFilterAfter(hashParamAuthenticationFilter(), BasicAuthenticationFilter.class);
- http
- .authorizeRequests()
- .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", "/comment")
- .authenticated()
- .anyRequest().permitAll()
- .and()
- .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY)
- .and()
- .sessionManagement().invalidSessionUrl("/")
- .and()
- .logout()
- .invalidateHttpSession(true)
- .logoutUrl("/logout")
- .logoutSuccessUrl("/login?logout")
- .deleteCookies("hash", COOKIE_NAME)
- .and()
- .formLogin()
- .loginPage("/login")
- .permitAll()
- .defaultSuccessUrl("/")
- .loginProcessingUrl("/login")
- .usernameParameter("username")
- .passwordParameter("password")
- .failureUrl("/login?error=1")
- .and()
- .rememberMe()
- .rememberMeCookieDomain(webDomain).key(rememberMeKey)
- .rememberMeServices(rememberMeServices())
- .and()
- .csrf().disable()
- .authenticationProvider(authenticationProvider())
- .headers().defaultsDisabled().cacheControl();
- }
-
- @Bean
- public DaoAuthenticationProvider authenticationProvider() throws Exception {
- DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
-
- authenticationProvider.setUserDetailsService(userDetailsService());
-
- return authenticationProvider;
- }
-
- @Bean
- public HashParamAuthenticationFilter hashParamAuthenticationFilter() throws Exception {
- return new HashParamAuthenticationFilter(userService, rememberMeServices());
- }
-
- @Bean
- public RememberMeServices rememberMeServices() throws Exception {
- TokenBasedRememberMeServices services = new TokenBasedRememberMeServices(
- rememberMeKey, userDetailsService());
-
- services.setCookieName(COOKIE_NAME);
- services.setCookieDomain(webDomain);
- services.setAlwaysRemember(true);
- services.setTokenValiditySeconds(6 * 30 * 24 * 3600);
- services.setUseSecureCookie(false); // TODO set true if https is supports
-
- return services;
- }
-
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.debug(false);
- web.ignoring().antMatchers("/style.css*", "/scripts.js*", "/h2-console/**", "/.well-known/**");
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java b/juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java
deleted file mode 100644
index edee3b87..00000000
--- a/juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.www.configuration;
-
-import com.juick.server.configuration.BaseWebConfiguration;
-import com.juick.server.configuration.StorageConfiguration;
-import com.juick.service.CloudflareCache;
-import com.juick.service.TagService;
-import com.juick.service.UserService;
-import com.juick.www.HelpService;
-import com.mitchellbosecke.pebble.PebbleEngine;
-import com.mitchellbosecke.pebble.extension.FormatterExtension;
-import com.mitchellbosecke.pebble.loader.ClasspathLoader;
-import com.mitchellbosecke.pebble.loader.Loader;
-import com.mitchellbosecke.pebble.spring4.PebbleViewResolver;
-import com.mitchellbosecke.pebble.spring4.extension.SpringExtension;
-import org.apache.commons.codec.CharEncoding;
-import org.commonmark.ext.autolink.AutolinkExtension;
-import org.commonmark.node.Link;
-import org.commonmark.parser.Parser;
-import org.commonmark.renderer.html.HtmlRenderer;
-import org.springframework.cache.annotation.EnableCaching;
-import org.springframework.cache.caffeine.CaffeineCacheManager;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
-import org.springframework.web.servlet.ViewResolver;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-import javax.inject.Inject;
-import java.util.Collections;
-
-/**
- * Created by aalexeev on 11/22/16.
- */
-@Configuration
-@EnableCaching
-@Import({ BaseWebConfiguration.class, WebSecurityConfig.class, SapeConfiguration.class,
- StorageConfiguration.class})
-public class WwwAppConfiguration implements WebMvcConfigurer {
- @Inject
- private UserService userService;
- @Inject
- private TagService tagService;
- @Bean
- public CaffeineCacheManager cacheManager() {
- return new CaffeineCacheManager("help");
- }
-
- @Bean
- public HelpService helpService() {
- return new HelpService("help");
- }
-
- @Bean
- public Parser cmParser() {
- return Parser.builder().extensions(Collections.singletonList(AutolinkExtension.create())).build();
- }
- @Bean
- public HtmlRenderer helpRenderer() {
- return HtmlRenderer.builder()
- .attributeProviderFactory(context -> (node, tagName, attributes) -> {
- if (node instanceof Link) {
- Link link = (Link) node;
- if (link.getDestination().startsWith("/")) {
- String destination = "/" + helpService().getHelpPath() + link.getDestination();
- link.setDestination(destination);
- attributes.put("href", destination);
- }
- }
- })
- .build();
- }
- @Bean
- public CloudflareCache cloudflareCache() {
- return new CloudflareCache();
- }
- @Bean
- public Loader templateLoader() {
- return new ClasspathLoader();
- }
-
- @Bean
- public SpringExtension springExtension() {
- return new SpringExtension();
- }
-
- @Bean
- public PebbleEngine pebbleEngine() {
- return new PebbleEngine.Builder()
- .loader(this.templateLoader())
- .extension(springExtension())
- .extension(new FormatterExtension())
- .strictVariables(true)
- .build();
- }
-
- @Bean
- public ViewResolver viewResolver() {
- PebbleViewResolver viewResolver = new PebbleViewResolver();
- viewResolver.setPrefix("templates");
- viewResolver.setSuffix(".html");
- viewResolver.setPebbleEngine(pebbleEngine());
- viewResolver.setCharacterEncoding(CharEncoding.UTF_8);
- return viewResolver;
- }
-
-}
diff --git a/juick-www/src/main/java/com/juick/www/controllers/AnythingFilter.java b/juick-www/src/main/java/com/juick/www/controllers/AnythingFilter.java
deleted file mode 100644
index d4519539..00000000
--- a/juick-www/src/main/java/com/juick/www/controllers/AnythingFilter.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.juick.www.controllers;
-
-import com.juick.server.util.WebUtils;
-import com.juick.service.MessagesService;
-import com.juick.service.UserService;
-import org.apache.commons.lang3.math.NumberUtils;
-import org.springframework.stereotype.Component;
-import org.springframework.web.filter.OncePerRequestFilter;
-import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
-import org.springframework.web.util.UriComponents;
-
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-@Component
-public class AnythingFilter extends OncePerRequestFilter {
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
-
- @Override
- public void doFilterInternal(@Nonnull HttpServletRequest servletRequest,
- @Nonnull HttpServletResponse servletResponse,
- @Nonnull FilterChain filterChain) throws IOException, ServletException {
- UriComponents components = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String anything = components.getPath().substring(1);
- int before = NumberUtils.toInt(components.getQueryParams().getFirst("before"), 0);
- if (before == 0) {
- boolean isPostNumber = WebUtils.isPostNumber(anything);
- int messageId = isPostNumber ?
- NumberUtils.toInt(anything) : 0;
-
- if (isPostNumber && anything.equals(Integer.toString(messageId))) {
- if (messageId > 0) {
- com.juick.User author = messagesService.getMessageAuthor(messageId);
-
- if (author != null) {
- servletResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
- servletResponse.setHeader("Location", "/" + author.getName() + "/" + anything);
- return;
- }
- }
- }
- com.juick.User user = userService.getUserByName(anything);
- if (user.getUid() > 0) {
- ((HttpServletResponse)servletResponse).sendRedirect("/" + user.getName() + "/");
- } else {
- filterChain.doFilter(servletRequest, servletResponse);
- }
- } else {
- com.juick.User user = userService.getUserByName(anything);
- if (!user.isAnonymous()) {
- ((HttpServletResponse) servletResponse).sendRedirect("/" + user.getName() + "/?before=" + before);
- } else {
- filterChain.doFilter(servletRequest, servletResponse);
- }
- }
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/controllers/AppSiteAssociation.java b/juick-www/src/main/java/com/juick/www/controllers/AppSiteAssociation.java
deleted file mode 100644
index dcf6c400..00000000
--- a/juick-www/src/main/java/com/juick/www/controllers/AppSiteAssociation.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.juick.www.controllers;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.Collections;
-import java.util.List;
-
-@RestController
-public class AppSiteAssociation {
- @Value("${ios_app_id:}")
- private String appId;
-
- @GetMapping("/.well-known/apple-app-site-association")
- @ResponseBody
- public SiteAssociations appSiteAssociations() {
- WebCredentials webCredentials = new WebCredentials();
- webCredentials.setApps(Collections.singletonList(appId));
- SiteAssociations siteAssociations = new SiteAssociations();
- siteAssociations.setWebcredentials(webCredentials);
- return siteAssociations;
- }
-
- private class SiteAssociations {
- private WebCredentials webcredentials;
-
- public WebCredentials getWebcredentials() {
- return webcredentials;
- }
-
- public void setWebcredentials(WebCredentials webcredentials) {
- this.webcredentials = webcredentials;
- }
- }
-
- private class WebCredentials {
- private List<String> apps;
-
- public List<String> getApps() {
- return apps;
- }
-
- public void setApps(List<String> apps) {
- this.apps = apps;
- }
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/controllers/Help.java b/juick-www/src/main/java/com/juick/www/controllers/Help.java
deleted file mode 100644
index 834cf1c1..00000000
--- a/juick-www/src/main/java/com/juick/www/controllers/Help.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.www.controllers;
-
-import com.juick.server.util.HttpNotFoundException;
-import com.juick.server.util.UserUtils;
-import com.juick.service.MessagesService;
-import com.juick.www.HelpService;
-import org.commonmark.parser.Parser;
-import org.commonmark.renderer.html.HtmlRenderer;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.Locale;
-import java.util.Objects;
-
-/**
- * Created by aalexeev on 11/21/16.
- */
-@Controller
-public class Help {
- @Inject
- private HelpService helpService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private Parser cmParser;
- @Inject
- private HtmlRenderer helpRenderer;
-
- @GetMapping({"/help/", "/help", "/help/{langOrPage}", "/help/{lang}/{page}"})
- public String showHelp(
- Locale locale,
- @PathVariable(required = false, name = "lang") String lang,
- @PathVariable(required = false, name = "page") String page,
- @PathVariable(required = false, name = "langOrPage") String langOrPage,
- Model model) throws IOException, URISyntaxException {
- com.juick.User visitor = UserUtils.getCurrentUser();
-
- String navigation = null;
-
- if (langOrPage != null) {
- if (helpService.canBeLang(langOrPage)) {
- navigation = helpService.getHelp("navigation", langOrPage);
- if (navigation != null)
- lang = langOrPage;
- }
-
- if (navigation == null && helpService.canBePage(langOrPage))
- page = langOrPage;
- }
-
- if (lang == null) {
- lang = locale.getLanguage();
- }
-
- String content = helpService.getHelp(page, lang);
- if (content == null && !Objects.equals("tos", page))
- content = helpService.getHelp("tos", lang);
-
- if (navigation == null)
- navigation = helpService.getHelp("navigation", lang);
-
- if (content == null || navigation == null)
- throw new HttpNotFoundException();
-
- model.addAttribute("navigation", helpRenderer.render(cmParser.parse(navigation)));
- model.addAttribute("content", helpRenderer.render(cmParser.parse(content)));
- model.addAttribute("visitor", visitor);
-
- return "views/help";
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/controllers/Login.java b/juick-www/src/main/java/com/juick/www/controllers/Login.java
deleted file mode 100644
index cb7df833..00000000
--- a/juick-www/src/main/java/com/juick/www/controllers/Login.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.www.controllers;
-
-import com.juick.server.util.UserUtils;
-import com.juick.service.UserService;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-
-import javax.inject.Inject;
-
-/**
- * @author Ugnich Anton
- */
-@Controller
-public class Login {
- @Inject
- private UserService userService;
-
- @GetMapping("/login")
- public String getloginForm(@RequestParam(required = false, defaultValue = "true") boolean redirect) {
- com.juick.User visitor = UserUtils.getCurrentUser();
-
- if (!visitor.isAnonymous()) {
- return redirect ? "redirect:/" : "redirect:/login/success";
- }
- return "views/login";
- }
- @GetMapping("/login/success")
- public String getSuccessLogin(ModelMap model) {
- model.addAttribute("hash", userService.getHashByUID(UserUtils.getCurrentUser().getUid()));
- return "views/login_success";
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java b/juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java
deleted file mode 100644
index f416cb86..00000000
--- a/juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.www.controllers;
-
-import com.juick.Message;
-import com.juick.Tag;
-import com.juick.formatters.PlainTextFormatter;
-import com.juick.server.component.MessageReadEvent;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.server.util.HttpNotFoundException;
-import com.juick.server.util.UserUtils;
-import com.juick.server.util.WebUtils;
-import com.juick.service.*;
-import com.juick.util.MessageUtils;
-import com.juick.www.Utils;
-import org.apache.commons.codec.CharEncoding;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.math.NumberUtils;
-import org.apache.commons.text.StringEscapeUtils;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
-import org.springframework.web.util.UriComponents;
-import ru.sape.Sape;
-
-import javax.inject.Inject;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- *
- * @author Ugnich Anton
- */
-@Controller
-public class MessagesWWW {
- @Inject
- private UserService userService;
- @Inject
- private TagService tagService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private Sape sape;
- @Inject
- private PMQueriesService pmQueriesService;
- @Inject
- private CrosspostService crosspostService;
- @Inject
- private ApplicationEventPublisher applicationEventPublisher;
-
- void fillUserModel(ModelMap model, com.juick.User user, com.juick.User visitor) {
- model.addAttribute("user", user);
- model.addAttribute("isSubscribed", userService.isSubscribed(visitor.getUid(), user.getUid()));
- model.addAttribute("isInBL", userService.isInBL(visitor.getUid(), user.getUid()));
- model.addAttribute("isInBLAny", userService.isInBLAny(user.getUid(), visitor.getUid()));
- model.addAttribute("statsIRead", userService.getStatsIRead(user.getUid()));
- model.addAttribute("statsMyReaders", userService.getStatsMyReaders(user.getUid()));
- model.addAttribute("statsMyBL", userService.getUserBLUsers(user.getUid()).size());
- model.addAttribute("statsMessages", userService.getStatsMessages(user.getUid()));
- model.addAttribute("statsReplies", userService.getStatsReplies(user.getUid()));
- model.addAttribute("iread", userService.getUserReadLeastPopular(user.getUid(), 8));
- model.addAttribute("tagStats", tagService.getUserTagStats(user.getUid())
- .stream().sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).limit(20).map(t -> t.getTag().getName()).collect(Collectors.toList()));
- }
-
- @GetMapping("/")
- protected String doGet(
- @RequestParam(required = false) String tag,
- @RequestParam(name = "show", required = false) String paramShow,
- @RequestParam(name = "search", required = false) String paramSearch,
- @RequestParam(name = "before", required = false, defaultValue = "0") Integer paramBefore,
- @RequestParam(name = "to", required = false, defaultValue = "0") Long paramTo,
- @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie,
- ModelMap model) throws IOException {
- if (tag != null) {
- return "redirect:/tag/" + URLEncoder.encode(tag, CharEncoding.UTF_8);
- }
- com.juick.User visitor = UserUtils.getCurrentUser();
-
- if (paramSearch != null && paramSearch.length() > 64) {
- paramSearch = null;
- }
-
- model.addAttribute("discover", false);
-
- String title;
- List<Integer> mids;
-
- if (paramSearch != null) {
- title = "Поиск: " + StringEscapeUtils.escapeHtml4(paramSearch);
- mids = messagesService.getSearch(Utils.encodeSphinx(paramSearch), paramBefore);
- } else if (paramShow == null) {
- if (!visitor.isAnonymous()) {
- title = "Популярные";
- mids = messagesService.getPopular(visitor.getUid(), paramBefore);
- model.addAttribute("discover", true);
- } else {
- title = "Микроблоги Juick: популярные записи";
- mids = messagesService.getPopular(0, paramBefore);
- }
-
- } else if (paramShow.equals("top")) {
- return "redirect:/";
- } else if (paramShow.equals("my") && !visitor.isAnonymous()) {
- title = "Моя лента";
- mids = messagesService.getMyFeed(visitor.getUid(), paramBefore, true);
- } else if (paramShow.equals("private") && !visitor.isAnonymous()) {
- title = "Приватные";
- mids = messagesService.getPrivate(visitor.getUid(), paramBefore);
- } else if (paramShow.equals("discuss") && !visitor.isAnonymous()) {
- title = "Обсуждения";
- mids = messagesService.getDiscussions(visitor.getUid(), paramTo);
- } else if (paramShow.equals("recommended") && !visitor.isAnonymous()) {
- title = "Рекомендации";
- mids = messagesService.getRecommended(visitor.getUid(), paramBefore);
- } else if (paramShow.equals("photos")) {
- title = "Фотографии";
- mids = messagesService.getPhotos(visitor.getUid(), paramBefore);
- model.addAttribute("discover", true);
- } else if (paramShow.equals("all")) {
- title = "Все сообщения";
- mids = messagesService.getAll(visitor.getUid(), paramBefore);
- model.addAttribute("discover", true);
- } else {
- throw new HttpNotFoundException();
- }
-
- String head = StringUtils.EMPTY;
- if (paramBefore > 0 || paramShow != null) {
- head = "<meta name=\"robots\" content=\"noindex\"/>";
- }
- model.addAttribute("title", title);
- model.addAttribute("headers", head);
- model.addAttribute("visitor", visitor);
- model.addAttribute("noindex", !(paramShow == null && paramBefore == 0));
- List<com.juick.Message> msgs = messagesService.getMessages(mids);
-
- if (!visitor.isAnonymous()) {
- fillUserModel(model, visitor, visitor);
- List<Integer> unread = messagesService.getUnread(visitor);
- visitor.setUnreadCount(unread.size());
- List<Integer> blUIDs = userService.checkBL(visitor.getUid(),
- msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList()));
- msgs.forEach(m -> {
- m.ReadOnly |= blUIDs.contains(m.getUser().getUid());
- m.setUnread(unread.contains(m.getMid()));
- });
- }
- model.addAttribute("msgs", msgs);
- model.addAttribute("tags", tagService.getPopularTags());
- model.addAttribute("headers", head);
- model.addAttribute("showAdv",
- paramShow == null && paramBefore == 0 && paramSearch == null && visitor.isAnonymous());
- if (mids.size() >= 20) {
- String nextpage = (paramShow != null && paramShow.equals("discuss")) ? "?to=" + msgs.get(msgs.size() - 1).getUpdated().toEpochMilli() : "?before=" + mids.get(mids.size() - 1);
- if (paramShow != null) {
- nextpage += "&amp;show=" + paramShow;
- }
- if (paramSearch != null) {
- nextpage += "&amp;search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8);
- }
- model.addAttribute("nextpage", nextpage);
- }
- UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String queryString = builder.getQuery();
- String requestURI = builder.toUri().getPath();
- if (sape != null && visitor.isAnonymous() && queryString == null) {
- String links = sape.getPageLinks(requestURI, sapeCookie).render();
- model.addAttribute("links", links);
- }
- return "views/index";
- }
-
- @GetMapping("/{uname}/")
- protected String doGetBlog(
- @RequestParam(required = false, name = "show") String paramShow,
- @RequestParam(required = false, name = "tag") String paramTagStr,
- @RequestParam(required = false, name = "search") String paramSearch,
- @PathVariable String uname,
- @RequestParam(required = false, defaultValue = "0") Integer before,
- @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie,
- ModelMap model) throws IOException {
- com.juick.User user = userService.getUserByName(uname);
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (user.isBanned() || user.isAnonymous()) {
- throw new HttpNotFoundException();
- }
-
- List<Integer> mids;
-
- com.juick.Tag paramTag = null;
- if (paramTagStr != null) {
- if (paramTagStr.length() < 64) {
- paramTag = tagService.getTag(paramTagStr, false);
- }
- if (paramTag == null) {
- throw new HttpNotFoundException();
- } else if (!paramTag.getName().equals(paramTagStr)) {
- String url = user.getName() + "/?tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8);
- return "redirect:/" + url;
- }
- }
- if (paramSearch != null && paramSearch.length() > 64) {
- paramSearch = null;
- }
-
- int privacy = 0;
- if (!visitor.isAnonymous()) {
- if (user.getUid() == visitor.getUid() || visitor.getUid() == 1) {
- privacy = -3;
- } else if (userService.isInWL(user.getUid(), visitor.getUid())) {
- privacy = -2;
- }
- }
-
- String title;
- if (paramShow == null) {
- if (paramTag != null) {
- title = "Блог " + user.getName() + ": *" + StringEscapeUtils.escapeHtml4(paramTag.getName());
- mids = messagesService.getUserTag(user.getUid(), paramTag.TID, privacy, before);
- } else if (paramSearch != null) {
- title = "Блог " + user.getName() + ": " + StringEscapeUtils.escapeHtml4(paramSearch);
- mids = messagesService.getUserSearch(user.getUid(), Utils.encodeSphinx(paramSearch), privacy, before);
- } else {
- title = "Блог " + user.getName();
- mids = messagesService.getUserBlog(user.getUid(), privacy, before);
- }
- } else if (paramShow.equals("recomm")) {
- title = "Рекомендации " + user.getName();
- mids = messagesService.getUserRecommendations(user.getUid(), before);
- } else if (paramShow.equals("photos")) {
- title = "Фотографии " + user.getName();
- mids = messagesService.getUserPhotos(user.getUid(), privacy, before);
- } else {
- throw new HttpNotFoundException();
- }
-
- String head = "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"@" +
- user.getName() + "\" href=\"//rss.juick.com/" + user.getName() + "/blog\"/>";
- if (paramTag != null && tagService.getTagNoIndex(paramTag.TID)) {
- head += "<meta name=\"robots\" content=\"noindex,nofollow\"/>";
- } else if (before > 0 || paramShow != null) {
- head += "<meta name=\"robots\" content=\"noindex\"/>";
- }
- model.addAttribute("pageUrl", "http://juick.com/" + user.getName());
- model.addAttribute("title", title);
- model.addAttribute("headers", head);
- model.addAttribute("visitor", visitor);
- model.addAttribute("noindex", paramShow == null && before == 0);
- fillUserModel(model, user, visitor);
- model.addAttribute("paramTag", paramTag);
- List<com.juick.Message> msgs = messagesService.getMessages(mids);
-
- if (!visitor.isAnonymous()) {
- List<Integer> unread = messagesService.getUnread(visitor);
- visitor.setUnreadCount(unread.size());
- List<Integer> blUIDs = userService.checkBL(visitor.getUid(),
- msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList()));
- msgs.forEach(m -> {
- m.ReadOnly |= blUIDs.contains(m.getUser().getUid());
- m.setUnread(unread.contains(m.getMid()));
- });
- }
- model.addAttribute("msgs", msgs);
- model.addAttribute("headers", head);
- model.addAttribute("showAdv",
- paramShow == null && before == 0 && paramSearch == null && visitor.getUid() == 0);
- if (mids.size() >= 20) {
- String nextpage = "?before=" + mids.get(mids.size() - 1);
- if (paramShow != null) {
- nextpage += "&amp;show=" + paramShow;
- }
- if (paramSearch != null) {
- nextpage += "&amp;search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8);
- }
- if (paramTag != null) {
- nextpage += "&amp;tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8);
- }
- model.addAttribute("nextpage", nextpage);
- }
- UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String queryString = builder.getQuery();
- String requestURI = builder.toUri().getPath();
- if (sape != null && visitor.isAnonymous() && queryString == null) {
- String links = sape.getPageLinks(requestURI, sapeCookie).render();
- model.addAttribute("links", links);
- }
- return "views/blog";
- }
-
- @GetMapping("/{uname}/tags")
- protected String doGetTags(@PathVariable String uname, ModelMap model) throws IOException {
- com.juick.User user = userService.getUserByName(uname);
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isBanned()) {
- throw new HttpNotFoundException();
- }
-
- model.addAttribute("title", "Теги " + user.getName());
- model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex,nofollow\"/>");
- model.addAttribute("visitor", visitor);
- fillUserModel(model, user, visitor);
- model.addAttribute("tags", tagService.getUserTagStats(user.getUid()).stream()
- .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList()));
-
- return "views/blog_tags";
- }
-
- @GetMapping("/{uname}/friends")
- protected String doGetFriends(@PathVariable String uname, ModelMap model) throws IOException {
- com.juick.User user = userService.getUserByName(uname);
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isBanned()) {
- throw new HttpNotFoundException();
- }
- model.addAttribute("title", "Подписки " + user.getName());
- model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>");
- model.addAttribute("visitor", visitor);
- fillUserModel(model, user, visitor);
- model.addAttribute("users", userService.getUserFriends(user.getUid()));
-
- return "views/users";
- }
-
- @GetMapping("/{uname}/readers")
- protected String doGetReaders(@PathVariable String uname, ModelMap model) throws IOException {
- com.juick.User user = userService.getUserByName(uname);
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isBanned()) {
- throw new HttpForbiddenException();
- }
- model.addAttribute("title", "Читатели " + user.getName());
- model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>");
- model.addAttribute("visitor", visitor);
- fillUserModel(model, user, visitor);
- model.addAttribute("users", userService.getUserReaders(user.getUid()));
-
- return "views/users";
- }
-
- @GetMapping("/{uname}/bl")
- protected String doGetBL(@PathVariable String uname, ModelMap model) throws IOException {
- com.juick.User user = userService.getUserByName(uname);
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isBanned() || visitor.getUid() != user.getUid()) {
- throw new HttpForbiddenException();
- }
- model.addAttribute("title", "Черный список " + user.getName());
- model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>");
- model.addAttribute("visitor", visitor);
- fillUserModel(model, user, visitor);
- model.addAttribute("users", userService.getUserBLUsers(user.getUid()));
-
- return "views/users";
- }
- @GetMapping("/tag/{tagName}")
- protected String tagAction(HttpServletRequest request,
- @PathVariable String tagName,
- @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie,
- @RequestParam(required = false, defaultValue = "0") int before,
- ModelMap model) throws IOException {
- com.juick.User visitor = UserUtils.getCurrentUser();
-
- String paramTagStr = StringEscapeUtils.unescapeHtml4(tagName);
- com.juick.Tag paramTag = tagService.getTag(paramTagStr, false);
- if (paramTag == null) {
- throw new HttpNotFoundException();
- } else if (paramTag.SynonymID > 0 && paramTag.TID != paramTag.SynonymID) {
- com.juick.Tag synTag = tagService.getTag(paramTag.SynonymID);
- String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(synTag.getName()), CharEncoding.UTF_8);
- if (request.getQueryString() != null) {
- url += "?" + request.getQueryString();
- }
- return "redirect:" + url;
- } else if (!paramTag.getName().equals(paramTagStr)) {
- String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(paramTag.getName()), CharEncoding.UTF_8);
- if (request.getQueryString() != null) {
- url += "?" + request.getQueryString();
- }
- return "redirect:" + url;
- }
-
- String title = "*" + StringEscapeUtils.escapeHtml4(paramTag.getName());
- model.addAttribute("title", title);
- List<Integer> mids = messagesService.getTag(paramTag.TID, visitor.getUid(), before, (visitor.isAnonymous()) ? 40 : 20);
- List<com.juick.Message> msgs = messagesService.getMessages(mids);
- if (!visitor.isAnonymous()) {
- List<Integer> unread = messagesService.getUnread(visitor);
- visitor.setUnreadCount(unread.size());
- List<Integer> blUIDs = userService.checkBL(
- visitor.getUid(),
- msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())
- );
- msgs.forEach(m -> {
- m.ReadOnly |= blUIDs.contains(m.getUser().getUid());
- m.setUnread(unread.contains(m.getMid()));
- });
- fillUserModel(model, visitor, visitor);
- }
-
- String head = StringUtils.EMPTY;
- if (tagService.getTagNoIndex(paramTag.TID)) {
- head = "<meta name=\"robots\" content=\"noindex,nofollow\"/>";
- } else if (before > 0 || mids.size() < 5) {
- head = "<meta name=\"robots\" content=\"noindex\"/>";
- }
- model.addAttribute("headers", head);
- model.addAttribute("visitor", visitor);
- model.addAttribute("tag", paramTag);
- model.addAttribute("title", title);
- model.addAttribute("msgs", msgs);
- model.addAttribute("tags", tagService.getPopularTags());
- model.addAttribute("noindex", before > 0);
- model.addAttribute("showAdv", before == 0 && visitor.isAnonymous());
- model.addAttribute("isSubscribed", tagService.isSubscribed(visitor, paramTag));
- model.addAttribute("isInBL", tagService.isInBL(visitor, paramTag));
- if (mids.size() >= 20) {
- String nextpage = "/tag/" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8) + "?before=" + mids.get(mids.size() - 1);
- model.addAttribute("nextpage", nextpage);
- }
- UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String queryString = builder.getQuery();
- String requestURI = builder.toUri().getPath();
- if (sape != null && visitor.isAnonymous() && queryString == null) {
- String links = sape.getPageLinks(requestURI, sapeCookie).render();
- model.addAttribute("links", links);
- }
- return "views/index";
- }
- @GetMapping("/pm/inbox")
- protected String doGetInbox(ModelMap model) {
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous()) {
- return "redirect:/login";
- }
- String title = "PM: Inbox";
- List<com.juick.Message> msgs = pmQueriesService.getLastPMInbox(visitor.getUid());
- fillUserModel(model, visitor, visitor);
- model.addAttribute("title", title);
- model.addAttribute("visitor", visitor);
- model.addAttribute("msgs", msgs);
- model.addAttribute("tags", tagService.getPopularTags());
- return "views/pm_inbox";
- }
-
- @GetMapping("/pm/sent")
- protected String doGetSent(@RequestParam(required = false) String uname,
- ModelMap model) {
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous()) {
- return "redirect:/login";
- }
- String title = "PM: Sent";
- List<com.juick.Message> msgs = pmQueriesService.getLastPMSent(visitor.getUid());
-
- if (WebUtils.isNotUserName(uname)) {
- uname = StringUtils.EMPTY;
- }
- fillUserModel(model, visitor, visitor);
- model.addAttribute("title", title);
- model.addAttribute("visitor", visitor);
- model.addAttribute("msgs", msgs);
- model.addAttribute("tags", tagService.getPopularTags());
- model.addAttribute("uname", uname);
- return "views/pm_sent";
- }
- @GetMapping("/{uname}/{mid}")
- protected String threadAction(ModelMap model,
- @PathVariable String uname,
- @PathVariable int mid,
- @CookieValue(name = "sape_cookie",
- required = false, defaultValue = StringUtils.EMPTY) String sapeCookie) {
- com.juick.User visitor = UserUtils.getCurrentUser();
-
- if (!messagesService.canViewThread(mid, visitor.getUid())) {
- throw new HttpForbiddenException();
- }
-
- com.juick.Message msg = messagesService.getMessage(mid);
-
- if (msg == null || msg.getUser().isBanned()) {
- throw new HttpNotFoundException();
- }
-
- com.juick.User user = userService.getUserByName(uname);
- if (user.isAnonymous() || !msg.getUser().equals(user)) {
- return String.format("redirect:/%s/%d", msg.getUser().getName(), mid);
- }
- msg.VisitorCanComment = !visitor.isAnonymous();
- List<com.juick.Message> replies = messagesService.getReplies(visitor, msg.getMid());
- // this should be after getReplies to mark thread as read
- fillUserModel(model, user, visitor);
- if (!visitor.isAnonymous()) {
- List<Integer> unread = messagesService.getUnread(visitor);
- visitor.setUnreadCount(unread.size());
- boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid();
- boolean isInBL = userService.isInBLAny(msg.getUser().getUid(), visitor.getUid());
- msg.VisitorCanComment = isMsgAuthor || !(msg.ReadOnly || isInBL);
- }
- model.addAttribute("msg", msg);
-
- String title = msg.getUser().getName() + ": " + MessageUtils.getTagsString(msg);
-
- model.addAttribute("title", title);
- model.addAttribute("visitor", visitor);
- String headers = "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"@" + msg.getUser().getName() + "\" href=\"//rss.juick.com/" + msg.getUser().getName() + "/blog\"/>";
- String pageUrl = "https://juick.com/" + msg.getUser().getName() + "/" + msg.getMid();
- if (msg.Hidden) {
- headers += "<meta name=\"robots\" content=\"noindex\"/>";
- }
- String cardType = StringUtils.isNotEmpty(msg.getAttachmentType()) ? "summary_large_image" : "summary";
- if (StringUtils.isNotEmpty(msg.getAttachmentType())) {
- // additional check in case of broken images
- if (msg.getAttachment() != null) {
- String msgImage = msg.getAttachment().getMedium().getUrl();
- headers += "<meta property=\"og:image\" content=\"" + msgImage + "\" />";
- }
- } else {
- String msgImage ="https://i.juick.com/a/" + msg.getUser().getUid() + ".png";
- headers += "<meta property=\"og:image\" content=\"" + msgImage + "\" />";
- }
- model.addAttribute("ogtype", "article");
- String cardDescription = StringEscapeUtils.escapeHtml4(PlainTextFormatter.formatTwitterCard(msg));
- headers += "<meta name=\"twitter:card\" content=\"" + cardType + "\" />\n" +
- "<meta name=\"twitter:site\" content=\"@juick\" />\n" +
- "<meta property=\"og:url\" content=\"" + pageUrl + "\" />\n" +
- "<meta property=\"og:title\" content=\"" + msg.getUser().getName() + " at Juick\" />\n" +
- "<meta property=\"og:description\" content=\"" + cardDescription + "\" />\n" +
- "<meta name=\"Description\" content=\"" + cardDescription + "\" />\n";
- String twitterName = crosspostService.getTwitterName(msg.getUser().getUid());
- if (StringUtils.isNotEmpty(twitterName)) {
- headers += "<meta name=\"twitter:creator\" content=\"@" + twitterName + "\" />\n";
- }
- if (msg.getTags().size() > 0) {
- headers += "<meta name=\"Keywords\" content=\"" + msg.getTags().stream().map(Tag::getName)
- .collect(Collectors.joining(", ")) + "\" />\n";
- }
- model.addAttribute("headers", headers);
- model.addAttribute("visitorSubscribed", messagesService.isSubscribed(visitor.getUid(), msg.getMid()));
- model.addAttribute("visitorInBL", userService.isInBL(msg.getUser().getUid(), visitor.getUid()));
- model.addAttribute("recomm", messagesService.getMessageRecommendations(msg.getMid()));
- List<Integer> blUIDs = new ArrayList<>();
- for (Message reply : replies) {
- if (reply.getUser().getUid() != msg.getUser().getUid()
- && !blUIDs.contains(reply.getUser().getUid())) {
- blUIDs.add(reply.getUser().getUid());
- }
- reply.VisitorCanComment = !visitor.isAnonymous();
- if (!visitor.isAnonymous()) {
- boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid();
- boolean isReplyAuthor = visitor.getUid() == reply.getUser().getUid();
- reply.VisitorCanComment = isMsgAuthor || (!msg.ReadOnly
- && msg.VisitorCanComment && (isReplyAuthor || !userService.isInBLAny(visitor.getUid(), reply.getUser().getUid())));
- }
- }
- model.addAttribute("replies", replies);
- model.addAttribute("showAdv", visitor.isAnonymous());
- UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String queryString = builder.getQuery();
- String requestURI = builder.toUri().getPath();
- if (sape != null && visitor.isAnonymous() && queryString == null) {
- String links = sape.getPageLinks(requestURI, sapeCookie).render();
- model.addAttribute("links", links);
- }
- return "views/thread";
- }
-
- // when message id is not fit to int
- @ExceptionHandler(NumberFormatException.class)
- public ResponseEntity<String> notFoundAction() {
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/controllers/NewMessage.java b/juick-www/src/main/java/com/juick/www/controllers/NewMessage.java
deleted file mode 100644
index a464add2..00000000
--- a/juick-www/src/main/java/com/juick/www/controllers/NewMessage.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.www.controllers;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.juick.Message;
-import com.juick.User;
-import com.juick.server.helpers.AnonymousUser;
-import com.juick.server.helpers.CommandResult;
-import com.juick.server.util.*;
-import com.juick.service.*;
-import com.juick.www.WebApp;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.text.StringEscapeUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.client.HttpClientErrorException;
-import org.springframework.web.client.RestTemplate;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URL;
-import java.util.stream.Collectors;
-
-/**
- * @author Ugnich Anton
- */
-@Controller
-public class NewMessage {
-
- @Inject
- private TagService tagService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private CrosspostService crosspostService;
- @Inject
- private PMQueriesService pmQueriesService;
- @Inject
- private WebApp webApp;
- @Inject
- private ObjectMapper jsonMapper;
- @Inject
- private ImagesService imagesService;
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgDir;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
- @Value("${api_url:http://localhost:8081}")
- private String apiUrl;
- private RestTemplate rest = new RestTemplate();
-
- private static final Logger logger = LoggerFactory.getLogger(NewMessage.class);
-
- @GetMapping("/post")
- protected String postAction(@RequestParam(required = false) String body, ModelMap model) {
- com.juick.User visitor = UserUtils.getCurrentUser();
- model.addAttribute("title", "Написать");
- model.addAttribute("headers", "");
- model.addAttribute("visitor", visitor);
- if (body == null) {
- body = StringUtils.EMPTY;
- } else {
- if (body.length() > 4096) {
- body = body.substring(0, 4096);
- }
- body = StringEscapeUtils.escapeHtml4(body);
- }
- model.addAttribute("body", body);
- model.addAttribute("visitor", visitor);
- model.addAttribute("tags", tagService.getUserTagStats(visitor.getUid()).stream()
- .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList()));
- return "views/post";
- }
-
- @PostMapping("/comment")
- public String doPostComment(
- @RequestParam Integer mid,
- @RequestParam(required = false, defaultValue = "0") Integer rid,
- @RequestParam(required = false, defaultValue = StringUtils.EMPTY) String body,
- @RequestParam(required = false, defaultValue = StringUtils.EMPTY) String img,
- @RequestParam(required = false) MultipartFile attach) throws IOException {
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous() || visitor.isBanned()) {
- throw new HttpForbiddenException();
- }
- com.juick.Message msg = messagesService.getMessage(mid);
- if (msg == null) {
- throw new HttpNotFoundException();
- }
-
- com.juick.Message reply = null;
- if (rid > 0) {
- reply = messagesService.getReply(mid, rid);
- if (reply == null) {
- throw new HttpNotFoundException();
- }
- }
-
- if ((StringUtils.isEmpty(body) || body.length() > 4096) && StringUtils.isEmpty(img) && attach == null) {
- throw new HttpBadRequestException();
- }
- body = body.replace("\r", StringUtils.EMPTY);
-
- if ((msg.ReadOnly && msg.getUser().getUid() != visitor.getUid())
- || userService.isInBLAny(msg.getUser().getUid(), visitor.getUid())
- || (reply != null && userService.isInBLAny(reply.getUser().getUid(), visitor.getUid()))) {
- throw new HttpForbiddenException();
- }
-
- URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
-
- if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) {
- try {
- URL imgUrl = new URL(img);
- attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir);
- } catch (Exception e) {
- logger.error("DOWNLOAD ERROR", e);
- throw new HttpBadRequestException();
- }
- }
-
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
- MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
- HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
-
-
- params.add("body", rid == 0 ? String.format("#%d %s", mid, body) : String.format("#%d/%d %s", mid, rid, body));
- params.add("hash", userService.getHashByUID(visitor.getUid()));
- if (StringUtils.isNotEmpty(attachmentFName.toString())) {
- params.add("img", attachmentFName.toASCIIString());
- }
- URI postUri = UriComponentsBuilder.fromUriString(apiUrl).path("/post").build().toUri();
- ResponseEntity<CommandResult> result = rest.postForEntity(
- postUri,
- request, CommandResult.class);
- logger.info("/comment: {}", jsonMapper.writeValueAsString(result.getBody()));
- return "redirect:/" + msg.getUser().getName() + "/" + mid + "#" + result.getBody().getNewMessage().get().getRid();
- }
-
- @PostMapping("/pm/send")
- public String doPostPM(@RequestParam(name = "uname", required = false) String unameParam,
- @RequestParam String body) throws IOException {
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous() || visitor.isBanned()) {
- throw new HttpForbiddenException();
- }
- String uname = unameParam;
- if (uname.startsWith("@")) {
- uname = uname.substring(1);
- }
- User userTo = AnonymousUser.INSTANCE;
- if (WebUtils.isUserName(uname)) {
- userTo = userService.getUserByName(uname);
- }
-
- if (userTo.isAnonymous() || body.length() > 10240) {
- throw new HttpBadRequestException();
- }
-
- if (userService.isInBLAny(userTo.getUid(), visitor.getUid())) {
- throw new HttpForbiddenException();
- }
-
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
- MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
- HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
-
- params.add("body", String.format("@%s %s", userTo.getName(), body));
- params.add("hash", userService.getHashByUID(visitor.getUid()));
- URI postUri = UriComponentsBuilder.fromUriString(apiUrl).path("/post").build().toUri();
- ResponseEntity<CommandResult> result = rest.postForEntity(
- postUri,
- request, CommandResult.class);
- logger.info("/pm: {}", jsonMapper.writeValueAsString(result.getBody()));
- return "redirect:/pm/sent";
-
- }
- @PostMapping("/post2")
- public String doPostMessage(@RequestParam(name = "body", required = false) String bodyParam,
- @RequestParam(required = false) String img,
- @RequestParam(required = false) MultipartFile attach) throws JsonProcessingException {
-
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous() || visitor.isBanned()) {
- throw new HttpForbiddenException();
- }
- String body = StringUtils.isNotEmpty(bodyParam) ? bodyParam.replace("\r", StringUtils.EMPTY) : StringUtils.EMPTY;
-
- URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
-
- if (StringUtils.isBlank(attachmentFName.toString()) && StringUtils.isNotBlank(img)) {
- try {
- URL imgUrl = new URL(img);
- attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir);
- } catch (Exception e) {
- logger.error("DOWNLOAD ERROR", e);
- throw new HttpBadRequestException();
- }
- }
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
- MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
- HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
-
- params.add("body", body);
- params.add("hash", userService.getHashByUID(visitor.getUid()));
- if (StringUtils.isNotEmpty(attachmentFName.toString())) {
- params.add("img", attachmentFName.toASCIIString());
- }
- URI postUri = UriComponentsBuilder.fromUriString(apiUrl).path("/post").build().toUri();
- try {
- ResponseEntity<CommandResult> result = rest.postForEntity(postUri,
- request, CommandResult.class);
- Message newMessage = result.getBody().getNewMessage().orElse(new Message());
- if (newMessage.getMid() > 0) {
- logger.info("/post: {}", jsonMapper.writeValueAsString(result.getBody()));
- return String.format("redirect:/%d", newMessage.getMid());
- } else {
- logger.info("{} : {}", body, result.getBody().getText());
- }
- } catch (HttpClientErrorException e) {
- logger.error("post error", e);
- }
- return "redirect:/?show=my";
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/controllers/Settings.java b/juick-www/src/main/java/com/juick/www/controllers/Settings.java
deleted file mode 100644
index f9527423..00000000
--- a/juick-www/src/main/java/com/juick/www/controllers/Settings.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.www.controllers;
-
-import com.juick.server.component.UserUpdatedEvent;
-import com.juick.server.helpers.NotifyOpts;
-import com.juick.server.helpers.UserInfo;
-import com.juick.server.util.*;
-import com.juick.service.*;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.inject.Inject;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.Session;
-import javax.mail.Transport;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-/**
- *
- * @author Ugnich Anton
- */
-@Controller
-public class Settings {
- private static final Logger logger = LoggerFactory.getLogger(Settings.class);
-
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgDir;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
- @Inject
- private TagService tagService;
- @Inject
- private UserService userService;
- @Inject
- private CrosspostService crosspostService;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private EmailService emailService;
- @Inject
- private TelegramService telegramService;
- @Inject
- private ApplicationEventPublisher applicationEventPublisher;
- @Inject
- private ImagesService imagesService;
-
- @GetMapping("/settings")
- protected String doGet(HttpServletRequest request, HttpServletResponse response, ModelMap model) throws IOException {
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous()) {
- response.sendRedirect("/login");
- }
- List<String> pages = Arrays.asList("main", "password", "about", "auth-email", "privacy");
- String page = request.getParameter("page");
- if (StringUtils.isEmpty(page) || !pages.contains(page)) {
- page = "main";
- }
-
- model.addAttribute("title", "Настройки");
- model.addAttribute("visitor", visitor);
- model.addAttribute("tags", tagService.getPopularTags());
- model.addAttribute("auths", userService.getAuthCodes(visitor));
- model.addAttribute("email_active", emailService.getNotificationsEmail(visitor.getUid()));
- model.addAttribute("ehash", userService.getEmailHash(visitor));
- model.addAttribute("emails", userService.getEmails(visitor));
- model.addAttribute("jids", userService.getAllJIDs(visitor));
- List<String> hours = IntStream.rangeClosed(0, 23).boxed()
- .map(i -> StringUtils.leftPad(String.format("%d", i), 2, "0")).collect(Collectors.toList());
- model.addAttribute("hours", hours);
- model.addAttribute("fbstatus", crosspostService.getFbCrossPostStatus(visitor.getUid()));
- model.addAttribute("twitter_name", crosspostService.getTwitterName(visitor.getUid()));
- model.addAttribute("telegram_name", crosspostService.getTelegramName(visitor.getUid()));
- model.addAttribute("notify_options", subscriptionService.getNotifyOptions(visitor));
- model.addAttribute("userinfo", userService.getUserInfo(visitor));
- if (page.equals("auth-email")) {
- if (emailService.verifyAddressByCode(visitor.getUid(), request.getParameter("code"))) {
- ;
- model.addAttribute("result", "OK!");
- } else {
- model.addAttribute("result", "Sorry, code unknown.");
- }
- }
- return String.format("views/settings_%s", page);
- }
-
- @PostMapping("/settings")
- protected String doPost(HttpServletRequest request, HttpServletResponse response,
- @RequestParam(required = false) MultipartFile avatar,
- ModelMap model)
- throws IOException {
- com.juick.User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous()) {
- throw new HttpBadRequestException();
- }
- List<String> pages = Arrays.asList("main", "password", "about", "email", "email-add", "email-del",
- "email-subscr", "auth-email", "privacy", "jid-del", "twitter-del", "telegram-del", "facebook-disable",
- "facebook-enable", "vk-del");
- String page = request.getParameter("page");
- if (StringUtils.isEmpty(page) || !pages.contains(page)) {
- throw new HttpBadRequestException();
- }
- String result = StringUtils.EMPTY;
- switch (page) {
- case "password":
- if (userService.updatePassword(visitor, request.getParameter("password"))) {
- result = "<p>Password has been changed.</p>";
- String hash = userService.getHashByUID(visitor.getUid());
- Cookie c = new Cookie("hash", hash);
- c.setMaxAge(365 * 24 * 60 * 60);
- response.addCookie(c);
- }
- break;
- case "main":
- NotifyOpts opts = new NotifyOpts();
- opts.setRepliesEnabled(StringUtils.isNotEmpty(request.getParameter("jnotify")));
- opts.setSubscriptionsEnabled(StringUtils.isNotEmpty(request.getParameter("subscr_notify")));
- opts.setRecommendationsEnabled(StringUtils.isNotEmpty(request.getParameter("recomm")));
- if (subscriptionService.setNotifyOptions(visitor, opts)) {
- result = "<p>Notification options has been updated</p>";
- }
- break;
- case "about":
- UserInfo info = new UserInfo();
- info.setFullName(request.getParameter("fullname"));
- info.setCountry(request.getParameter("country"));
- info.setUrl(request.getParameter("url"));
- info.setDescription(request.getParameter("descr"));
- String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, tmpDir).getHost();
- if (StringUtils.isNotEmpty(avatarTmpPath)) {
- imagesService.saveAvatar(avatarTmpPath, visitor.getUid());
- }
- if (userService.updateUserInfo(visitor, info)) {
- applicationEventPublisher.publishEvent(new UserUpdatedEvent(this, visitor));
- result = String.format("<p>Your info is updated.</p><p><a href='/%s/'>Back to blog</a>.</p>", visitor.getName());
- }
- break;
- case "jid-del":
- // FIXME: stop using ugnich-csv in parameters
- String[] params = request.getParameter("delete").split(";", 2);
- boolean res = false;
- if (params[0].equals("xmpp")) {
- res = userService.deleteJID(visitor.getUid(), params[1]);
- } else if (params[0].equals("xmpp-unauth")) {
- res = userService.unauthJID(visitor.getUid(), params[1]);
- }
- if (res) {
- result = "<p>Deleted. <a href=\"/settings\">Back</a>.</p>";
- } else {
- result = "<p>Error</p>";
- }
- break;
- case "email-add":
- if (!emailService.verifyAddressByCode(visitor.getUid(), request.getParameter("account"))) {
- String authCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase();
- if (emailService.addVerificationCode(visitor.getUid(), request.getParameter("account"), authCode)) {
- Session session = Session.getDefaultInstance(System.getProperties());
- try {
- MimeMessage message = new MimeMessage(session);
- message.setFrom(new InternetAddress("noreply@mail.juick.com"));
- message.addRecipient(Message.RecipientType.TO, new InternetAddress(request.getParameter("account")));
- message.setSubject("Juick authorization link");
- message.setText(String.format("Follow link to attach this email to Juick account:\n" +
- "http://juick.com/settings?page=auth-email&code=%s\n\n" +
- "If you don't know, what this mean - just ignore this mail.\n", authCode));
- Transport.send(message);
- result = "<p>Authorization link has been sent to your email. Follow it to proceed.</p>" +
- "<p><a href=\"/settings\">Back</a></p>";
-
- } catch (MessagingException ex) {
- logger.error("mail exception", ex);
- throw new HttpBadRequestException();
- }
- }
- }
- break;
- case "email-del":
- if (emailService.deleteEmail(visitor.getUid(), request.getParameter("account"))) {
- result = "<p>Deleted. <a href=\"/settings\">Back</a>.</p>";
- } else {
- result = "<p>An error occured while deleting.</p>";
- }
- break;
- case "email-subscr":
- if (emailService.setNotificationsEmail(visitor.getUid(), request.getParameter("account"))) {
- result = String.format("<p>Saved! Will send notifications to <strong>%s</strong>." +
- "</p><p><a href=\"/settings\">Back</a></p>", request.getParameter("account"));
- } else {
- result = "<p>Disabled.</p><p><a href=\"/settings\">Back</a></p>";
- }
- break;
- case "twitter-del":
- crosspostService.deleteTwitterToken(visitor.getUid());
- for (Cookie cookie : request.getCookies()) {
- if (cookie.getName().equals("request_token")) {
- cookie.setMaxAge(0);
- response.addCookie(cookie);
- }
- if (cookie.getName().equals("request_token_secret")) {
- cookie.setMaxAge(0);
- response.addCookie(cookie);
- }
- }
- result = "<p><a href=\"/settings\">Back</a></p>";
- break;
- case "telegram-del":
- telegramService.deleteTelegramUser(visitor.getUid());
- telegramService.getTelegramIdentifiers(Collections.singletonList(visitor)).forEach(t -> {
- telegramService.deleteChat(t);
- });
- result = "<p><a href=\"/settings\">Back</a></p>";
- break;
- case "facebook-disable":
- crosspostService.disableFBCrosspost(visitor.getUid());
- result = "<p><a href=\"/settings\">Back</a></p>";
- break;
- case "facebook-enable":
- crosspostService.enableFBCrosspost(visitor.getUid());
- result = "<p><a href=\"/settings\">Back</a></p>";
- break;
- case "vk-del":
- crosspostService.deleteVKUser(visitor.getUid());
- result = "<p><a href=\"/settings\">Back</a></p>";
- break;
- default:
- throw new HttpBadRequestException();
- }
-
- model.addAttribute("title", "Настройки");
- model.addAttribute("visitor", visitor);
- model.addAttribute("result", result);
- return "views/settings_result";
- }
-}
diff --git a/juick-www/src/main/java/com/juick/www/controllers/SignUp.java b/juick-www/src/main/java/com/juick/www/controllers/SignUp.java
deleted file mode 100644
index 02a8006b..00000000
--- a/juick-www/src/main/java/com/juick/www/controllers/SignUp.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.www.controllers;
-
-import com.juick.server.util.HttpBadRequestException;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.server.util.UserUtils;
-import com.juick.service.CrosspostService;
-import com.juick.service.MessengerService;
-import com.juick.service.UserService;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-
-import javax.inject.Inject;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- *
- * @author Ugnich Anton
- */
-@Controller
-public class SignUp {
-
- @Inject
- private UserService userService;
- @Inject
- private CrosspostService crosspostService;
- @Inject
- private MessengerService messengerService;
-
-
- @GetMapping("/signup")
- protected String doGet(@RequestParam String type, @RequestParam String hash, ModelMap model) {
- com.juick.User visitor = UserUtils.getCurrentUser();
-
- if (hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$")
- || !hash.matches("^[a-zA-Z0-9\\-]+$")) {
- throw new HttpBadRequestException();
- }
-
- String account = null;
- switch (type) {
- case "fb":
- account = crosspostService.getFacebookNameByHash(hash);
- break;
- case "vk":
- account = crosspostService.getVKNameByHash(hash);
- break;
- case "xmpp":
- account = crosspostService.getJIDByHash(hash);
- break;
- case "durov":
- account = crosspostService.getTelegramNameByHash(hash);
- break;
- case "messenger":
- account = messengerService.getDisplayName(hash);
- break;
- }
- if (account == null) {
- throw new HttpBadRequestException();
- }
-
- model.addAttribute("title", "Новый пользователь");
- model.addAttribute("visitor", visitor);
- model.addAttribute("account", account);
- model.addAttribute("type", type);
- model.addAttribute("hash", hash);
- return "views/signup";
- }
-
- @PostMapping("/signup")
- protected String doPost(
- HttpServletResponse response,
- @RequestParam String type,
- @RequestParam String hash,
- @RequestParam String action,
- @RequestParam(required = false) String username,
- @RequestParam(required = false) String password) {
- com.juick.User visitor = UserUtils.getCurrentUser();
- int uid = 0;
-
- if (hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") || !hash.matches("^[a-zA-Z0-9\\-]+$")) {
- throw new HttpBadRequestException();
- }
-
- if (action.charAt(0) == 'l') {
-
- if (visitor.isAnonymous()) {
- if (username.length() > 32) {
- throw new HttpBadRequestException();
- }
- uid = userService.checkPassword(username, password);
- } else {
- uid = visitor.getUid();
- }
-
- if (uid <= 0) {
- throw new HttpForbiddenException();
- }
-
- if (!(type.charAt(0) == 'f' && crosspostService.setFacebookUser(hash, uid))
- && !(type.charAt(0) == 'v' && crosspostService.setVKUser(hash, uid))
- && !(type.charAt(0) == 'd' && crosspostService.setTelegramUser(hash, uid))
- && !(type.charAt(0) == 'x' && crosspostService.setJIDUser(hash, uid))
- && !(type.charAt(0) == 'm' && messengerService.linkMessengerUser(hash, uid))) {
- throw new HttpBadRequestException();
- }
-
- } else { // Create new account
- if (username.length() < 2 || username.length() > 16 || !username.matches("^[a-zA-Z0-9\\-]+$") || password.length() < 6 || password.length() > 32) {
- throw new HttpBadRequestException();
- }
-
- // CHECK USERNAME
-
- uid = userService.createUser(username, password);
- if (uid <= 0) {
- throw new HttpBadRequestException();
- }
-
- if (!(type.charAt(0) == 'f' && crosspostService.setFacebookUser(hash, uid))
- && !(type.charAt(0) == 'v' && crosspostService.setVKUser(hash, uid))
- && !(type.charAt(0) == 'd' && crosspostService.setTelegramUser(hash, uid))
- && !(type.charAt(0) == 'x' && crosspostService.setJIDUser(hash, uid))
- && !(type.charAt(0) == 'm' && messengerService.linkMessengerUser(hash, uid))) {
- throw new HttpBadRequestException();
- }
- }
-
- if (visitor.isAnonymous()) {
- hash = userService.getHashByUID(uid);
- Cookie c = new Cookie("hash", hash);
- c.setMaxAge(365 * 24 * 60 * 60);
- response.addCookie(c);
- }
- return "redirect:/";
- }
-}
diff --git a/juick-www/src/main/resources/errors.properties b/juick-www/src/main/resources/errors.properties
deleted file mode 100644
index 7ec8fbfd..00000000
--- a/juick-www/src/main/resources/errors.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-error.title = Error page
-
-error.login=Wrong user or password \ No newline at end of file
diff --git a/juick-www/src/main/resources/errors_ru.properties b/juick-www/src/main/resources/errors_ru.properties
deleted file mode 100644
index ca13b926..00000000
--- a/juick-www/src/main/resources/errors_ru.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-error.title = Произошла ошибка
-
-error.login=Произошла ошибка, проверьте имя пользователя и пароль \ No newline at end of file
diff --git a/juick-www/src/main/resources/help b/juick-www/src/main/resources/help
deleted file mode 160000
-Subproject ce103cd9a2a8a200c6ebb3b41525e7c8f639d98
diff --git a/juick-www/src/main/resources/messages.properties b/juick-www/src/main/resources/messages.properties
deleted file mode 100644
index e53e9e7b..00000000
--- a/juick-www/src/main/resources/messages.properties
+++ /dev/null
@@ -1,79 +0,0 @@
-date.format=MM/dd/yyyy
-
-link.settings=Settings
-link.returnToMain=Back to Home Page
-link.contacts=Contacts
-link.tos=TOS
-link.adv=Advertisement
-
-link.popular=Popular
-link.allMessages=Discover
-link.withPhotos=Photos
-link.trends=Trends
-link.my=My feed
-link.privateMessages=PM
-link.discuss=Discuss
-link.recommended=Recommended
-link.postMessage=Post
-link.Login=Login
-link.logout=Logout
-
-link.settings.main=Main
-link.settings.password=Password
-link.settings.about=About
-
-label.sponsor=Sponsor
-label.sponsors=Sponsors
-label.search=Search
-label.register=Register
-label.username=User name
-label.password=Password
-
-postForm.newMessage=New message...
-postForm.imageLink=Link to image
-postForm.imageFormats=JPG/PNG, up to 10 MB
-postForm.or=or
-postForm.upload=Upload
-postForm.tags=Tags (space separated)
-postForm.submit=Send
-
-message.recommend=Recommend
-message.recommendedBy=Recommended by
-message.comment=Comment
-message.writeComment=Write a comment...
-message.share=Share
-message.subscribe=Subscribe
-message.subscribed=Subscribed
-message.delete=Delete
-message.loginForSending=<a href="{0}" class="a-login">Login</a> to post messages and comments
-message.sendLoginToXmpp=Send <b>LOGIN</b> to <a href="xmpp:juick@juick.com?message;body=LOGIN">juick@juick.com</a>
-
-messages.next=Next
-
-reply.reply=Reply
-reply.inReplyTo=in reply to
-reply.replies=Replies
-
-replies.showAsList=Show as list
-replies.showAsTree=Show as tree
-replies.unfoldAll=Unfold all
-
-question.areRegistered=Already registered?
-
-title.help=Help
-title.loginOrSignup=Juick - Log In or Sign Up
-title.index.anonym=Juick microblogs: popular posts
-title.index.user=Popular
-
-error.pageNotFound=Page not found
-error.pageNotFound.description=User probably deleted this post, or this page never existed.
-
-blog.blog=Blog
-blog.recommendations=Recommendations
-blog.photos=Photos
-blog.iread=I read
-blog.readers=My readers
-blog.bl=My blacklist
-blog.messages=Messages
-blog.comments=Comments
-blog.allPostsWithTag=All posts tagged \ No newline at end of file
diff --git a/juick-www/src/main/resources/messages_ru.properties b/juick-www/src/main/resources/messages_ru.properties
deleted file mode 100644
index fca6aa4b..00000000
--- a/juick-www/src/main/resources/messages_ru.properties
+++ /dev/null
@@ -1,77 +0,0 @@
-date.format=dd.MM.yyyy
-
-link.settings=Настройки
-link.returnToMain=Вернуться на главную
-link.contacts=Контакты
-link.tos=TOS
-
-link.popular=Популярные
-link.allMessages=Обзор
-link.withPhotos=Фото
-link.trends=Темы
-link.my=Моя лента
-link.privateMessages=Приватные
-link.discuss=Диалоги
-link.recommended=Рекомендации
-link.postMessage=Написать
-link.Login=Войти
-link.logout=Выйти
-
-link.settings.main=Главная
-link.settings.password=Пароль
-link.settings.about=О пользователе
-
-label.sponsor=Спонсор
-label.sponsors=Спонсоры
-label.search=Поиск
-label.register=Зарегистрироваться
-label.username=Имя пользователя
-label.password=Пароль
-
-postForm.newMessage=Новое сообщение...
-postForm.imageLink=Ссылка на изображение
-postForm.imageFormats=JPG/PNG, до 10Мб
-postForm.or=или
-postForm.upload=загрузить
-postForm.tags=Теги (через пробел)
-postForm.submit=Отправить
-
-message.recommend=Рекомендовать
-message.recommendedBy=Рекомендовали
-message.comment=Комментировать
-message.writeComment=Написать комментарий...
-message.share=Поделиться
-message.subscribe=Подписаться
-message.subscribed=Подписан
-message.delete=Удалить
-message.loginForSending=Чтобы добавлять сообщения и комментарии, <a href="{0}" class="a-login">представьтесь</a>
-message.sendLoginToXmpp=Отправьте <b>LOGIN</b> на <a href="xmpp:juick@juick.com?message;body=LOGIN">juick@juick.com</a>
-
-messages.next=Читать дальше
-
-reply.reply=Ответить
-reply.inReplyTo=в ответ на
-reply.replies=Ответы
-replies.showAsList=Показать списком
-replies.showAsTree=Показать деревом
-replies.unfoldAll=Раскрыть все
-
-question.areRegistered=Уже зарегистрированы?
-
-title.help=Справка
-title.loginOrSignup=Juick - Войдите в систему или зарегистрируйтесь
-title.index.anonym=Микроблоги Juick: популярные записи
-title.index.user=Популярные
-
-error.pageNotFound=Страница не найдена
-error.pageNotFound.description=Сожалеем, но страницу с этим адресом удалил её автор, либо её никогда не существовало.
-
-blog.blog=Блог
-blog.recommendations=Рекомендации
-blog.photos=Фотографии
-blog.iread=Я читаю
-blog.readers=Мои подписчики
-blog.bl=Черный список
-blog.messages=Сообщения
-blog.comments=Комментарии
-blog.allPostsWithTag=Все записи с тегом \ No newline at end of file
diff --git a/juick-www/src/main/resources/static/favicon.png b/juick-www/src/main/resources/static/favicon.png
deleted file mode 100644
index bc7161e2..00000000
--- a/juick-www/src/main/resources/static/favicon.png
+++ /dev/null
Binary files differ
diff --git a/juick-www/src/main/resources/static/logo.png b/juick-www/src/main/resources/static/logo.png
deleted file mode 100644
index 933f6099..00000000
--- a/juick-www/src/main/resources/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/juick-www/src/main/resources/static/tagscloud.png b/juick-www/src/main/resources/static/tagscloud.png
deleted file mode 100644
index 3e1bf169..00000000
--- a/juick-www/src/main/resources/static/tagscloud.png
+++ /dev/null
Binary files differ
diff --git a/juick-www/src/main/resources/templates/layouts/content.html b/juick-www/src/main/resources/templates/layouts/content.html
deleted file mode 100644
index 2ca9fd7e..00000000
--- a/juick-www/src/main/resources/templates/layouts/content.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<html prefix="og: http://ogp.me/ns#">
-<head id="org" itemprop="publisher" itemscope="" itemtype="http://schema.org/Organization">
- <meta charset="utf-8"/>
- <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
- <script type="text/javascript" src="{{ beans.webApp.scriptsUrl }}"></script>
- <link rel="stylesheet" type="text/css" href="{{ beans.webApp.styleUrl }}"/>
- {% block headers %}
- {{ headers | default('') | raw }}
- {% endblock %}
- <title itemprop="name">{{ title | default('Juick') }}</title>
- <meta itemprop="url" content="https://juick.com/" />
- <meta property="og:type" content="{{ ogtype | default('website') }}" />
- <meta property="fb:app_id" content="130568668304" />
- <meta name="viewport" content="width=device-width,initial-scale=1"/>
- <meta name="msapplication-config" content="//i.juick.com/browserconfig.xml"/>
- <meta name="msapplication-TileColor" content="#ffffff"/>
- <meta name="msapplication-TileImage" content="//i.juick.com/ms-icon-144x144.png"/>
- <meta name="theme-color" content="#ffffff"/>
- <meta name="apple-mobile-web-app-capable" content="yes" />
- <link rel="apple-touch-icon" sizes="57x57" href="//i.juick.com/apple-icon-57x57.png"/>
- <link rel="apple-touch-icon" sizes="60x60" href="//i.juick.com/apple-icon-60x60.png"/>
- <link rel="apple-touch-icon" sizes="72x72" href="//i.juick.com/apple-icon-72x72.png"/>
- <link rel="apple-touch-icon" sizes="76x76" href="//i.juick.com/apple-icon-76x76.png"/>
- <link rel="apple-touch-icon" sizes="114x114" href="//i.juick.com/apple-icon-114x114.png"/>
- <link rel="apple-touch-icon" sizes="120x120" href="//i.juick.com/apple-icon-120x120.png"/>
- <link rel="apple-touch-icon" sizes="144x144" href="//i.juick.com/apple-icon-144x144.png"/>
- <link rel="apple-touch-icon" sizes="152x152" href="//i.juick.com/apple-icon-152x152.png"/>
- <link rel="apple-touch-icon" sizes="180x180" href="//i.juick.com/apple-icon-180x180.png"/>
- <link itemprop="logo" href="http://juick.com/#juick-logo" itemtype="http://schema.org/ImageObject" />
- <link rel="icon" type="image/png" sizes="32x32" href="//i.juick.com/favicon-32x32.png"/>
- <link rel="icon" type="image/png" sizes="96x96" href="//i.juick.com/favicon-96x96.png"/>
- <link rel="icon" type="image/png" sizes="16x16" href="//i.juick.com/favicon-16x16.png"/>
- <link rel="manifest" href="//i.juick.com/manifest.json"/>
- <script type="application/ld+json">
-{
- "@context": "http://schema.org",
- "@id": "http://juick.com/#juick-logo",
- "@type": "ImageObject",
- "url": "http://juick.com/logo.png",
- "width": 110,
- "height": 36
-}
- </script>
-</head>
-<body id="body" {% if visitor.uid > 0 %}data-hash="{{visitor.authHash}}"{% endif %}>
-{% block body %}
-{% endblock %}
-</body>
-</html>
diff --git a/juick-www/src/main/resources/templates/layouts/default.html b/juick-www/src/main/resources/templates/layouts/default.html
deleted file mode 100644
index 343885c4..00000000
--- a/juick-www/src/main/resources/templates/layouts/default.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends "layouts/content" %}
-{% block body %}
-{% include "views/partial/navigation" %}
-<div id="wrapper">
- <section id="content"
- {% if msg | default('') is not empty %}data-mid="{{ msg.mid }}"{% endif %}>
- {% block content %}
- {% endblock %}
- </section>
- <aside id="column">
- {% block column %}
- {% endblock %}
- </aside>
-</div>
-{% include "views/partial/footer" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/layouts/minimal.html b/juick-www/src/main/resources/templates/layouts/minimal.html
deleted file mode 100644
index 15924521..00000000
--- a/juick-www/src/main/resources/templates/layouts/minimal.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% extends "layouts/content" %}
-{% block body %}
-<div id="wrapper">
- <section id="minimal_content">
- {% block content %}
- {% endblock %}
- </section>
-</div>
-{% include "views/partial/footer" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/404.html b/juick-www/src/main/resources/templates/views/404.html
deleted file mode 100644
index 02a790e6..00000000
--- a/juick-www/src/main/resources/templates/views/404.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
- <article>
- <h1>Страница не найдена</h1>
- <p>Сожалеем, но страницу с этим адресом удалил её автор, либо её никогда не существовало.</p>
- </article>
-{% endblock %}
-
-{% block "column" %}
-{% include "views/partial/homecolumn" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/blog.html b/juick-www/src/main/resources/templates/views/blog.html
deleted file mode 100644
index 9cf4714e..00000000
--- a/juick-www/src/main/resources/templates/views/blog.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{% extends "layouts/default" %}
-{% import "views/macros/tags" %}
-{% block content %}
-{% if noindex %}
-<!--noindex-->
-{% endif %}
-{% if paramTag | default('') is not empty %}
-<p class="page"><a href="/tag/{{ paramTag.name | urlencode }}">← {{ i18n("messages","blog.allPostsWithTag") }} <b>{{ paramTag.name | escape }}</b></a></p>
-{% endif %}
-<div itemscope="" itemtype="http://schema.org/Blog">
- <meta itemprop="url" content="{{ pageUrl }}"/>
-{% for msg in msgs %}
-{% include "views/partial/message" %}
-{% endfor %}
-</div>
-{% if nextpage | default('') is not empty %}
-<p class="page"><a href="{{ nextpage | raw }}" rel="prev">{{ i18n("messages","messages.next") }} →</a></p>
-{% endif %}
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/usercolumn" %}
-{% if noindex %}
-<!--/noindex-->
-{% endif %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/blog_tags.html b/juick-www/src/main/resources/templates/views/blog_tags.html
deleted file mode 100644
index 48e517eb..00000000
--- a/juick-www/src/main/resources/templates/views/blog_tags.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% extends "layouts/default" %}
-{% import "views/macros/tags" %}
-{% block content %}
-<p>
- {{ tags(user.name, tags) }}
-</p>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/usercolumn" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/help.html b/juick-www/src/main/resources/templates/views/help.html
deleted file mode 100644
index 3a022497..00000000
--- a/juick-www/src/main/resources/templates/views/help.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<article>
- {{ content | raw }}
-</article>
-{% endblock %}
-
-{% block "column" %}
-{{ navigation | raw }}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/index.html b/juick-www/src/main/resources/templates/views/index.html
deleted file mode 100644
index 97d726de..00000000
--- a/juick-www/src/main/resources/templates/views/index.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends "layouts/default" %}
-{% import "views/macros/tags" %}
-{% block content %}
-{% if noindex %}
-<!--noindex-->
-{% endif %}
-{% for msg in msgs %}
-{% include "views/partial/message" %}
-{% endfor %}
-{% if nextpage | default('') is not empty %}
-<p class="page"><a href="{{ nextpage | raw }}" rel="prev">{{ i18n("messages","messages.next") }} →</a></p>
-{% endif %}
-{% endblock %}
-{% block "column" %}
-{% if tag | default('') is not empty %}
-{% include "views/partial/tagcolumn" %}
-{% elseif visitor.uid > 0 %}
-{% if discover %}
-{% include "views/partial/homecolumn" %}
-{% else %}
-{% include "views/partial/usercolumn" %}
-{% endif %}
-{% else %}
-{% include "views/partial/homecolumn" %}
-{% endif %}
-{% if noindex %}
-<!--/noindex-->
-{% endif %}
-{% endblock %}
diff --git a/juick-www/src/main/resources/templates/views/login.html b/juick-www/src/main/resources/templates/views/login.html
deleted file mode 100644
index a538cb26..00000000
--- a/juick-www/src/main/resources/templates/views/login.html
+++ /dev/null
@@ -1,144 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <title>Juick</title>
- <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js" defer="defer"></script>
- <style>
- * { margin: 0; padding: 0; }
- html { font-family: sans-serif; font-size: 12pt; }
- html { background: #f8f8f8; }
- body { margin: 100px auto 0 auto; width: 1000px; }
- a { color: #069; }
- ul { float: left; width: 700px; height: 350px; list-style-type: none; background: url(/tagscloud.png) no-repeat; position: relative; box-shadow: 0 0 3px rgba(0,0,0,.16); }
- ul a { position: absolute; display: block; text-indent: 100%; white-space: nowrap; overflow: hidden; }
-
- #bottom1 { position: absolute; left: 0px; bottom: 10px; width: 100%; text-align: center; color: #555; }
- #bottom2 { position: absolute; left: 0px; bottom: -50px; width: 100%; padding-bottom: 20px; text-align: center; font-size: small; color: #777; }
-
- #signup,#signin { margin-left: 730px; width: 250px; }
- #signup { padding-top: 25px; }
- #signup>div { width: 100%; margin: 15px 0; }
- #signup>div>a { display: block; width: 100%; height: 32px; line-height: 32px; text-indent: 37px; text-decoration: none; overflow: hidden; }
-
- #facebook a { color: #FFF; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAADNQTFRFO1edX3ewl6bLnKrOoK3QrrrYvMXe2N7r3OLu3+Tv5urz7O/29vf6+Pn7+vv9/Pz9////ykQjsQAAAEZJREFUOMtjYBgFuAATO68ADxdOaUYuATDAqYBbAL8CFgECCjiBcqz4XMiPz3oQEKCtAgEkwEdIAQchBWyEFDAPkDdHsAIAhZkIwz/VK/UAAAAASUVORK5CYII=") no-repeat #3A569C; }
- #vk a { color: #FFF; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAHJQTFRFbY+zbo+zbpCzb5C0cpO1c5O2dZW3dpa4e5m6gJ29gZ69lq/In7bNo7jPrcDUs8XXvs3dv87dy9fkztnlz9rm0Nrm093o1N7o1+Dq3OTt3ubu4Ofv5Orw7fH27vL28PP38vX49Pb5+vv8+/z9/Pz9////2jSYlQAAAG5JREFUOMvtkEcOgDAMBE3vvXdIyP+/iMMRKfYHmMtcRtE6AD8f1Is8pyKgAs0RGYO2HSWqMQaoBHVRgYsS3AsrtyFlrqgdJlCLb95gxQO6IkZCqL+KCjz0TQU5ejOf2a3aJXPF7BOB2PvMhp8PDzGRFgEe7xvEAAAAAElFTkSuQmCC") no-repeat #6d8fb3; }
- #xmpp>a { color: #333; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAt9QTFRFBj5rCUFoFz5mDEFuDUNqGUJkGUNgAEprEkVtGkRhHURnHEZjAE+BIElmEEx/HEx0PUdTHE5wMEtfO0laJk5sFlN6Nk1cKFBuIlJ1R0pYRExTGF2KMVh1OFhxDGSQT1deNlx6TVhkIGKPUlphKWOFU1tiOmB+Vl1kmlAuNmaDQGpaIG6ba2Q2SGeBN2uUNW+LF3mGRmyLV3BAL3xWgmVJ2VAa2lEb0VYX11UafWlmam9mam5xy1km3lUea29y2VccvF8obHBz21gd4VcXS3ebPoVG1lwl5FkaU3iYYHaH2F0ejHFIx2Iv4V0aP4o+02As218g4l4bPYtFX351KZFaymU43mIrVYoz5mEfT4w0xGsrSo82eXqDw2s+z2k1OZVAT4SoPZU5RZM4NJVYbYc8VoSiWY43WItbuXBK52gYOI20TpM6YYSYfX98T5Q7foB9aYSZSZc8Ro21cYxH7GwdT46e0HFGeopO2HA8V5ZEf4s8528l1HM7UJs5UI+xXoyrWZhGY4ylyHdMwXhXQZS7XpdNU5tH4XJBYJZZcIuhbpJk0HdPU588kohqOZ2pVZS30XpX6XcxSaFrz4FIUqVWWqVCYZm23YBGxYRrZZ2QXpy/5YFC7IA+6oBRb6ZUcKZn44ZSgKJu54hHmJqXbbBNfKh3cq5nYqrMpJyVdrBjx5WCo56diamAjaWdyZeE8JFV8ZJWnqOlaK/Sd7Zh7pVdgbN6x52N8JdfwqCU8ZhgZLbXb7a5grtTmK+UxKKWtqahq6qhd7bHqauop6yvqqyp755pbbzRrK6rwamgib53qq+xra+sobSg86Jza8PJsLKujMVctrGwqLapv7Crr7Wr8ad1srSxj8R9uLOys7WyubSztLazkcZ/sre6tbe0r7m0tri1t7m2vri3uLq3v7m4ubu4nc9mpNBvos2UptJxtNWXtteFuNmbuNyQud2Rut6SwN+aweCbwuGdw+KeweKkF4OfHQAAAadJREFUOMtjeEoAMAwfBQ9vYJO68RCu4BYDq1bXmr2XHoMlnlzet2qGpTgX8y2EFWwensEdrW2FbUAlJ2zDKsqaYwo6eZHcIGzoor/s5IdPSfefPt3nf3Xn3HZpPU8xJAXWIvU88VOffcm78vTptrijJe4OfAmy1kgKzjMlaIfUPvvQcObp03U120ucTZQaGc8je1NZrd8g8cL7eUufPp0wfX2mvVydripKONzkTG1JXvL6TuXTR/Zb5gR6F+Vw30QNqNUss/pqn3/MvrTD7Wy1V9Y0jtXoIZnLv2nSnrfzF3bP3hDRu1wwHzOo1VWOTXn5yi/q7MTJuxTUscTFQyHNc5vfbU1btOCgjNBDbJH1UF5i9/XP6VVrJRUfYo/Nx3YcpS/euOo4Pn6MqeDx44f3LoY2SYnOXBlUfPPug8cwRQxQ6Qf39sda+HqZaQiwGydFlx+4eQ+qAqrg4b3bPqbmNhkrNm5cbGUUHply7d6Dh8hWPH744Oa186eOHDl06NDh46fP33zwEMUEiCMe3L13GwjuPbj38PHTx9jT5OPHj5G9MFQyDgA8riWAv9eLFAAAAABJRU5ErkJggg==") no-repeat #BBB; }
- #xmppinfo { background: #FFF; padding: 10px; display: none; }
-
- #signin { text-align: center; font-size: small; }
- #signinform { background: #FFF; padding: 10px 15px; margin-top: 15px; display: none; }
- input.txt { width: 212px; border: 1px solid #CCC; margin: 3px 0; padding: 3px; }
- input.submit { width: 70px; border: 1px solid #CCC; margin: 3px 0; padding: 3px; }
- </style>
- <link rel="icon" href="//i.juick.com/favicon.png"/>
- </head>
-
-<body>
-
-<ul id="tags">
- <li><a href="/tag/juick" style="left: 359px; top: 120px; width: 311px; height: 99px">juick</a></li>
- <li><a href="/tag/linux" style="left: 201px; top: 100px; width: 98px; height: 35px">linux</a></li>
- <li><a href="/tag/android" style="left: 314px; top: 42px; width: 45px; height: 158px">android</a></li>
- <li><a href="/tag/работа" style="left: 149px; top: 138px; width: 165px; height: 41px">работа</a></li>
- <li><a href="/tag/music" style="left: 119px; top: 249px; width: 124px; height: 32px">music</a></li>
- <li><a href="/tag/windows" style="left: 448px; top: 234px; width: 186px; height: 32px">windows</a></li>
- <li><a href="/tag/google" style="left: 244px; top: 252px; width: 134px; height: 41px">google</a></li>
- <li><a href="/tag/кино" style="left: 68px; top: 83px; width: 97px; height: 28px">кино</a></li>
- <li><a href="/tag/фото" style="left: 400px; top: 266px; width: 101px; height: 29px">фото</a></li>
- <li><a href="/tag/жизнь" style="left: 554px; top: 266px; width: 125px; height: 27px">жизнь</a></li>
- <li><a href="/tag/еда" style="left: 46px; top: 196px; width: 71px; height: 32px">еда</a></li>
- <li><a href="/tag/музыка" style="left: 61px; top: 111px; width: 139px; height: 27px">музыка</a></li>
- <li><a href="/tag/прекрасное" style="left: 152px; top: 200px; width: 205px; height: 32px">прекрасное</a></li>
- <li><a href="/tag/книги" style="left: 148px; top: 293px; width: 103px; height: 25px">книги</a></li>
- <li><a href="/tag/цитата" style="left: 325px; top: 301px; width: 126px; height: 27px">цитата</a></li> <li><a href="/tag/games" style="left: 117px; top: 142px; width: 30px; height: 104px">games</a></li>
- <li><a href="/tag/ubuntu" style="left: 503px; top: 2px; width: 28px; height: 102px">ubuntu</a></li>
- <li><a href="/tag/котэ" style="left: 534px; top: 27px; width: 76px; height: 28px">котэ</a></li>
- <li><a href="/tag/ВНЕЗАПНО" style="left: 501px; top: 293px; width: 146px; height: 23px">ВНЕЗАПНО</a></li>
- <li><a href="/tag/юмор" style="left: 73px; top: 53px; width: 84px; height: 28px">юмор</a></li>
- <li><a href="/tag/мысли" style="left: 202px; top: 179px; width: 102px; height: 21px">мысли</a></li>
- <li><a href="/tag/pic" style="left: 400px; top: 78px; width: 33px; height: 38px">pic</a></li>
- <li><a href="/tag/политота" style="left: 531px; top: 60px; width: 130px; height: 24px">политота</a></li>
- <li><a href="/tag/WOT" style="left: 159px; top: 63px; width: 48px; height: 20px">WOT</a></li>
- <li><a href="/tag/fail" style="left: 8px; top: 170px; width: 34px; height: 27px">fail</a></li>
- <li><a href="/tag/погода" style="left: 670px; top: 126px; width: 24px; height: 93px">погода</a></li>
- <li><a href="/tag/apple" style="left: 42px; top: 167px; width: 64px; height: 29px">apple</a></li>
- <li><a href="/tag/jabber" style="left: 436px; top: 43px; width: 25px; height: 75px">jabber</a></li>
- <li><a href="/tag/тян" style="left: 532px; top: 94px; width: 47px; height: 21px">тян</a></li>
- <li><a href="/tag/work" style="left: 359px; top: 55px; width: 58px; height: 23px">work</a></li>
- <li><a href="/tag/Python" style="left: 240px; top: 63px; width: 74px; height: 23px">Python</a></li>
- <li><a href="/tag/Видео" style="left: 266px; top: 232px; width: 76px; height: 20px">Видео</a></li>
- <li><a href="/tag/авто" style="left: 359px; top: 30px; width: 58px; height: 24px">авто</a></li>
- <li><a href="/tag/Anime" style="left: 360px; top: 328px; width: 66px; height: 21px">Anime</a></li>
- <li><a href="/tag/игры" style="left: 378px; top: 242px; width: 22px; height: 58px">игры</a></li>
- <li><a href="/tag/вело" style="left: 176px; top: 9px; width: 18px; height: 54px">вело</a></li>
- <li><a href="/tag/web" style="left: 661px; top: 219px; width: 22px; height: 47px">web</a></li>
- <li><a href="/tag/YouTube" style="left: 498px; top: 316px; width: 81px; height: 24px">YouTube</a></li>
- <li><a href="/tag/Вопрос" style="left: 208px; top: 18px; width: 22px; height: 72px">Вопрос</a></li>
- <li><a href="/tag/железо" style="left: 159px; top: 318px; width: 75px; height: 16px">железо</a></li>
- <li><a href="/tag/Microsoft" style="left: 20px; top: 146px; width: 86px; height: 21px">Microsoft</a></li>
- <li><a href="/tag/video" style="left: 616px; top: 101px; width: 51px; height: 19px">video</a></li>
- <li><a href="/tag/Россия" style="left: 32px; top: 242px; width: 68px; height: 16px">Россия</a></li>
- <li><a href="/tag/java" style="left: 409px; top: 226px; width: 39px; height: 22px">java</a></li>
- <li><a href="/tag/новости" style="left: 39px; top: 67px; width: 21px; height: 79px">новости</a></li>
- <li><a href="/tag/интернет" style="left: 100px; top: 233px; width: 17px; height: 85px">интернет</a></li>
- <li><a href="/tag/steam" style="left: 14px; top: 228px; width: 52px; height: 13px">steam</a></li>
- <li><a href="/tag/слова" style="left: 501px; top: 272px; width: 51px; height: 18px">слова</a></li>
- <li><a href="/tag/почта" style="left: 477px; top: 27px; width: 17px; height: 56px">почта</a></li>
- <li><a href="/tag/help" style="left: 123px; top: 281px; width: 21px; height: 35px">help</a></li>
- <li><a href="/tag/skype" style="left: 110px; top: 320px; width: 49px; height: 20px">skype</a></li>
- <li><a href="/tag/debian" style="left: 461px; top: 47px; width: 16px; height: 51px">debian</a></li>
- <li><a href="/tag/win" style="left: 505px; top: 104px; width: 27px; height: 16px">win</a></li>
- <li><a href="/tag/Религия" style="left: 33px; top: 281px; width: 67px; height: 17px">Религия</a></li>
- <li><a href="/tag/soft" style="left: 286px; top: 86px; width: 28px; height: 14px">soft</a></li>
- <li><a href="/tag/Политика" style="left: 144px; top: 281px; width: 75px; height: 12px">Политика</a></li>
- <li><a href="/tag/сны" style="left: 426px; top: 328px; width: 33px; height: 13px">сны</a></li>
- <li><a href="/tag/Питер" style="left: 146px; top: 233px; width: 50px; height: 16px">Питер</a></li>
- <li><a href="/tag/bash" style="left: 451px; top: 311px; width: 38px; height: 16px">bash</a></li>
- <li><a href="/tag/code" style="left: 279px; top: 310px; width: 39px; height: 16px">code</a></li>
- <li><a href="/tag/yandex" style="left: 19px; top: 263px; width: 56px; height: 18px">yandex</a></li>
- <li><a href="/tag/firefox" style="left: 452px; top: 295px; width: 48px; height: 16px">firefox</a></li>
- <li><a href="/tag/hardware" style="left: 230px; top: 40px; width: 67px; height: 18px">hardware</a></li>
- <li><a href="/tag/git" style="left: 78px; top: 258px; width: 20px; height: 19px">git</a></li>
- <li><a href="/tag/dev" style="left: 165px; top: 88px; width: 31px; height: 19px">dev</a></li>
- <li><a href="/tag/mobile" style="left: 421px; top: 24px; width: 15px; height: 47px">mobile</a></li>
- <li><a href="/tag/люди" style="left: 151px; top: 184px; width: 43px; height: 15px">люди</a></li>
- <li><a href="/tag/php" style="left: 149px; top: 24px; width: 27px; height: 18px">php</a></li>
- <li><a href="/tag/haskell" style="left: 271px; top: 293px; width: 48px; height: 16px">haskell</a></li>
- <li><a href="/tag/стихи" style="left: 135px; top: 42px; width: 41px; height: 11px">стихи</a></li>
- <li><a href="/tag/photo" style="left: 639px; top: 219px; width: 20px; height: 39px">photo</a></li>
- <li><a href="/tag/чай" style="left: 448px; top: 220px; width: 27px; height: 14px">чай</a></li>
- <li><a href="/tag/Опрос" style="left: 297px; top: 22px; width: 14px; height: 41px">Опрос</a></li>
- <li><a href="/tag/Chrome" style="left: 311px; top: 25px; width: 48px; height: 17px">Chrome</a></li>
- <li><a href="/tag/life" style="left: 255px; top: 311px; width: 23px; height: 16px">life</a></li>
- <li><a href="/tag/opera" style="left: 226px; top: 232px; width: 38px; height: 14px">opera</a></li>
- <li><a href="/tag/programming" style="left: 234px; top: 327px; width: 81px; height: 14px">programming</a></li>
- <li><a href="/tag/дети" style="left: 15px; top: 197px; width: 31px; height: 13px">дети</a></li>
- <li><a href="/tag/сериалы" style="left: 575px; top: 219px; width: 61px; height: 13px">сериалы</a></li>
- <li><a href="/tag/учеба" style="left: 616px; top: 84px; width: 43px; height: 17px">учеба</a></li>
- </ul>
-
-<div id="bottom1">juick.com &copy; 2008-2018 &nbsp; <a href="/help/ru/contacts" rel="nofollow">Контакты</a> &middot; <a href="/help/" rel="nofollow">Помощь</a></div>
-
-<div id="signup">
- {{ i18n("messages","label.register") }}:
- <div id="facebook"><a href="/_fblogin" rel="nofollow">Facebook</a></div>
- <div id="vk"><a href="/_vklogin" rel="nofollow">ВКонтакте</a></div>
- <div id="tg">
- <script async src="https://telegram.org/js/telegram-widget.js?3"
- data-telegram-login="Juick_bot" data-size="medium" data-radius="0"
- data-auth-url="https://juick.com/_tglogin" data-request-access="write"></script>
- </div>
- </div>
-<div id="signin">
- <a href="#" onclick="$('#signinform').toggle(); $('#nickinput').focus(); return false">
- {{ i18n("messages","question.areRegistered") }}
- </a>
- <div id="signinform"><form action="/login" method="POST">
- <input class="txt" type="text" name="username" placeholder='{{ i18n("messages","label.username") }}' id="nickinput"/>
- <input class="txt" type="password" name="password" placeholder='{{ i18n("messages","label.password") }}'/>
- <input class="submit" type="submit" value="OK"/>
- </form></div>
- </div>
-
-</body>
-</html>
diff --git a/juick-www/src/main/resources/templates/views/login_success.html b/juick-www/src/main/resources/templates/views/login_success.html
deleted file mode 100644
index ee71f12f..00000000
--- a/juick-www/src/main/resources/templates/views/login_success.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <title>Blank window</title>
-</head>
-<body>
- <script type="text/javascript">
- window.opener.postMessage("{{ hash }}", "*");
- window.close();
- </script>
-</body>
-</html>
diff --git a/juick-www/src/main/resources/templates/views/macros/tags.html b/juick-www/src/main/resources/templates/views/macros/tags.html
deleted file mode 100644
index 09278ffe..00000000
--- a/juick-www/src/main/resources/templates/views/macros/tags.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% macro tags(uname="", tagsList) %}
-{% for tag in tagsList %}
-<a href="/{{ uname }}/?tag={{ tag | urlencode }}">{{ tag | raw }}</a>
-{% endfor %}
-{% endmacro %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/partial/footer.html b/juick-www/src/main/resources/templates/views/partial/footer.html
deleted file mode 100644
index 35972254..00000000
--- a/juick-www/src/main/resources/templates/views/partial/footer.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<div id="footer">
- <div id="footer-right"> &middot;
- <a href="/help/contacts" rel="nofollow">{{ i18n("messages","link.contacts") }}</a> &middot;
- <a href="/help/tos" rel="nofollow">{{ i18n("messages","link.tos") }}</a>
- </div>
- <div id="footer-social">
- <a href="https://twitter.com/Juick" rel="nofollow"><i data-icon="ei-sc-twitter" data-size="m"></i></a>
- <a href="https://vk.com/juick" rel="nofollow"><i data-icon="ei-sc-vk" data-size="m"></i></a>
- <a href="https://www.facebook.com/JuickCom" rel="nofollow"><i data-icon="ei-sc-facebook" data-size="m"></i></a>
- </div>
- <div id="footer-left">juick.com &copy; 2008-2018
- {% if links | default ('') is not empty %}
- <br/>{{ i18n("messages","label.sponsors") }}: {{ links | raw }}
- {% endif %}
- </div>
-</div>
diff --git a/juick-www/src/main/resources/templates/views/partial/homecolumn.html b/juick-www/src/main/resources/templates/views/partial/homecolumn.html
deleted file mode 100644
index 64dd9cbd..00000000
--- a/juick-www/src/main/resources/templates/views/partial/homecolumn.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<ul class="toolbar">
- <li>
- <a href="/" title="Top">
- <i data-icon="ei-heart" data-size="s"></i>Top
- </a>
- </li>
- <li>
- <a href="/?show=all" title="{{ i18n("messages","link.allMessages") }}">
- <i data-icon="ei-search" data-size="s"></i>{{ i18n("messages","link.allMessages") }}
- </a>
- </li>
- <li>
- <a href="/?show=photos" title="{{ i18n("messages","link.withPhotos") }}">
- <i data-icon="ei-camera" data-size="s"></i>{{ i18n("messages","link.withPhotos") }}
- </a>
- </li>
-</ul>
-<div class="tags">
- <h4>{{ i18n("messages","link.trends") }}</h4>
- {% include "views/partial/tags" %}
- {% if showAdv | default(false) %}
- <h4>Наши друзья</h4>
- <a href="https://ru.wix.com/">конструктор сайтов</a>
- {% endif %}
-</div> \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/partial/message.html b/juick-www/src/main/resources/templates/views/partial/message.html
deleted file mode 100644
index 0b6db3df..00000000
--- a/juick-www/src/main/resources/templates/views/partial/message.html
+++ /dev/null
@@ -1,76 +0,0 @@
-<article data-mid="{{ msg.mid }}" itemprop="blogPost" itemscope="" itemtype="http://schema.org/BlogPosting" itemref="org">
- <header class="h">
- <span itemprop="author" itemscope="" itemtype="http://schema.org/Person">
- <a href="/{{ msg.user.name }}/" itemprop="url" rel="author"><span itemprop="name">{{ msg.user.name }}</span></a>
- </span>
- <div class="msg-avatar"><a href="/{{ msg.user.name }}/">
- <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/></a>
- </div>
- <div class="msg-ts">
- <a href="/{{ msg.user.name }}/{{ msg.mid }}">
- <time itemprop="datePublished dateModified" itemtype="http://schema.org/Date" datetime="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }}Z"
- title="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }} GMT">
- {{ msg.timestamp | prettyTime }}
- </time>
- </a>
- </div>
- <div class="msg-tags" itemprop="headline">
- {{ tags(msg.user.name, msg.tags | tagsList) }}
- </div>
- </header>
- <p itemprop="description">{{ msg | formatMessage }}</p>
- {% if msg.AttachmentType is not empty %}
- <p class="ir"><a href="//i.juick.com/p/{{ msg.mid }}.{{ msg.AttachmentType }}" data-fname="{{ msg.mid }}.{{ msg.AttachmentType }}">
- <img itemprop="image" src="//i.juick.com/photos-512/{{ msg.mid }}.{{ msg.AttachmentType }}" alt=""/></a>
- </p>
- {% endif %}
- <nav class="l">
- {% if visitor.uid == msg.user.uid %}
- <a href="/{{ msg.mid }}" class="a-like msg-button">
- <span class="msg-button-icon">
- <i data-icon="ei-heart" data-size="s"></i>
- {% if msg.likes > 0 %}&nbsp;{{ msg.likes }}{% endif %}
- </span>
- <span>&nbsp;{{ i18n("messages","message.recommend") }}</span>
- </a>
- {% elseif visitor.uid > 0 %}
- <a href="/post?body=!+%23{{ msg.mid }}" class="a-like msg-button">
- <span class="msg-button-icon">
- <i data-icon="ei-heart" data-size="s"></i>
- {% if msg.likes > 0 %}&nbsp;{{ msg.likes }}{% endif %}
- </span>
- <span>&nbsp;{{ i18n("messages","message.recommend") }}</span>
- </a>
- {% else %}
- <a href="/login" class="a-login msg-button">
- <span class="msg-button-icon">
- <i data-icon="ei-heart" data-size="s"></i>
- {% if msg.likes > 0 %}&nbsp;{{ msg.likes }}{% endif %}
- </span>
- <span>&nbsp;{{ i18n("messages","message.recommend") }}</span>
- </a>
- {% endif %}
- {% if (not msg.ReadOnly) or (visitor.uid == msg.user.uid) %}
- <a href="/{{ msg.mid }}" class="a-comment msg-button">
- <span class="msg-button-icon">
- <i data-icon="ei-comment" data-size="s"></i>
- {% if msg.Replies > 0 %}&nbsp;
- {% if msg.unread %}
- <span class="badge">{{ msg.Replies }}</span>
- {% else %}
- {{ msg.Replies }}
- {% endif %}
- {% endif %}
- </span>
- <span>&nbsp;{{ i18n("messages","message.comment") }}</span>
- </a>
- <a href="#" class="msg-menu msg-button">
- <i data-icon="ei-link" data-size="s"></i>
- <span>&nbsp;{{ i18n("messages","message.share") }}</span>
- </a>
- {% endif %}
- {% if msg.FriendsOnly %}
- <a href="#" class="a-privacy">Открыть доступ</a>
- {% endif %}
- </nav>
-</article> \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/partial/navigation.html b/juick-www/src/main/resources/templates/views/partial/navigation.html
deleted file mode 100644
index fa1dadcc..00000000
--- a/juick-www/src/main/resources/templates/views/partial/navigation.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<header>
- <div id="header_wrapper">
- {% if visitor.uid > 0 %}
- <div id="ctitle">
- <a href="/{{ visitor.name }}">
- <img src="//i.juick.com/a/{{ visitor.uid }}.png" alt=""/>{{ visitor.name }}
- </a>
- </div>
- {% else %}
- <div id="logo"><a href="/{% if visitor.uid > 0 %}?show=my{% endif %}">Juick</a></div>
- {% endif %}
- <div id="search">
- <form action="/">
- <input name="search" class="text"
- placeholder="{{ i18n('messages','label.search') }}" value="{{ search | default('') }}"/>
- </form>
- </div>
- <nav id="global">
- <ul>
- {% if visitor.uid > 0 %}
- <li><a href="/?show=discuss"><i data-icon="ei-comment" data-size="s"></i>{{ i18n("messages","link.discuss") }}{% if visitor.unreadCount > 0 %}<span class="badge">{{ visitor.unreadCount }}</span>{% endif %}</a></li>
- {% else %}
- <li><a href="/?show=photos" rel="nofollow"><i data-icon="ei-camera" data-size="s"></i>{{ i18n("messages","link.withPhotos") }}</a></li>
- {% endif %}
- <li><a href="/?show=all" rel="nofollow"><i data-icon="ei-search" data-size="s"></i>{{ i18n("messages","link.allMessages") }}</a></li>
- {% if visitor.uid > 0 %}
- <li><a id="post" href="/post">
- <i data-icon="ei-pencil" data-size="s"></i>{{ i18n("messages","link.postMessage") }}</a>
- </li>
- {% else %}
- <li>
- <a class="a-login" href="/login" rel="nofollow">
- <i data-icon="ei-user" data-size="s"></i>{{ i18n("messages", "link.Login") }}
- </a>
- </li>
- {% endif %}
- </ul>
- </nav>
- </div>
-</header>
diff --git a/juick-www/src/main/resources/templates/views/partial/settings_tabs.html b/juick-www/src/main/resources/templates/views/partial/settings_tabs.html
deleted file mode 100644
index 4715253e..00000000
--- a/juick-www/src/main/resources/templates/views/partial/settings_tabs.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<div id="pagetabs"><ul>
- <li><a href="/settings">{{ i18n("messages","link.settings.main") }}</a></li>
- <li><a href="/settings?page=password">{{ i18n("messages","link.settings.password") }}</a></li>
- <li><a href="/settings?page=about">{{ i18n("messages","link.settings.about") }}</a></li>
- <li><a href="/logout"><i data-icon="ei-user" data-size="s"></i>{{ i18n("messages","link.logout") }}</a></li>
-</ul></div> \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/partial/tagcolumn.html b/juick-www/src/main/resources/templates/views/partial/tagcolumn.html
deleted file mode 100644
index 3e61d3d3..00000000
--- a/juick-www/src/main/resources/templates/views/partial/tagcolumn.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<div id="ctitle">
- <h2>*{{ tag.name }}</h2>
-</div>
-{% if visitor is not empty and visitor.uid > 0 %}
-<ul class="toolbar">
- {% if isSubscribed %}
- <li>
- <a href="/post?body=U+%2A{{ tag.name }}" title="Подписан">
- <i data-icon="ei-check" data-size="s"></i>Subscribed
- </a>
- </li>
- {% else %}
- <li>
- <a href="/post?body=S+%2A{{ tag.name }}" title="Подписаться">
- <i data-icon="ei-plus" data-size="s"></i>Subscribe
- </a>
- </li>
- {% endif %}
- {% if isInBL %}
- <li>
- <a href="/post?body=BL+%2A{{ tag.name }}" title="Разблокировать">
- <i data-icon="ei-close-o" data-size="s"></i>Unblock
- </a>
- </li>
- {% else %}
- <li>
- <a href="/post?body=BL+%2A{{ tag.name }}" title="Заблокировать">
- <i data-icon="ei-close" data-size="s"></i>Block
- </a>
- </li>
- {% endif %}
-</ul>
-{% endif %}
diff --git a/juick-www/src/main/resources/templates/views/partial/tags.html b/juick-www/src/main/resources/templates/views/partial/tags.html
deleted file mode 100644
index 3235213e..00000000
--- a/juick-www/src/main/resources/templates/views/partial/tags.html
+++ /dev/null
@@ -1,3 +0,0 @@
-{% for tag in tags %}
- <a href="/tag/{{ tag | urlencode }}" title="{{ tag }}">{{ tag | raw }}</a>
-{% endfor %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/partial/usercolumn.html b/juick-www/src/main/resources/templates/views/partial/usercolumn.html
deleted file mode 100644
index 2b1963e3..00000000
--- a/juick-www/src/main/resources/templates/views/partial/usercolumn.html
+++ /dev/null
@@ -1,89 +0,0 @@
-{% if visitor is not empty and visitor.uid > 0 and visitor.uid != user.uid %}
-<div id="ctitle">
- <a href="/{{ user.name }}">
- <img src="//i.juick.com/a/{{ user.uid }}.png" alt=""/>{{ user.name }}
- </a>
-</div>
-<ul class="toolbar">
- {% if isSubscribed %}
- <li>
- <a href="/post?body=U+%40{{ user.name }}" title="Подписан">
- <i data-icon="ei-check" data-size="s"></i>Subscribed
- </a>
- </li>
- {% else %}
- <li>
- <a href="/post?body=S+%40{{ user.name }}" title="Подписаться">
- <i data-icon="ei-plus" data-size="s"></i>Subscribe
- </a>
- </li>
- {% endif %}
- {% if isInBL %}
- <li>
- <a href="/post?body=BL+%40{{ user.name }}" title="Разблокировать">
- <i data-icon="ei-close-o" data-size="s"></i>Unblock
- </a>
- </li>
- {% else %}
- <li>
- <a href="/post?body=BL+%40{{ user.name }}" title="Заблокировать">
- <i data-icon="ei-close" data-size="s"></i>Block
- </a>
- </li>
- {% endif %}
- {% if not isInBLAny %}
- <li>
- <a href="/pm/sent?uname={{ user.name }}" title="Написать приватное сообщение">
- <i data-icon="ei-envelope" data-size="s"></i>PM
- </a>
- </li>
- {% endif %}
-</ul>
-{% else %}
-<hr/>
-{% endif %}
-<ul>
- {% if visitor is not empty and visitor.uid == user.uid %}
- <li><a href="/?show=my"><i data-icon="ei-clock" data-size="s"></i>{{ i18n("messages","link.my") }}</a></li>
- <li><a href="/pm/inbox"><i data-icon="ei-envelope" data-size="s"></i>{{ i18n("messages","link.privateMessages") }}</a></li>
- <li><a href="/?show=discuss"><i data-icon="ei-comment" data-size="s"></i>{{ i18n("messages","link.discuss") }}</a></li>
- {% endif %}
- <li><a href="/{{ user.name }}/?show=recomm" rel="nofollow"><i data-icon="ei-heart" data-size="s"></i>{{ i18n("messages","blog.recommendations") }}</a></li>
- <li><a href="/{{ user.name }}/?show=photos" rel="nofollow"><i data-icon="ei-camera" data-size="s"></i>{{ i18n("messages","blog.photos") }}</a></li>
- {% if visitor is not empty and visitor.uid == user.uid and false %}
- <li><a href="/?show=mycomments" rel="nofollow">{{ i18n("messages","blog.comments") }}</a></li>
- <li><a href="/?show=unanswered" rel="nofollow">Неотвеченные</a></li>
- {% endif %}
- {% if visitor is not empty and visitor.uid == user.uid %}
- <li><a href="/settings" rel="nofollow"><i data-icon="ei-gear" data-size="s"></i>{{ i18n("messages","link.settings") }}</a></li>
- {% endif %}
-</ul>
-<hr/>
-<form action="/{{ user.name }}/">
- <p><input type="text" name="search" class="inp" placeholder="{{ i18n('messages','label.search') }}"/></p>
-</form>
-{% include "views/partial/usertags" %}
-<hr/>
-<div id="ustats">
- <ul>
- <li><a href="/{{ user.name }}/friends">{{ i18n("messages","blog.iread") }}: {{ statsIRead }}</a></li>
- <li><a href="/{{ user.name }}/readers">{{ i18n("messages","blog.readers") }}: {{ statsMyReaders }}</a></li>
- {% if statsMyBL > 0 and visitor.uid == user.uid %}
- <li><a href="/{{ user.name }}/bl">{{ i18n("messages","blog.bl") }}: {{ statsMyBL }}</a></li>
- {% endif %}
- <li>{{ i18n("messages","blog.messages") }}: {{ statsMessages }}</li>
- <li>{{ i18n("messages","blog.comments") }}: {{ statsReplies }}</li>
- </ul>
- {% if iread is not empty %}
- <div class="iread">
- {% for u in iread %}
- <span>
- <a href="/{{ u.name }}/">
- <img src="//i.juick.com/as/{{ u.uid }}.png" alt="{{ u.name }}"/>
- </a>
- </span>
- {% endfor %}
- </div>
- {% endif %}
-
-</div>
diff --git a/juick-www/src/main/resources/templates/views/partial/usertags.html b/juick-www/src/main/resources/templates/views/partial/usertags.html
deleted file mode 100644
index 71d1303e..00000000
--- a/juick-www/src/main/resources/templates/views/partial/usertags.html
+++ /dev/null
@@ -1,3 +0,0 @@
-{% import "views/macros/tags" %}
-{{ tags(user.name, tagStats) }}
-<a href="/{{ user.name }}/tags" rel="nofollow">...</a> \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/pm_inbox.html b/juick-www/src/main/resources/templates/views/pm_inbox.html
deleted file mode 100644
index d6a9b65f..00000000
--- a/juick-www/src/main/resources/templates/views/pm_inbox.html
+++ /dev/null
@@ -1,35 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-{% if not msgs.isEmpty() %}
-<ul id="private-messages">
- {% for msg in msgs %}
- <li class="msg">
- <div class="msg-cont">
- <div class="msg-header">
- @<a href="/{{ msg.user.name }}/">{{ msg.user.name }}</a>:
- <div class="msg-avatar">
- <a href="/{{ msg.user.name }}/">
- <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/>
- </a>
- </div>
- <div class="msg-ts">{{ msg.timestamp | prettyTime }}</div>
- </div>
-
- <div class="msg-txt">{{ msg | formatMessage }}</div>
- <form action="/pm/send" method="POST" enctype="multipart/form-data">
- <input type="hidden" name="uname" value="{{ msg.user.name }}"/>
- <div class="msg-comment">
- <div class="ta-wrapper">
- <textarea name="body" rows="1" class="replypm" placeholder="Написать ответ"></textarea>
- </div>
- </div>
- </form>
- </div>
- </li>
- {% endfor %}
-</ul>
-{% endif %}
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/usercolumn" %}
-{% endblock %}
diff --git a/juick-www/src/main/resources/templates/views/pm_sent.html b/juick-www/src/main/resources/templates/views/pm_sent.html
deleted file mode 100644
index bc42c4ab..00000000
--- a/juick-www/src/main/resources/templates/views/pm_sent.html
+++ /dev/null
@@ -1,33 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<form action="/pm/send" method="POST" enctype="multipart/form-data">
- <div class="newpm">
- <div class="newpm-to">To: <input type="text" name="uname" placeholder="username" value="{{ uname }}"/></div>
- <div class="newpm-body"><textarea name="body" rows="2"></textarea></div>
- <div class="newpm-send"><input type="submit" value="OK"/></div>
- </div>
-</form>
-{% if not msgs.isEmpty() %}
-<ul id="private-messages">
- {% for msg in msgs %}
- <li class="msg">
- <div class="msg-cont">
- <div class="msg-header">
- @<a href="/{{ msg.user.name }}/">{{ msg.user.name }}</a>:
- <div class="msg-avatar">
- <a href="/{{ msg.user.name }}/">
- <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/>
- </a>
- </div>
- <div class="msg-ts">{{ msg.timestamp | prettyTime }}</div>
- </div>
- <div class="msg-txt">{{ msg | formatMessage }}</div>
- </div>
- </li>
- {% endfor %}
-</ul>
-{% endif %}
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/usercolumn" %}
-{% endblock %}
diff --git a/juick-www/src/main/resources/templates/views/post.html b/juick-www/src/main/resources/templates/views/post.html
deleted file mode 100644
index 1f642ce1..00000000
--- a/juick-www/src/main/resources/templates/views/post.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends "layouts/minimal" %}
-{% import "views/macros/tags" %}
-{% block content %}
-<article>
-<form action="/post2" method="post" id="postmsg" enctype="multipart/form-data">
- <p style="text-align: left">
- <b>Фото:</b> <span id="attachmentfile">
- <input style="width: 100%;" type="file" name="attach"/> <i>({{ i18n("messages","postForm.imageFormats") }})</i></span>
- </p>
- <p>
- <textarea name="body" class="newmessage" rows="7" cols="10" placeholder="*weather It's very cold today!">{{ body }}</textarea>
- <br/>
- <input type="submit" class="subm" value=" {{ i18n("messages","postForm.submit") }} "/>
- </p>
-</form>
-</article>
-<p style="text-align: left"><b>Теги:</b></p>
-{{ tags(visitor.name, tags) }}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/post_success.html b/juick-www/src/main/resources/templates/views/post_success.html
deleted file mode 100644
index 2106f3cb..00000000
--- a/juick-www/src/main/resources/templates/views/post_success.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends "layouts/minimal" %}
-{% block content %}
-<h1>Сообщение опубликовано</h1>
-<p>Поделитесь своим новым постом в социальных сетях:</p>
-{% if sharetwi | default('') is not empty %}
-<p class="social">
- <a href="https://twitter.com/intent/tweet?text={{ sharetwi }}"
- class="sharenew"><i data-icon="ei-sc-twitter" data-size="m"></i>Отправить в Twitter</a></p>
-{% endif %}
-<p class="social">
- <a href="https://vk.com/share.php?url={{ url | urlencode }}"
- class="sharenew"><i data-icon="ei-sc-vk" data-size="m"></i>Отправить в ВКонтакте</a></p>
-{% if facebook | default('') is not empty %}
-<p class="social">
- <a href="https://www.facebook.com/sharer/sharer.php?u={{ url | urlencode }}"
- class="sharenew"><i data-icon="ei-sc-facebook" data-size="m"></i>Отправить в Facebook</a></p>
-{% endif %}
-<p>Ссылка на сообщение: <a href="{{ url | raw }}">{{ url }}</a></p>
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/settings_about.html b/juick-www/src/main/resources/templates/views/settings_about.html
deleted file mode 100644
index bbf9e772..00000000
--- a/juick-www/src/main/resources/templates/views/settings_about.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<article>
- <form action="/settings" method="POST" enctype="multipart/form-data">
- <p>Full name: <input type="text" name="fullname" value="{{ userinfo.fullName }}"/></p>
- <p>Country: <input type="text" name="country" value="{{ userinfo.country }}"/></p>
- <p>URL: <input type="text" name="url" value="{{ userinfo.url }}" size="32"/><br/>
- <small>Please, start with &quot;http://&quot;</small></p>
- <p>About:<br/>
- <input type="text" name="descr" value="{{ userinfo.description }}" style="width: 100%"/><br/>
- <small>Max. 255 symbols</small></p>
- <p>Avatar: <input type="file" name="avatar"/><br/>
- <small>Recommendations: PNG, 96x96, &lt;50Kb. Also, JPG and GIF supported.</small></p>
- <p><input type="hidden" name="page" value="about"/><input type="submit" value=" OK "/></p>
- </form>
-</article>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/settings_tabs" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/settings_auth-email.html b/juick-www/src/main/resources/templates/views/settings_auth-email.html
deleted file mode 100644
index e906d704..00000000
--- a/juick-www/src/main/resources/templates/views/settings_auth-email.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<article>
- <p>{{ result }}</p><p><a href="/settings">Settings</a>.</p>
-</article>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/settings_tabs" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/settings_main.html b/juick-www/src/main/resources/templates/views/settings_main.html
deleted file mode 100644
index 65fbc984..00000000
--- a/juick-www/src/main/resources/templates/views/settings_main.html
+++ /dev/null
@@ -1,151 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<article>
- <h1>Настройки</h1>
- <form action="/settings" method="POST" enctype="multipart/form-data">
- <fieldset>
- <legend>Notification options</legend>
- <p><input type="checkbox" name="jnotify" value="1" {% if notify_options.repliesEnabled %}
- checked="checked" {% endif %}/> Reply notifications (&quot;Message posted&quot;)</p>
- <p><input type="checkbox" name="subscr_notify" value="1" {% if notify_options.subscriptionsEnabled %}
- checked="checked" {% endif %}/> Subscriptions notifications (&quot;@user subscribed...&quot;)</p>
- <p><input type="checkbox" name="recomm" value="1" {% if notify_options.recommendationsEnabled %}
- checked="checked" {% endif %}/> Posts recommendations (&quot;Recommended by @user&quot;)</p>
- <p><input type="hidden" name="page" value="main"/><input type="submit" value=" OK "/></p>
- </fieldset>
- </form>
- <fieldset>
- <legend style="background: url(//telegram.org/favicon.ico?3) no-repeat; padding-left: 58px; line-height: 48px;">
- Telegram</legend>
- {% if telegram_name is not empty %}
- <form action="/settings" method="post">
- <div>Telegram: <b>{{ telegram_name }}</b> &mdash;
- <input type="hidden" name="page" value="telegram-del"/>
- <input type="submit" value=" Disable "/>
- </div>
- </form>
- {% else %}
- <p>To connect Telegram account: send any text message to <a href="https://telegram.me/Juick_bot">@Juick_bot</a>
- </p>
- {% endif %}
- </fieldset>
- {% if jids | length > 0 %}
- <form action="/settings" method="POST" enctype="multipart/form-data">
- <fieldset>
- <legend style="background: url(//static.juick.com/settings/xmpp.png) no-repeat; padding-left: 58px; line-height: 48px;">
- XMPP accounts
- </legend>
- <p>Your accounts:</p>
- <p>
- {% for jid in jids %}
- <label><input type="radio" name="delete" value="xmpp;{{ jid }}">{{ jid }}</label><br/>
- {% endfor %}
- {% for auth in auths %}
- <label><input type="radio" name="delete"
- value="xmpp-unauth;{{ auth.account }}">{{ auth.account }}</label>
- &mdash; <a href="#"
- onclick="alert(\'To confirm, please send &quot;AUTH {{ auth.getAuthCode() }}&quot; (without quotes) from this account to &quot;juick@juick.com&quot;.\'); return false;">Confirm</a><br/>
- {% endfor %}
- </p>
- {% if jids | length > 1 %}
- <p><input type="hidden" name="page" value="jid-del"/><input type="submit" value=" Delete "/></p>
- {% endif %}
- <p>To add new jabber account: send any text message to <a href="xmpp:juick@juick.com?message;body=login">juick@juick.com</a>
- </p>
- </fieldset>
- </form>
- {% endif %}
- <fieldset>
- <legend style="background: url(//static.juick.com/settings/email.png) no-repeat; padding-left: 58px; line-height: 48px;">
- E-mail
- </legend>
- <form action="/settings" method="POST" enctype="multipart/form-data">
- <p>Add account:<br/>
- <input type="text" name="account"/>
- <input type="hidden" name="page" value="email-add"/>
- <input type="submit" value=" Add "/>
- </p>
- </form>
- <form action="/settings" method="POST" enctype="multipart/form-data">
- <p>Your accounts:</p>
- <p>
- {% for email in emails %}
- <label><input type="radio" name="account" value="{{ email }}">{{ email }}</label><br/>
- {% endfor %}
- {% if emails is empty %}
- - </p>
- {% else %}
- </p>
- {% if jids | length > 1 %}
- <p><input type="hidden" name="page" value="email-del"/><input type="submit" value=" Delete "/></p>
- {% endif %}
- {% endif %}
- </form>
- {% if emails is not empty %}
- <!--email_off-->
- <form action="/settings" method="POST" enctype="multipart/form-data">
- <p>You can receive notifications to email:<br/>
- Sent to <select name="account">
- <option value="">Disabled</option>
- {% for email in emails %}
- <option value="{{ email }}" {% if email_active == email %} selected="selected" {% endif %}>
- {{ email }}
- </option>
- {% endfor %}
- </select>
- <input type="hidden" name="page" value="email-subscr"/>
- <input type="submit" value="OK"/></p>
- </form>
- <!--/email_off-->
- {% endif %}
- <p>&nbsp;</p>
- <p>You can post to Juick via e-mail. Send your <span style="text-decoration: underline">plain text</span>
- messages to <span><a href="mailto:juick@juick.com">juick@juick.com</a></span>. You can attach one photo or video file.</p>
- </fieldset>
- <fieldset>
- <legend style="background: url(//static.juick.com/settings/facebook.png) no-repeat; padding-left: 58px; line-height: 48px;">
- Facebook
- </legend>
- {% if fbstatus.connected %}
- {% if fbstatus.crosspostEnabled %}
- <form action="/settings" method="post">
- <div>
- Facebook: <b>Enabled</b> &mdash;
- <input type="hidden" name="page" value="facebook-disable"/>
- <input type="submit" value=" Disable "/>
- </div>
- </form>
- {% else %}
- <form action="/settings" method="post">
- <div>
- Facebook: <b>Disabled</b> &mdash;
- <input type="hidden" name="page" value="facebook-enable"/>
- <input type="submit" value=" Enable "/>
- </div>
- </form>
- {% endif %}
- {% else %}
- <p>Cross-posting to Facebook: <a href="/_fblogin"><img src="//static.juick.com/facebook-connect.png" alt="Connect to Facebook"/></a></p>
- {% endif %}
- </fieldset>
- <fieldset>
- <legend style="background: url(//static.juick.com/settings/twitter.png) no-repeat; padding-left: 58px; line-height: 48px;">
- Twitter</legend>
- {% if twitter_name is not empty %}
- <form action="/settings" method="post">
- <div>Twitter: <b>{{ twitter_name }}</b> &mdash;
- <input type="hidden" name="page" value="twitter-del"/>
- <input type="submit" value=" Disable "/>
- </div>
- </form>
- {% else %}
- <p>Cross-posting to Twitter: <a href="/_twitter"><img src="//static.juick.com/twitter-connect.png"
- alt="Connect to Twitter"/></a></p>
- {% endif %}
- </fieldset>
-
-</article>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/settings_tabs" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/settings_password.html b/juick-www/src/main/resources/templates/views/settings_password.html
deleted file mode 100644
index aba0b139..00000000
--- a/juick-www/src/main/resources/templates/views/settings_password.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<article>
- <fieldset>
- <legend>Changing your password</legend>
- <form action="/settings" method="post">
- <input type="hidden" name="page" value="password"/>
- <p>Change password: <input type="password" name="password" size="8"/> <input type="submit"
- value=" Update "/><br/>
- <i>(max. length - 16 symbols)</i></p>
- </form>
- </fieldset>
-</article>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/settings_tabs" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/settings_privacy.html b/juick-www/src/main/resources/templates/views/settings_privacy.html
deleted file mode 100644
index 83b87b93..00000000
--- a/juick-www/src/main/resources/templates/views/settings_privacy.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<article>
- <p>Privacy</p>
-</article>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/settings_tabs" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/settings_result.html b/juick-www/src/main/resources/templates/views/settings_result.html
deleted file mode 100644
index d87a5ea6..00000000
--- a/juick-www/src/main/resources/templates/views/settings_result.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<article>
- <p>{{ result | raw }}</p>
-</article>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/settings_tabs" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/signup.html b/juick-www/src/main/resources/templates/views/signup.html
deleted file mode 100644
index 44fd366f..00000000
--- a/juick-www/src/main/resources/templates/views/signup.html
+++ /dev/null
@@ -1,41 +0,0 @@
-{% extends "layouts/default" %}
-{% block content %}
-<h1 class="signup-h1">
- {% if type | slice(0, 1) == 'f' %}
- <img src="//static.juick.com/settings/facebook.png" alt="Facebook"/>
- {% elseif type | slice(0, 1) == 'v' %}
- <img src="//static.juick.com/settings/vk.png" alt="VKontakte"/>
- {% elseif type | slice(0, 1) == 'x' %}
- <img src="//static.juick.com/settings/xmpp.png" alt="XMPP"/>
- {% elseif type | slice(0, 1) == 'd' %}
- <img src="//telegram.org/favicon.ico?3" alt="Telegram"/>
- {% endif %}
- {{ account | raw }}</h1>
-
-<h2 class="signup-h2">Связать с существующим аккаунтом Juick</h2>
-<form action="/signup" method="post">
- <input type="hidden" name="action" value="link"/>
- <input type="hidden" name="type" value="{{ type }}"/>
- <input type="hidden" name="hash" value="{{ hash }}"/>
- {% if visitor.getUID() > 0 %}
- <input type="submit" value="Связать с этим аккаунтом"/>
- {% else %}
- <p>Имя пользователя: <input type="text" name="username"/></p>
- <p>Пароль: <input type="password" name="password"/></p>
- <p><input type="submit" value=" OK "/></p>
- {% endif %}
-</form>
-
-<hr class="signup-hr"/>
-
-<h2 class="signup-h2">Создать новый аккаунт Juick</h2>
-<form action="/signup" method="post">
- <input type="hidden" name="action" value="new"/>
- <input type="hidden" name="type" value="{{ type }}"/>
- <input type="hidden" name="hash" value="{{ hash }}"/>
- <p>Имя пользователя: <input type="text" name="username" id="username"/><br/><i>(От 2-х до 16-и латинских символов
- и/или цифр, дефис)</i></p>
- <p>Пароль: <input type="password" name="password"/><br/><i>(от 6-и до 32-х символов)</i></p>
- <p><input type="submit" value=" OK "/></p>
-</form>
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/test.html b/juick-www/src/main/resources/templates/views/test.html
deleted file mode 100644
index 7700be6f..00000000
--- a/juick-www/src/main/resources/templates/views/test.html
+++ /dev/null
@@ -1,2 +0,0 @@
-{% import "views/macros/tags" %}
-{{ tags("ugnich", tagsList)}} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/thread.html b/juick-www/src/main/resources/templates/views/thread.html
deleted file mode 100644
index e002bc71..00000000
--- a/juick-www/src/main/resources/templates/views/thread.html
+++ /dev/null
@@ -1,170 +0,0 @@
-{% extends "layouts/default" %}
-{% import "views/macros/tags" %}
-{% block content %}
-<ul id="0">
- <li id="msg-{{ msg.mid }}" class="msg msgthread">
- <div class="msg-cont" itemscope="" itemtype="http://schema.org/BlogPosting" itemref="org">
- <div class="msg-header">
- <div class="msg-avatar">
- <a href="/{{ msg.user.name }}/"><img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/></a>
- </div>
- <span itemprop="author" itemscope="" itemtype="http://schema.org/Person">
- <a itemprop="url" rel="author" href="/{{ msg.user.name }}/"><span itemprop="name">{{ msg.user.name }}</span></a>
- </span>
- <div class="msg-ts">
- <a href="/{{ msg.user.name }}/{{ msg.mid }}">
- <time itemprop="datePublished dateModified" datetime="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }}Z"
- title="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }} GMT">
- {{ msg.timestamp | prettyTime }}
- </time>
- </a>
- </div>
- <div class="msg-tags" itemprop="headline">
- {{ tags(msg.user.name, msg.tags | tagsList) }}
- </div>
- </div>
- <div class="msg-txt" itemprop="articleBody">{{ msg | formatMessage }}</div>
- {% if msg.AttachmentType is not empty %}
- <div class="msg-media">
- <a href="//i.juick.com/p/{{ msg.mid }}.{{ msg.AttachmentType }}" data-fname="{{ msg.mid }}.{{ msg.AttachmentType }}">
- <img itemprop="image" src="//i.juick.com/photos-512/{{ msg.mid }}.{{ msg.AttachmentType }}" alt=""/>
- </a>
- </div>
- {% endif %}
- <nav class="l">
- {% if visitor.uid == msg.user.uid %}
- <a href="/{{ msg.mid }}" class="a-like msg-button">
- <span class="msg-button-icon">
- <i data-icon="ei-heart" data-size="s"></i>
- {% if msg.Likes > 0 %}&nbsp;{{ msg.Likes }}{% endif %}
- </span>
- <span>&nbsp;{{ i18n("messages","message.recommend") }}</span>
- </a>
- {% elseif visitor.uid > 0 %}
- <a href="/post?body=!+%23{{ msg.mid }}" class="a-like msg-button">
- <span class="msg-button-icon">
- <i data-icon="ei-heart" data-size="s"></i>
- {% if msg.Likes > 0 %}&nbsp;{{ msg.Likes }}{% endif %}
- </span>
- <span>&nbsp;{{ i18n("messages","message.recommend") }}</span>
- </a>
- {% else %}
- <a href="/login" class="a-login msg-button">
- <span class="msg-button-icon">
- <i data-icon="ei-heart" data-size="s"></i>
- {% if msg.Likes > 0 %}&nbsp;{{ msg.Likes }}{% endif %}
- </span>
- <span>&nbsp;{{ i18n("messages","message.recommend") }}</span>
-
- </a>
- {% endif %}
- <a href="#" class="msg-menu msg-button">
- <i data-icon="ei-link" data-size="s"></i>
- <span>&nbsp;{{ i18n("messages","message.share") }}</span>
- </a>
- {% if visitor.uid > 0 %}
- {% if visitor.uid != msg.user.uid %}
- {% if visitorSubscribed %}
- <a href="/post?body=U+%23{{ msg.mid }}" class="msg-button">
- <i data-icon="ei-check" data-size="s"></i>
- <span>&nbsp;{{ i18n("messages","message.subscribed") }}</span>
- </a>
- {% else %}
- <a href="/post?body=S+%23{{ msg.mid }}" class="msg-button">
- <i data-icon="ei-eye" data-size="s"></i>
- <span>&nbsp;{{ i18n("messages","message.subscribe") }}</span>
- </a>
- {% endif %}
- {% else %}
- <a href="/post?body=D+%23{{ msg.mid }}" class="msg-button">
- <i data-icon="ei-close" data-size="s"></i>
- <span>&nbsp;{{ i18n("messages","message.delete") }}</span>
- </a>
- {% endif %}
- {% endif %}
- {% if msg.FriendsOnly %}
- <a href="#" class="a-privacy">Открыть доступ</a>
- {% endif %}
- </nav>
- {% if msg.VisitorCanComment %}
- <form action="/comment" method="POST" enctype="multipart/form-data" class="msg-comment-target">
- <input type="hidden" name="mid" value="{{ msg.mid }}"/>
- <div class="msg-comment">
- <div class="ta-wrapper">
- <textarea name="body" rows="1" class="reply" placeholder="{{ i18n("messages","message.writeComment") }}"></textarea>
- </div>
- </div>
- </form>
- {% endif %}
- {% if recomm is not empty %}
- <div class="msg-recomms">{{ i18n("messages","message.recommendedBy") }} ({{ recomm.size() }}):
- {% for rec in recomm %}
- <a href="/{{ rec }}/">@{{ rec }}</a>{% if loop.index < (loop.length - 1) %}, {% endif %}
- {% endfor %}
- </div>
- {% endif %}
- </div>
- </li>
-</ul>
-<div class="title2">
- {% if visitor.uid > 0 %}
- <img src="https://api.juick.com/thread/mark_read/{{ msg.mid }}-{{ msg.rid }}.gif?hash={{visitor.authHash}}" />
- {% endif %}
- <h2>{{ i18n("messages","reply.replies") }} ({{ replies.size() }})</h2>
-</div>
-
-<ul id="replies">
- {% for msg in replies %}
- <li id="{{ msg.rid }}" class="msg">
- <div class="msg-cont">
- <div class="msg-header">
- {% if not msg.user.banned %}
- <a href="/{{ msg.user.name }}/">{{ msg.user.name }}</a>
- <div class="msg-avatar"><a href="/{{ msg.user.name }}/">
- <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/></a>
- </div>
- {% else %}
- [удалено]:
- <div class="msg-avatar">
- <img src="//i.juick.com/av-96.png"/>
- </div>
- {% endif %}
- <div class="msg-ts">
- <a href="/{{ msg.mid }}#{{ msg.rid }}">
- <time datetime="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }}Z"
- title="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }} GMT">
- {{ msg.timestamp | prettyTime }}
- </time>
- </a>
- </div>
- </div>
- <div class="msg-txt">{{ msg | formatMessage }}</div>
- {% if msg.AttachmentType is not empty %}
- <div class="msg-media">
- <a href="//i.juick.com/p/{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}" data-fname="{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}">
- <img src="//i.juick.com/photos-512/{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}" alt=""/>
- </a>
- </div>
- {% endif %}
- <div class="msg-links">/{{ msg.rid }}
- {% if msg.replyto > 0 %}
- {{ i18n("messages","reply.inReplyTo") }} <a href="#{{ msg.replyto }}">/{{ msg.replyto }}</a>
- {% endif %}
- {% if msg.VisitorCanComment %}
- &middot; <a href="/post?body=%23{{ msg.mid }}/{{ msg.rid }}%20" class="a-thread-comment">{{ i18n("messages","reply.reply") }}</a>
- </div>
- <div class="msg-comment-target msg-comment-hidden"></div>
- {% elseif visitor.uid == 0 %}
- &middot; <a href="#" class="a-login">{{ i18n("messages","reply.reply") }}</a>
- </div>
- {% else %}
- </div>
- {% endif %}
- </div>
- </li>
- {% endfor %}
-</ul>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/usercolumn" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/main/resources/templates/views/users.html b/juick-www/src/main/resources/templates/views/users.html
deleted file mode 100644
index 702ba6b9..00000000
--- a/juick-www/src/main/resources/templates/views/users.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "layouts/default" %}
-{% import "views/macros/tags" %}
-{% block content %}
-<div class="users">
- {% for u in users %}
- <span>
- <a href="/{{ u.name }}/">
- <img src="//i.juick.com/as/{{ u.uid }}.png" alt="{{ u.name }}"/>
- {{ u.name }}
- </a>
- </span>
- {% endfor %}
-</div>
-{% endblock %}
-{% block "column" %}
-{% include "views/partial/usercolumn" %}
-{% endblock %} \ No newline at end of file
diff --git a/juick-www/src/test/java/com/juick/WebAppTests.java b/juick-www/src/test/java/com/juick/WebAppTests.java
deleted file mode 100644
index 74be2c92..00000000
--- a/juick-www/src/test/java/com/juick/WebAppTests.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick;
-
-import com.gargoylesoftware.htmlunit.CookieManager;
-import com.gargoylesoftware.htmlunit.Page;
-import com.gargoylesoftware.htmlunit.WebClient;
-import com.gargoylesoftware.htmlunit.css.StyleElement;
-import com.gargoylesoftware.htmlunit.html.DomElement;
-import com.gargoylesoftware.htmlunit.html.HtmlPage;
-import com.juick.service.*;
-import com.juick.util.MessageUtils;
-import com.juick.www.Utils;
-import com.juick.www.WebApp;
-import com.mitchellbosecke.pebble.PebbleEngine;
-import com.mitchellbosecke.pebble.error.PebbleException;
-import com.mitchellbosecke.pebble.template.PebbleTemplate;
-import org.apache.commons.text.StringEscapeUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.http.MediaType;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.mock.web.MockMultipartFile;
-import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.MvcResult;
-import org.springframework.util.FileSystemUtils;
-
-import javax.inject.Inject;
-import javax.servlet.http.Cookie;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import java.util.stream.StreamSupport;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-/**
- * Created by vitalyster on 12.01.2017.
- */
-@RunWith(SpringRunner.class)
-@AutoConfigureMockMvc
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { Application.class})
-@TestPropertySource(properties = {"ios_app_id=12345678.com.juick.ExampleApp"})
-public class WebAppTests {
- @MockBean
- private ImagesService imagesService;
- @Inject
- private WebApp webApp;
-
- @Inject
- private MockMvc mockMvc;
- @Inject
- private WebClient webClient;
-
- @Inject
- private UserService userService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private PrivacyQueriesService privacyQueriesService;
- @Inject
- private JdbcTemplate jdbcTemplate;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private ApplicationEventPublisher applicationEventPublisher;
-
- @Inject
- private PebbleEngine pebbleEngine;
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgPath;
- @Value("${ios_app_id:}")
- private String appId;
-
- private static User ugnich, freefd;
- private static String ugnichName, ugnichPassword, freefdName, freefdPassword;
-
- private static boolean isSetUp = false;
-
- @Before
- public void setup() throws IOException {
- if (!isSetUp) {
- webClient.getOptions().setJavaScriptEnabled(false);
- webClient.getOptions().setCssEnabled(false);
- webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
-
- ugnichName = "ugnich";
- ugnichPassword = "secret";
- freefdName = "freefd";
- freefdPassword = "MyPassw0rd!";
-
- userService.createUser(ugnichName, ugnichPassword);
- ugnich = userService.getUserByName(ugnichName);
- int freefdId = userService.createUser(freefdName, freefdPassword);
- freefd = userService.getUserByUID(freefdId).orElseThrow(IllegalStateException::new);
-
- isSetUp = true;
- }
- Files.createDirectory(Paths.get(imgPath, "p"));
- Files.createDirectory(Paths.get(imgPath, "photos-1024"));
- Files.createDirectory(Paths.get(imgPath, "photos-512"));
- Files.createDirectory(Paths.get(imgPath, "ps"));
- }
-
- @After
- public void teardown() throws IOException {
- FileSystemUtils.deleteRecursively(Paths.get(imgPath, "p"));
- FileSystemUtils.deleteRecursively(Paths.get(imgPath, "photos-1024"));
- FileSystemUtils.deleteRecursively(Paths.get(imgPath, "photos-512"));
- FileSystemUtils.deleteRecursively(Paths.get(imgPath, "ps"));
- }
-
- @Test
- public void postWithoutTagsShouldNotHaveAsteriskInTitle() throws Exception {
- String msgText = "Привет, я - Угнич";
- int mid = messagesService.createMessage(ugnich.getUid(), msgText, null, null);
- HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid));
- assertThat(threadPage.getTitleText(), equalTo("ugnich:"));
- }
- @Test
- public void bannedUserBlogandPostShouldReturn404() throws IOException {
- String userName = "isilmine";
- String userPassword = "secret";
- String msgText = "автор этого поста был забанен";
- String hash = "12345678";
-
- User isilmine = userService.getUserByUID(userService.createUser(userName, userPassword)).orElseThrow(IllegalStateException::new);
- int mid = messagesService.createMessage(isilmine.getUid(), msgText, null, null);
- jdbcTemplate.update("UPDATE users SET banned=1 WHERE id=?", isilmine.getUid());
- Page blogPage = webClient.getPage("http://localhost:8080/isilmine");
- Page threadPage = webClient.getPage(String.format("http://localhost:8080/isilmine/%d", mid));
- assertThat(blogPage.getWebResponse().getStatusCode(), equalTo(404));
- assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(404));
- }
- @Test
- public void repliesList() throws IOException {
- int mid = messagesService.createMessage(ugnich.getUid(), "hello", null, null);
- IntStream.range(1, 15).forEach(i ->
- messagesService.createReply(mid, i-1, freefd, String.valueOf(i-1), null ));
-
- HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid));
- assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200));
- Long visibleItems = StreamSupport.stream(threadPage.getHtmlElementById("replies")
- .getChildElements().spliterator(), false).filter(e -> {
- StyleElement display = e.getStyleElement("display");
- return display == null || !display.getValue().equals("none");
- }).count();
- assertThat(visibleItems, equalTo(14L));
- }
- @Test
- public void userShouldNotSeeReplyButtonToBannedUser() throws Exception {
- int mid = messagesService.createMessage(ugnich.getUid(), "freefd bl me", null, null);
- messagesService.createReply(mid, 0, ugnich, "yo", null);
- MvcResult loginResult = mockMvc.perform(post("/login")
- .param("username", freefdName)
- .param("password", freefdPassword)).andReturn();
- Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me");
- webClient.setCookieManager(new CookieManager());
- webClient.getCookieManager().addCookie(
- new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(),
- loginCookie.getName(),
- loginCookie.getValue()));
- HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid));
- assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200));
- assertThat(threadPage.querySelectorAll(".msg-comment-target").isEmpty(), equalTo(false));
- assertThat(threadPage.querySelectorAll(".a-thread-comment").isEmpty(), equalTo(false));
- privacyQueriesService.blacklistUser(freefd, ugnich);
- assertThat(userService.isInBLAny(freefd.getUid(), ugnich.getUid()), equalTo(true));
- int renhaId = userService.createUser("renha", "secret");
- messagesService.createReply(mid, 0, userService.getUserByUID(renhaId).orElseThrow(IllegalStateException::new),
- "people", null);
- threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid));
- assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200));
- assertThat(threadPage.querySelectorAll(".msg-comment-target").isEmpty(), equalTo(true));
- assertThat(threadPage.querySelectorAll(".a-thread-comment").isEmpty(), equalTo(true));
- }
- @Test
- public void correctTagsEscaping() throws PebbleException, IOException {
- PebbleTemplate template = pebbleEngine.getTemplate("views/test");
- Writer writer = new StringWriter();
- template.evaluate(writer,
- Collections.singletonMap("tagsList",
- Collections.singletonList(StringEscapeUtils.escapeHtml4(new Tag(">_<").getName()))));
- String output = writer.toString().trim();
- assertThat(output, equalTo("<a href=\"/ugnich/?tag=%26gt%3B_%26lt%3B\">&gt;_&lt;</a>"));
- }
-
- public DomElement fetchMeta(String url, String name) throws IOException {
- HtmlPage page = webClient.getPage(url);
- DomElement emptyMeta = new DomElement("", "meta", null, null);
- return page.getElementsByTagName("meta").stream()
- .filter(t -> t.getAttribute("name").equals(name)).findFirst().orElse(emptyMeta);
- }
- @Test
- public void testTwitterCards() throws Exception {
-
- int mid = messagesService.createMessage(ugnich.getUid(), "without image", null, null);
-
- assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid), "twitter:card")
- .getAttribute("content"), equalTo("summary"));
- int mid2 = messagesService.createMessage(ugnich.getUid(), "with image", "png", null);
- Message message = messagesService.getMessage(mid2);
- assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid2), "twitter:card")
- .getAttribute("content"), equalTo("summary_large_image"));
- assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid2), "og:description")
- .getAttribute("content"),
- startsWith(StringEscapeUtils.escapeHtml4(MessageUtils.getMessageHashTags(message))));
- }
- @Test
- public void postMessageTests() throws Exception {
- mockMvc.perform(post("/post2").param("body", "yo")).andExpect(redirectedUrl("http://localhost/login"));
- MvcResult loginResult = mockMvc.perform(post("/login")
- .param("username", ugnichName)
- .param("password", ugnichPassword)).andReturn();
- String msgText = "yoppppl";
- mockMvc.perform(post("/post2")
- .cookie(loginResult.getResponse().getCookies())
- .param("body", msgText)).andExpect(status().isFound());
- Message lastMessage = messagesService.getMessage(messagesService.getMyFeed(ugnich.getUid(), 0, false).get(0));
- assertThat(lastMessage.getText(), equalTo(msgText));
- mockMvc.perform(post("/post2")
- .cookie(loginResult.getResponse().getCookies())
- .param("img", "http://static.juick.com/settings/facebook.png")).andExpect(status().isFound());
- lastMessage = messagesService.getMessage(messagesService.getMyFeed(ugnich.getUid(), 0, false).get(0));
- assertThat(lastMessage.getAttachmentType(), equalTo("png"));
- mockMvc.perform(post("/post2")
- .cookie(loginResult.getResponse().getCookies())
- .param("img", "bad_url")).andExpect(status().isBadRequest());
- FileInputStream fi = new FileInputStream(new ClassPathResource("static/tagscloud.png").getFile());
- MockMultipartFile file = new MockMultipartFile("attach", fi);
- mockMvc.perform(multipart("/post2")
- .file(file)
- .cookie(loginResult.getResponse().getCookies())).andExpect(status().isFound());
- int mid = messagesService.createMessage(ugnich.getUid(), "dummy message", null, null);
- mockMvc.perform(post("/comment")
- .param("mid", String.valueOf(mid))
- .param("body", "yo")).andExpect(redirectedUrl("http://localhost/login"));
- mockMvc.perform(post("/comment")
- .cookie(loginResult.getResponse().getCookies())
- .param("wrong_param", "yo")).andExpect(status().isBadRequest());
- mockMvc.perform(post("/comment")
- .cookie(loginResult.getResponse().getCookies())
- .param("mid", String.valueOf(mid))
- .param("wrong_param", "yo")).andExpect(status().isBadRequest());
- mockMvc.perform(post("/comment")
- .cookie(loginResult.getResponse().getCookies())
- .param("mid", String.valueOf(mid))
- .param("img", "http://static.juick.com/settings/facebook.png")).andExpect(status().isFound());
- mockMvc.perform(multipart("/comment")
- .file(file)
- .cookie(loginResult.getResponse().getCookies())
- .param("mid", String.valueOf(mid))).andExpect(status().isFound());
- mockMvc.perform(post("/comment")
- .cookie(loginResult.getResponse().getCookies())
- .param("mid", String.valueOf(mid))
- .param("body", "yo")).andExpect(redirectedUrl(String.format("/%s/%d#%d", ugnichName, mid, 3)));
- mockMvc.perform(post("/post2")
- .cookie(loginResult.getResponse().getCookies())
- .param("body", String.format("D #%d/%d", mid, 3)))
- .andExpect(status().isFound());
- assertThat(messagesService.getReplies(ugnich, mid).size(), equalTo(2));
- }
- @Test
- public void hashLoginShouldNotUseSession() throws Exception {
- String hash = userService.getHashByUID(ugnich.getUid());
- MvcResult hashLoginResult = mockMvc.perform(get("/?show=my&hash=" + hash))
- .andExpect(status().isOk())
- .andExpect(model().attribute("visitor", hasProperty("authHash", equalTo(hash))))
- .andExpect(content().string(containsString(hash)))
- .andReturn();
- Cookie rememberMeFromHash = hashLoginResult.getResponse().getCookie("juick-remember-me");
- MvcResult formLoginResult = mockMvc.perform(post("/login")
- .param("username", ugnichName)
- .param("password", ugnichPassword)).andReturn();
- Cookie rememberMeFromForm = formLoginResult.getResponse().getCookie("juick-remember-me");
- mockMvc.perform(get("/?show=my").cookie(rememberMeFromForm)).andExpect(status().isOk())
- .andExpect(model().attribute("visitor", hasProperty("authHash", equalTo(hash))))
- .andExpect(content().string(containsString(hash)));
- mockMvc.perform(get("/?show=my").cookie(rememberMeFromHash)).andExpect(status().isOk())
- .andExpect(model().attribute("visitor", hasProperty("authHash", equalTo(hash))))
- .andExpect(content().string(containsString(hash)));
- }
- @Test
- public void nonExistentBlogShouldReturn404() throws Exception {
- mockMvc.perform(get("/ololoe/")).andExpect(status().isNotFound());
- }
- @Test
- public void discussionsShouldBePageableByTimestamp() throws Exception {
- String msgText = "Привет, я снова Угнич";
- int mid = messagesService.createMessage(ugnich.getUid(), msgText, null, null);
- int midNew = messagesService.createMessage(ugnich.getUid(), "Я более новый Угнич", null, null);
- MvcResult loginResult = mockMvc.perform(post("/login")
- .param("username", freefdName)
- .param("password", freefdPassword)).andReturn();
- Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me");
- webClient.setCookieManager(new CookieManager());
- webClient.getCookieManager().addCookie(
- new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(),
- loginCookie.getName(),
- loginCookie.getValue()));
- String discussionsUrl = "http://localhost:8080/?show=discuss";
- HtmlPage discussions = webClient.getPage(discussionsUrl);
- assertThat(discussions.querySelectorAll("article").size(), is(0));
- subscriptionService.subscribeMessage(messagesService.getMessage(mid), freefd);
- discussions = (HtmlPage) discussions.refresh();
- assertThat(discussions.querySelectorAll("article").size(), is(1));
- subscriptionService.subscribeMessage(messagesService.getMessage(midNew), freefd);
- discussions = (HtmlPage) discussions.refresh();
- assertThat(discussions.querySelectorAll("article").size(), is(2));
- assertThat(discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew)));
- messagesService.createReply(mid, 0, freefd, "I'm replied", null);
- discussions = (HtmlPage) discussions.refresh();
- assertThat(discussions.querySelectorAll("article").size(), is(2));
- assertThat(discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid)));
- Message msg = messagesService.getMessage(mid);
- HtmlPage discussionsOld = webClient.getPage(discussionsUrl + "&to=" + msg.getUpdated().toEpochMilli());
- assertThat(discussionsOld.querySelectorAll("article").size(), is(1));
- assertThat(discussionsOld.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew)));
- List<Integer> newMids = IntStream.rangeClosed(1, 19).map(i -> messagesService.createMessage(ugnich.getUid(), String.valueOf(i), null, null)).boxed().collect(Collectors.toList());
- for (Integer m : newMids) {
- subscriptionService.subscribeMessage(messagesService.getMessage(m), freefd);
- }
- discussions = (HtmlPage) discussions.refresh();
- assertThat(discussions.querySelectorAll("article").size(), is(20));
- assertThat(discussions.querySelectorAll("article")
- .get(19).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid)));
- messagesService.createReply(midNew, 0, freefd, "I'm replied", null);
- discussions = (HtmlPage) discussions.refresh();
- assertThat(discussions.querySelectorAll("article")
- .get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew)));
- Message old = messagesService.getMessage(newMids.get(0));
- discussionsOld = webClient.getPage(discussionsUrl + "&to=" + old.getUpdated().toEpochMilli());
- assertThat(discussionsOld.querySelectorAll("article").size(), is(1));
- assertThat(discussionsOld.querySelectorAll("article")
- .get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid)));
- }
- @Test
- public void redirectParamShouldCorrectlyRedirectLoggedUser() throws Exception {
- MvcResult formLoginResult = mockMvc.perform(post("/login")
- .param("username", ugnichName)
- .param("password", ugnichPassword)).andReturn();
- Cookie rememberMeFromForm = formLoginResult.getResponse().getCookie("juick-remember-me");
- mockMvc.perform(get("/login").cookie(rememberMeFromForm))
- .andExpect(status().is3xxRedirection())
- .andExpect(redirectedUrl("/"));
- mockMvc.perform(get("/login?redirect=false").cookie(rememberMeFromForm))
- .andExpect(status().is3xxRedirection())
- .andExpect(redirectedUrl("/login/success"));
- }
- @Test
- public void anythingRedirects() throws Exception {
- int mid = messagesService.createMessage(ugnich.getUid(), "yo", null, null);
- mockMvc.perform(get(String.format("/%d", mid)))
- .andExpect(status().isMovedPermanently())
- .andExpect(redirectedUrl(String.format("/%s/%d", ugnich.getName(), mid)));
- }
- @Test
- public void appAssociationsTest() throws Exception {
- mockMvc.perform((get("/.well-known/apple-app-site-association")))
- .andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
- .andExpect(jsonPath("$.webcredentials.apps[0]", is(appId)));
- }
- @Test
- public void notificationsTests() throws Exception {
- MvcResult loginResult = mockMvc.perform(post("/login")
- .param("username", freefdName)
- .param("password", freefdPassword)).andReturn();
- Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me");
- webClient.setCookieManager(new CookieManager());
- webClient.getCookieManager().addCookie(
- new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(),
- loginCookie.getName(),
- loginCookie.getValue()));
- int mid = messagesService.createMessage(ugnich.getUid(), "new test", null, null);
- subscriptionService.subscribeMessage(messagesService.getMessage(mid), freefd);
- int rid = messagesService.createReply(mid, 0, ugnich, "new reply", null);
- HtmlPage discussionsPage = webClient.getPage("http://localhost:8080/?show=discuss");
- assertThat(discussionsPage.querySelectorAll("#global a .badge").size(), is(1));
- HtmlPage unreadThread = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid));
- assertThat(unreadThread.querySelectorAll("#global a .badge").size(), is(0));
- }
- @Test
- public void escapeSqlTests() {
- String sql = String.format("SELECT * FROM table WHERE data='%s'", Utils.encodeSphinx("';-- DROP TABLE table"));
- assertThat(sql, is("SELECT * FROM table WHERE data='\\';-- DROP TABLE table\'"));
- }
-}