From 93c8d1a052753584a4f55118f6753a952a3089a9 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 24 Aug 2018 14:48:50 +0300 Subject: drop www files --- juick-www/src/main/assets/embed.js | 336 -------- juick-www/src/main/assets/logo.png | Bin 2447 -> 0 bytes juick-www/src/main/assets/logo@2x.png | Bin 4822 -> 0 bytes juick-www/src/main/assets/scripts.js | 802 ----------------- juick-www/src/main/assets/style.css | 951 --------------------- juick-www/src/main/java/com/juick/Application.java | 25 - .../java/com/juick/service/CloudflareCache.java | 55 -- .../com/juick/service/cloudflare/FilesPayload.java | 20 - .../src/main/java/com/juick/www/HelpService.java | 69 -- juick-www/src/main/java/com/juick/www/WebApp.java | 71 -- .../juick/www/configuration/EmbeddedAPIConfig.java | 19 - .../juick/www/configuration/SapeConfiguration.java | 37 - .../juick/www/configuration/WebSecurityConfig.java | 140 --- .../www/configuration/WwwAppConfiguration.java | 123 --- .../com/juick/www/controllers/AnythingFilter.java | 64 -- .../juick/www/controllers/AppSiteAssociation.java | 49 -- .../main/java/com/juick/www/controllers/Help.java | 93 -- .../main/java/com/juick/www/controllers/Login.java | 50 -- .../com/juick/www/controllers/MessagesWWW.java | 595 ------------- .../java/com/juick/www/controllers/NewMessage.java | 263 ------ .../java/com/juick/www/controllers/Settings.java | 269 ------ .../java/com/juick/www/controllers/SignUp.java | 156 ---- juick-www/src/main/resources/errors.properties | 3 - juick-www/src/main/resources/errors_ru.properties | 3 - juick-www/src/main/resources/help | 1 - juick-www/src/main/resources/messages.properties | 79 -- .../src/main/resources/messages_ru.properties | 77 -- juick-www/src/main/resources/static/favicon.png | Bin 244 -> 0 bytes juick-www/src/main/resources/static/logo.png | Bin 1184 -> 0 bytes juick-www/src/main/resources/static/tagscloud.png | Bin 42316 -> 0 bytes .../main/resources/templates/layouts/content.html | 50 -- .../main/resources/templates/layouts/default.html | 16 - .../main/resources/templates/layouts/minimal.html | 10 - .../src/main/resources/templates/views/404.html | 11 - .../src/main/resources/templates/views/blog.html | 25 - .../main/resources/templates/views/blog_tags.html | 10 - .../src/main/resources/templates/views/help.html | 10 - .../src/main/resources/templates/views/index.html | 29 - .../src/main/resources/templates/views/login.html | 144 ---- .../resources/templates/views/login_success.html | 13 - .../resources/templates/views/macros/tags.html | 5 - .../resources/templates/views/partial/footer.html | 16 - .../templates/views/partial/homecolumn.html | 25 - .../resources/templates/views/partial/message.html | 76 -- .../templates/views/partial/navigation.html | 40 - .../templates/views/partial/settings_tabs.html | 6 - .../templates/views/partial/tagcolumn.html | 33 - .../resources/templates/views/partial/tags.html | 3 - .../templates/views/partial/usercolumn.html | 89 -- .../templates/views/partial/usertags.html | 3 - .../main/resources/templates/views/pm_inbox.html | 35 - .../main/resources/templates/views/pm_sent.html | 33 - .../src/main/resources/templates/views/post.html | 19 - .../resources/templates/views/post_success.html | 19 - .../resources/templates/views/settings_about.html | 20 - .../templates/views/settings_auth-email.html | 9 - .../resources/templates/views/settings_main.html | 151 ---- .../templates/views/settings_password.html | 17 - .../templates/views/settings_privacy.html | 9 - .../resources/templates/views/settings_result.html | 9 - .../src/main/resources/templates/views/signup.html | 41 - .../src/main/resources/templates/views/test.html | 2 - .../src/main/resources/templates/views/thread.html | 170 ---- .../src/main/resources/templates/views/users.html | 17 - juick-www/src/test/java/com/juick/WebAppTests.java | 423 --------- 65 files changed, 5938 deletions(-) delete mode 100644 juick-www/src/main/assets/embed.js delete mode 100644 juick-www/src/main/assets/logo.png delete mode 100644 juick-www/src/main/assets/logo@2x.png delete mode 100644 juick-www/src/main/assets/scripts.js delete mode 100644 juick-www/src/main/assets/style.css delete mode 100644 juick-www/src/main/java/com/juick/Application.java delete mode 100644 juick-www/src/main/java/com/juick/service/CloudflareCache.java delete mode 100644 juick-www/src/main/java/com/juick/service/cloudflare/FilesPayload.java delete mode 100644 juick-www/src/main/java/com/juick/www/HelpService.java delete mode 100644 juick-www/src/main/java/com/juick/www/WebApp.java delete mode 100644 juick-www/src/main/java/com/juick/www/configuration/EmbeddedAPIConfig.java delete mode 100644 juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java delete mode 100644 juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java delete mode 100644 juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/AnythingFilter.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/AppSiteAssociation.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/Help.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/Login.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/NewMessage.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/Settings.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/SignUp.java delete mode 100644 juick-www/src/main/resources/errors.properties delete mode 100644 juick-www/src/main/resources/errors_ru.properties delete mode 160000 juick-www/src/main/resources/help delete mode 100644 juick-www/src/main/resources/messages.properties delete mode 100644 juick-www/src/main/resources/messages_ru.properties delete mode 100644 juick-www/src/main/resources/static/favicon.png delete mode 100644 juick-www/src/main/resources/static/logo.png delete mode 100644 juick-www/src/main/resources/static/tagscloud.png delete mode 100644 juick-www/src/main/resources/templates/layouts/content.html delete mode 100644 juick-www/src/main/resources/templates/layouts/default.html delete mode 100644 juick-www/src/main/resources/templates/layouts/minimal.html delete mode 100644 juick-www/src/main/resources/templates/views/404.html delete mode 100644 juick-www/src/main/resources/templates/views/blog.html delete mode 100644 juick-www/src/main/resources/templates/views/blog_tags.html delete mode 100644 juick-www/src/main/resources/templates/views/help.html delete mode 100644 juick-www/src/main/resources/templates/views/index.html delete mode 100644 juick-www/src/main/resources/templates/views/login.html delete mode 100644 juick-www/src/main/resources/templates/views/login_success.html delete mode 100644 juick-www/src/main/resources/templates/views/macros/tags.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/footer.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/homecolumn.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/message.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/navigation.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/settings_tabs.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/tagcolumn.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/tags.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/usercolumn.html delete mode 100644 juick-www/src/main/resources/templates/views/partial/usertags.html delete mode 100644 juick-www/src/main/resources/templates/views/pm_inbox.html delete mode 100644 juick-www/src/main/resources/templates/views/pm_sent.html delete mode 100644 juick-www/src/main/resources/templates/views/post.html delete mode 100644 juick-www/src/main/resources/templates/views/post_success.html delete mode 100644 juick-www/src/main/resources/templates/views/settings_about.html delete mode 100644 juick-www/src/main/resources/templates/views/settings_auth-email.html delete mode 100644 juick-www/src/main/resources/templates/views/settings_main.html delete mode 100644 juick-www/src/main/resources/templates/views/settings_password.html delete mode 100644 juick-www/src/main/resources/templates/views/settings_privacy.html delete mode 100644 juick-www/src/main/resources/templates/views/settings_result.html delete mode 100644 juick-www/src/main/resources/templates/views/signup.html delete mode 100644 juick-www/src/main/resources/templates/views/test.html delete mode 100644 juick-www/src/main/resources/templates/views/thread.html delete mode 100644 juick-www/src/main/resources/templates/views/users.html delete mode 100644 juick-www/src/test/java/com/juick/WebAppTests.java (limited to 'juick-www/src') 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) - ? `${p1}` - : `${extractDomain(match)}`; -} - -function urlReplaceInCode(match, p1, p2, p3) { - let isBrackets = (p1 !== undefined); - return (isBrackets) - ? `${match}` - : `${match}`; -} - -function messageReplyReplace(messageId) { - return function(match, mid, rid) { - let replyPart = (rid && rid != '0') ? '#' + rid : ''; - return `${match}`; - }; -} - -/** - * 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(/^(?:>|>)\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: '@$1' }, - ]) - : formatText(txt, [ - { pr: 0, re: /((?:^(?:>|>)\s?[\s\S]+?$\n?)+)/gmi, brackets: true, with: ['', '', 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: '@$1' }, - { pr: 2, re: /\B\*([^\n]+?)\*((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['', ''] }, - { pr: 2, re: /\B\/([^\n]+?)\/((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['', ''] }, - { pr: 2, re: /\b\_([^\n]+?)\_((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['', ''] }, - { pr: 3, re: /\n/g, with: '
' }, - ]); -} - -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 = ``; - 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 = ``; - 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 = ``; - 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 = ``; - 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 Binary files a/juick-www/src/main/assets/logo.png and /dev/null 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 Binary files a/juick-www/src/main/assets/logo@2x.png and /dev/null 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 `
`; -} - -/* 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')} /${msg.replyto}`; - } - let photoDiv = (msg.attach == null) ? '' : ` -
- -
`; - let msgContHtml = ` -
-
- ${msg.user.uname}: -
- ${msg.user.uname} -
- -
-
${killy.format(msg.body, msg.mid, false)}
${photoDiv} - -
-
`; - - 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', `

${i18n('postForm.pleaseInputMessageText')}

`); - 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 = ` -
- - -
-
- -
${evilIcon('ei-camera')}
-
- -
-
`; - 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 = ` -
- ${i18n('shareDialog.linkToMessage')}:
${hlink}
- ${i18n('shareDialog.messageNumber')}:
${mlink}
- ${i18n('shareDialog.share')}: - -
`; - - 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(``, true); - return false; - } else { - openDialog(``, true); - return false; - } -} - -function openPostDialog() { - let newmessageTemplate = ` -
- -
- - ${i18n('postForm.or')} ${i18n('postForm.upload')}
-
- -
-
- `; - return openDialog(newmessageTemplate); -} - -function openDialog(html, image) { - var dialogHtml = ` -
-
-
-
-
${evilIcon('ei-close')}
-
- ${html} -
-
`; - 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 = ` -
-

${i18n('loginDialog.pleaseIntroduceYourself')}:

- ${evilIcon('ei-sc-facebook')}${i18n('loginDialog.facebook')} - ${evilIcon('ei-sc-vk')}${i18n('loginDialog.vk')} -

${i18n('loginDialog.registeredAlready')}

-
-
-
- -
-
`; - 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', ''); - 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 { - 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 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 files; - - public FilesPayload() { - files = new ArrayList<>(); - } - - public List getFiles() { - return files; - } - - public void setFiles(List 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 . - */ - -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 . - */ -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 parseTags(String tagsStr) { - List tags = new ArrayList<>(); - if (tagsStr != null && !tagsStr.isEmpty()) { - Stream 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 . - */ - -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 . - */ - -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 . - */ - -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 apps; - - public List getApps() { - return apps; - } - - public void setApps(List 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 . - */ - -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 . - */ -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 . - */ -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 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 = ""; - } - model.addAttribute("title", title); - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("noindex", !(paramShow == null && paramBefore == 0)); - List msgs = messagesService.getMessages(mids); - - if (!visitor.isAnonymous()) { - fillUserModel(model, visitor, visitor); - List unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List 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 += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&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 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 = ""; - if (paramTag != null && tagService.getTagNoIndex(paramTag.TID)) { - head += ""; - } else if (before > 0 || paramShow != null) { - head += ""; - } - 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 msgs = messagesService.getMessages(mids); - - if (!visitor.isAnonymous()) { - List unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List 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 += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); - } - if (paramTag != null) { - nextpage += "&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", ""); - 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", ""); - 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", ""); - 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", ""); - 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 mids = messagesService.getTag(paramTag.TID, visitor.getUid(), before, (visitor.isAnonymous()) ? 40 : 20); - List msgs = messagesService.getMessages(mids); - if (!visitor.isAnonymous()) { - List unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List 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 = ""; - } else if (before > 0 || mids.size() < 5) { - head = ""; - } - 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 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 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 replies = messagesService.getReplies(visitor, msg.getMid()); - // this should be after getReplies to mark thread as read - fillUserModel(model, user, visitor); - if (!visitor.isAnonymous()) { - List 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 = ""; - String pageUrl = "https://juick.com/" + msg.getUser().getName() + "/" + msg.getMid(); - if (msg.Hidden) { - headers += ""; - } - 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 += ""; - } - } else { - String msgImage ="https://i.juick.com/a/" + msg.getUser().getUid() + ".png"; - headers += ""; - } - model.addAttribute("ogtype", "article"); - String cardDescription = StringEscapeUtils.escapeHtml4(PlainTextFormatter.formatTwitterCard(msg)); - headers += "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n"; - String twitterName = crosspostService.getTwitterName(msg.getUser().getUid()); - if (StringUtils.isNotEmpty(twitterName)) { - headers += "\n"; - } - if (msg.getTags().size() > 0) { - headers += "\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 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 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 . - */ -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 params = new LinkedMultiValueMap<>(); - HttpEntity> 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 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 params = new LinkedMultiValueMap<>(); - HttpEntity> 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 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 params = new LinkedMultiValueMap<>(); - HttpEntity> 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 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 . - */ -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 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 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 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 = "

Password has been changed.

"; - 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 = "

Notification options has been updated

"; - } - 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("

Your info is updated.

Back to blog.

", 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 = "

Deleted. Back.

"; - } else { - result = "

Error

"; - } - 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 = "

Authorization link has been sent to your email. Follow it to proceed.

" + - "

Back

"; - - } catch (MessagingException ex) { - logger.error("mail exception", ex); - throw new HttpBadRequestException(); - } - } - } - break; - case "email-del": - if (emailService.deleteEmail(visitor.getUid(), request.getParameter("account"))) { - result = "

Deleted. Back.

"; - } else { - result = "

An error occured while deleting.

"; - } - break; - case "email-subscr": - if (emailService.setNotificationsEmail(visitor.getUid(), request.getParameter("account"))) { - result = String.format("

Saved! Will send notifications to %s." + - "

Back

", request.getParameter("account")); - } else { - result = "

Disabled.

Back

"; - } - 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 = "

Back

"; - break; - case "telegram-del": - telegramService.deleteTelegramUser(visitor.getUid()); - telegramService.getTelegramIdentifiers(Collections.singletonList(visitor)).forEach(t -> { - telegramService.deleteChat(t); - }); - result = "

Back

"; - break; - case "facebook-disable": - crosspostService.disableFBCrosspost(visitor.getUid()); - result = "

Back

"; - break; - case "facebook-enable": - crosspostService.enableFBCrosspost(visitor.getUid()); - result = "

Back

"; - break; - case "vk-del": - crosspostService.deleteVKUser(visitor.getUid()); - result = "

Back

"; - 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 . - */ -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 index ce103cd9..00000000 --- a/juick-www/src/main/resources/help +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ce103cd9a2a8a200c6ebb3b41525e7c8f639d98c 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= to post messages and comments -message.sendLoginToXmpp=Send LOGIN to juick@juick.com - -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=Чтобы добавлять сообщения и комментарии, -message.sendLoginToXmpp=Отправьте LOGIN на juick@juick.com - -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 Binary files a/juick-www/src/main/resources/static/favicon.png and /dev/null 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 Binary files a/juick-www/src/main/resources/static/logo.png and /dev/null 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 Binary files a/juick-www/src/main/resources/static/tagscloud.png and /dev/null 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 @@ - - - - - - - - {% block headers %} - {{ headers | default('') | raw }} - {% endblock %} - {{ title | default('Juick') }} - - - - - - - - - - - - - - - - - - - - - - - - - - 0 %}data-hash="{{visitor.authHash}}"{% endif %}> -{% block body %} -{% endblock %} - - 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" %} -
-
- {% block content %} - {% endblock %} -
- -
-{% 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 %} -
-
- {% block content %} - {% endblock %} -
-
-{% 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 %} -
-

