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