Страница не найдена

-

Сожалеем, но страницу с этим адресом удалил её автор, либо её никогда не существовало.

-
-{% 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 %} - -{% endif %} -{% if paramTag | default('') is not empty %} -

← {{ i18n("messages","blog.allPostsWithTag") }} {{ paramTag.name | escape }}

-{% endif %} -
- -{% for msg in msgs %} -{% include "views/partial/message" %} -{% endfor %} -
-{% if nextpage | default('') is not empty %} -

-{% endif %} -{% endblock %} -{% block "column" %} -{% include "views/partial/usercolumn" %} -{% if 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 %} -

- {{ tags(user.name, tags) }} -

-{% 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 %} -
- {{ content | raw }} -
-{% 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 %} - -{% endif %} -{% for msg in msgs %} -{% include "views/partial/message" %} -{% endfor %} -{% if nextpage | default('') is not empty %} -

-{% 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 %} - -{% 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 @@ - - - - Juick - - - - - - - - - -
juick.com © 2008-2018   Контакты · Помощь
- -
- {{ i18n("messages","label.register") }}: - - -
- -
-
- - - - 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 @@ - - - - - Blank window - - - - - 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 %} -{{ tag | raw }} -{% 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 @@ - 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 @@ - -
-

{{ i18n("messages","link.trends") }}

- {% include "views/partial/tags" %} - {% if showAdv | default(false) %} -

Наши друзья

- конструктор сайтов - {% endif %} -
\ 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 @@ -
-
- -
- {{ msg.user.name }} -
- -
- {{ tags(msg.user.name, msg.tags | tagsList) }} -
-
-

{{ msg | formatMessage }}

- {% if msg.AttachmentType is not empty %} -

- -

- {% endif %} - -
\ 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 @@ -
-
- {% if visitor.uid > 0 %} - - {% else %} - - {% endif %} - - -
-
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 @@ - \ 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 @@ -
-

*{{ tag.name }}

-
-{% if visitor is not empty and visitor.uid > 0 %} - -{% 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 %} - {{ tag | raw }} -{% 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 %} - - -{% else %} -
-{% endif %} - -
-
-

-
-{% include "views/partial/usertags" %} -
-
- - {% if iread is not empty %} -
- {% for u in iread %} - - - {{ u.name }} - - - {% endfor %} -
- {% endif %} - -
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) }} -... \ 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() %} -
    - {% for msg in msgs %} -
  • -
    -
    - @{{ msg.user.name }}: -
    - - {{ msg.user.name }} - -
    -
    {{ msg.timestamp | prettyTime }}
    -
    - -
    {{ msg | formatMessage }}
    -
    - -
    -
    - -
    -
    -
    -
    -
  • - {% endfor %} -
-{% 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 %} -
-
-
To:
-
-
-
-
-{% if not msgs.isEmpty() %} -
    - {% for msg in msgs %} -
  • -
    -
    - @{{ msg.user.name }}: -
    - - {{ msg.user.name }} - -
    -
    {{ msg.timestamp | prettyTime }}
    -
    -
    {{ msg | formatMessage }}
    -
    -
  • - {% endfor %} -
-{% 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 %} -
-
-

- Фото: - ({{ i18n("messages","postForm.imageFormats") }}) -

-

- -
- -

-
-
-

Теги:

-{{ 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 %} -

Сообщение опубликовано

-

Поделитесь своим новым постом в социальных сетях:

-{% if sharetwi | default('') is not empty %} - -{% endif %} - -{% if facebook | default('') is not empty %} - -{% endif %} -

Ссылка на сообщение: {{ url }}

-{% 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 %} -
-
-

Full name:

-

Country:

-

URL:
- Please, start with "http://"

-

About:
-
- Max. 255 symbols

-

Avatar:
- Recommendations: PNG, 96x96, <50Kb. Also, JPG and GIF supported.

-

-
-
-{% 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 %} - -{% 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 %} -
-

Настройки

-
-
- Notification options -

Reply notifications ("Message posted")

-

Subscriptions notifications ("@user subscribed...")

-

Posts recommendations ("Recommended by @user")

-

-
-
-
- - Telegram - {% if telegram_name is not empty %} -
-
Telegram: {{ telegram_name }} — - - -
-
- {% else %} -

To connect Telegram account: send any text message to @Juick_bot -

- {% endif %} -
- {% if jids | length > 0 %} -
-
- - XMPP accounts - -

Your accounts:

-

- {% for jid in jids %} -
- {% endfor %} - {% for auth in auths %} - - — Confirm
- {% endfor %} -

- {% if jids | length > 1 %} -

- {% endif %} -

To add new jabber account: send any text message to juick@juick.com -

-
-
- {% endif %} -
- - E-mail - -
-

Add account:
- - - -

-
-
-

Your accounts:

-

- {% for email in emails %} -
- {% endfor %} - {% if emails is empty %} - -

- {% else %} -

- {% if jids | length > 1 %} -

- {% endif %} - {% endif %} -
- {% if emails is not empty %} - -
-

You can receive notifications to email:
- Sent to - -

-
- - {% endif %} -

 

-

You can post to Juick via e-mail. Send your plain text - messages to juick@juick.com. You can attach one photo or video file.

-
-
- - Facebook - - {% if fbstatus.connected %} - {% if fbstatus.crosspostEnabled %} -
-
- Facebook: Enabled — - - -
-
- {% else %} -
-
- Facebook: Disabled — - - -
-
- {% endif %} - {% else %} -

Cross-posting to Facebook: Connect to Facebook

- {% endif %} -
-
- - Twitter - {% if twitter_name is not empty %} -
-
Twitter: {{ twitter_name }} — - - -
-
- {% else %} -

Cross-posting to Twitter: Connect to Twitter

- {% endif %} -
- -
-{% 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 %} -
-
- Changing your password -
- -

Change password:
- (max. length - 16 symbols)

-
-
-
-{% 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 %} -
-

Privacy

-
-{% 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 %} -
-

{{ result | raw }}

-
-{% 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 %} -

- {% if type | slice(0, 1) == 'f' %} - Facebook - {% elseif type | slice(0, 1) == 'v' %} - VKontakte - {% elseif type | slice(0, 1) == 'x' %} - XMPP - {% elseif type | slice(0, 1) == 'd' %} - Telegram - {% endif %} - {{ account | raw }}

- - -
- - - - {% if visitor.getUID() > 0 %} - - {% else %} -

Имя пользователя:

-

Пароль:

-

- {% endif %} -
- - - - -
- - - -

Имя пользователя:
(От 2-х до 16-и латинских символов - и/или цифр, дефис)

-

Пароль:
(от 6-и до 32-х символов)

-

-
-{% 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 %} - -
- {% if visitor.uid > 0 %} - - {% endif %} -

{{ i18n("messages","reply.replies") }} ({{ replies.size() }})

-
- - -{% 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 %} -
- {% for u in users %} - - - {{ u.name }} - {{ u.name }} - - - {% endfor %} -
-{% 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 . - */ - -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(">_<")); - } - - 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 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\'")); - } -} -- cgit v1.2.3