aboutsummaryrefslogtreecommitdiff
path: root/juick-www
diff options
context:
space:
mode:
Diffstat (limited to 'juick-www')
-rw-r--r--juick-www/package-lock.json907
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/PageTemplates.java7
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/UserThread.java318
-rw-r--r--juick-www/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java2
-rw-r--r--juick-www/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatRepliesFilter.java48
-rw-r--r--juick-www/src/main/webapp/WEB-INF/layouts/content.html3
-rw-r--r--juick-www/src/main/webapp/WEB-INF/views/macros/tree.html54
-rw-r--r--juick-www/src/main/webapp/WEB-INF/views/partial/message.html6
-rw-r--r--juick-www/src/main/webapp/WEB-INF/views/partial/thread_list.html44
-rw-r--r--juick-www/src/main/webapp/WEB-INF/views/partial/thread_tree.html2
-rw-r--r--juick-www/src/main/webapp/WEB-INF/views/thread.html108
11 files changed, 311 insertions, 1188 deletions
diff --git a/juick-www/package-lock.json b/juick-www/package-lock.json
index 9a0871cf..b47230b6 100644
--- a/juick-www/package-lock.json
+++ b/juick-www/package-lock.json
@@ -1071,7 +1071,6 @@
"requires": {
"anymatch": "1.3.2",
"async-each": "1.0.1",
- "fsevents": "1.1.2",
"glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
"inherits": "2.0.3",
"is-binary-path": "1.0.1",
@@ -2399,905 +2398,6 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
- "fsevents": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz",
- "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==",
- "dev": true,
- "optional": true,
- "requires": {
- "nan": "2.6.2",
- "node-pre-gyp": "0.6.36"
- },
- "dependencies": {
- "abbrev": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ajv": {
- "version": "4.11.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "co": "4.6.0",
- "json-stable-stringify": "1.0.1"
- }
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "dev": true
- },
- "aproba": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "delegates": "1.0.0",
- "readable-stream": "2.2.9"
- }
- },
- "asn1": {
- "version": "0.2.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "assert-plus": {
- "version": "0.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "asynckit": {
- "version": "0.4.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aws-sign2": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aws4": {
- "version": "1.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "balanced-match": {
- "version": "0.4.2",
- "bundled": true,
- "dev": true
- },
- "bcrypt-pbkdf": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "tweetnacl": "0.14.5"
- }
- },
- "block-stream": {
- "version": "0.0.9",
- "bundled": true,
- "dev": true,
- "requires": {
- "inherits": "2.0.3"
- }
- },
- "boom": {
- "version": "2.10.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "hoek": "2.16.3"
- }
- },
- "brace-expansion": {
- "version": "1.1.7",
- "bundled": true,
- "dev": true,
- "requires": {
- "balanced-match": "0.4.2",
- "concat-map": "0.0.1"
- }
- },
- "buffer-shims": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "caseless": {
- "version": "0.12.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "co": {
- "version": "4.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true
- },
- "combined-stream": {
- "version": "1.0.5",
- "bundled": true,
- "dev": true,
- "requires": {
- "delayed-stream": "1.0.0"
- }
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "dev": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- },
- "cryptiles": {
- "version": "2.0.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "boom": "2.10.1"
- }
- },
- "dashdash": {
- "version": "1.14.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "assert-plus": "1.0.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "debug": {
- "version": "2.6.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "deep-extend": {
- "version": "0.4.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "delayed-stream": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ecc-jsbn": {
- "version": "0.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "jsbn": "0.1.1"
- }
- },
- "extend": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "extsprintf": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- },
- "forever-agent": {
- "version": "0.6.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "form-data": {
- "version": "2.1.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "asynckit": "0.4.0",
- "combined-stream": "1.0.5",
- "mime-types": "2.1.15"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "fstream": {
- "version": "1.0.11",
- "bundled": true,
- "dev": true,
- "requires": {
- "graceful-fs": "4.1.11",
- "inherits": "2.0.3",
- "mkdirp": "0.5.1",
- "rimraf": "2.6.1"
- }
- },
- "fstream-ignore": {
- "version": "1.0.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "fstream": "1.0.11",
- "inherits": "2.0.3",
- "minimatch": "3.0.4"
- }
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aproba": "1.1.1",
- "console-control-strings": "1.1.0",
- "has-unicode": "2.0.1",
- "object-assign": "4.1.1",
- "signal-exit": "3.0.2",
- "string-width": "1.0.2",
- "strip-ansi": "3.0.1",
- "wide-align": "1.1.2"
- }
- },
- "getpass": {
- "version": "0.1.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "assert-plus": "1.0.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "glob": {
- "version": "7.1.2",
- "bundled": true,
- "dev": true,
- "requires": {
- "fs.realpath": "1.0.0",
- "inflight": "1.0.6",
- "inherits": "2.0.3",
- "minimatch": "3.0.4",
- "once": "1.4.0",
- "path-is-absolute": "1.0.1"
- }
- },
- "graceful-fs": {
- "version": "4.1.11",
- "bundled": true,
- "dev": true
- },
- "har-schema": {
- "version": "1.0.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "har-validator": {
- "version": "4.2.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ajv": "4.11.8",
- "har-schema": "1.0.5"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "hawk": {
- "version": "3.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "boom": "2.10.1",
- "cryptiles": "2.0.5",
- "hoek": "2.16.3",
- "sntp": "1.0.9"
- }
- },
- "hoek": {
- "version": "2.16.3",
- "bundled": true,
- "dev": true
- },
- "http-signature": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "assert-plus": "0.2.0",
- "jsprim": "1.4.0",
- "sshpk": "1.13.0"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "dev": true,
- "requires": {
- "once": "1.4.0",
- "wrappy": "1.0.2"
- }
- },
- "inherits": {
- "version": "2.0.3",
- "bundled": true,
- "dev": true
- },
- "ini": {
- "version": "1.3.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "requires": {
- "number-is-nan": "1.0.1"
- }
- },
- "is-typedarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "isstream": {
- "version": "0.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "jodid25519": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "jsbn": "0.1.1"
- }
- },
- "jsbn": {
- "version": "0.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "json-schema": {
- "version": "0.2.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "json-stable-stringify": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "jsonify": "0.0.0"
- }
- },
- "json-stringify-safe": {
- "version": "5.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "jsonify": {
- "version": "0.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "jsprim": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "assert-plus": "1.0.0",
- "extsprintf": "1.0.2",
- "json-schema": "0.2.3",
- "verror": "1.3.6"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "mime-db": {
- "version": "1.27.0",
- "bundled": true,
- "dev": true
- },
- "mime-types": {
- "version": "2.1.15",
- "bundled": true,
- "dev": true,
- "requires": {
- "mime-db": "1.27.0"
- }
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "dev": true,
- "requires": {
- "brace-expansion": "1.1.7"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "bundled": true,
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "ms": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "node-pre-gyp": {
- "version": "0.6.36",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "mkdirp": "0.5.1",
- "nopt": "4.0.1",
- "npmlog": "4.1.0",
- "rc": "1.2.1",
- "request": "2.81.0",
- "rimraf": "2.6.1",
- "semver": "5.3.0",
- "tar": "2.2.1",
- "tar-pack": "3.4.0"
- }
- },
- "nopt": {
- "version": "4.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "abbrev": "1.1.0",
- "osenv": "0.1.4"
- }
- },
- "npmlog": {
- "version": "4.1.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "1.1.4",
- "console-control-strings": "1.1.0",
- "gauge": "2.7.4",
- "set-blocking": "2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true
- },
- "oauth-sign": {
- "version": "0.8.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "requires": {
- "wrappy": "1.0.2"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "os-homedir": "1.0.2",
- "os-tmpdir": "1.0.2"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true
- },
- "performance-now": {
- "version": "0.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "1.0.7",
- "bundled": true,
- "dev": true
- },
- "punycode": {
- "version": "1.4.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "qs": {
- "version": "6.4.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "deep-extend": "0.4.2",
- "ini": "1.3.4",
- "minimist": "1.2.0",
- "strip-json-comments": "2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "readable-stream": {
- "version": "2.2.9",
- "bundled": true,
- "dev": true,
- "requires": {
- "buffer-shims": "1.0.0",
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "1.0.7",
- "string_decoder": "1.0.1",
- "util-deprecate": "1.0.2"
- }
- },
- "request": {
- "version": "2.81.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aws-sign2": "0.6.0",
- "aws4": "1.6.0",
- "caseless": "0.12.0",
- "combined-stream": "1.0.5",
- "extend": "3.0.1",
- "forever-agent": "0.6.1",
- "form-data": "2.1.4",
- "har-validator": "4.2.1",
- "hawk": "3.1.3",
- "http-signature": "1.1.1",
- "is-typedarray": "1.0.0",
- "isstream": "0.1.2",
- "json-stringify-safe": "5.0.1",
- "mime-types": "2.1.15",
- "oauth-sign": "0.8.2",
- "performance-now": "0.2.0",
- "qs": "6.4.0",
- "safe-buffer": "5.0.1",
- "stringstream": "0.0.5",
- "tough-cookie": "2.3.2",
- "tunnel-agent": "0.6.0",
- "uuid": "3.0.1"
- }
- },
- "rimraf": {
- "version": "2.6.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "glob": "7.1.2"
- }
- },
- "safe-buffer": {
- "version": "5.0.1",
- "bundled": true,
- "dev": true
- },
- "semver": {
- "version": "5.3.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "sntp": {
- "version": "1.0.9",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "hoek": "2.16.3"
- }
- },
- "sshpk": {
- "version": "1.13.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "asn1": "0.2.3",
- "assert-plus": "1.0.0",
- "bcrypt-pbkdf": "1.0.1",
- "dashdash": "1.14.1",
- "ecc-jsbn": "0.1.1",
- "getpass": "0.1.7",
- "jodid25519": "1.0.2",
- "jsbn": "0.1.1",
- "tweetnacl": "0.14.5"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "requires": {
- "code-point-at": "1.1.0",
- "is-fullwidth-code-point": "1.0.0",
- "strip-ansi": "3.0.1"
- }
- },
- "string_decoder": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "safe-buffer": "5.0.1"
- }
- },
- "stringstream": {
- "version": "0.0.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "ansi-regex": "2.1.1"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "tar": {
- "version": "2.2.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "block-stream": "0.0.9",
- "fstream": "1.0.11",
- "inherits": "2.0.3"
- }
- },
- "tar-pack": {
- "version": "3.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "debug": "2.6.8",
- "fstream": "1.0.11",
- "fstream-ignore": "1.0.5",
- "once": "1.4.0",
- "readable-stream": "2.2.9",
- "rimraf": "2.6.1",
- "tar": "2.2.1",
- "uid-number": "0.0.6"
- }
- },
- "tough-cookie": {
- "version": "2.3.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "punycode": "1.4.1"
- }
- },
- "tunnel-agent": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "5.0.1"
- }
- },
- "tweetnacl": {
- "version": "0.14.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "uid-number": {
- "version": "0.0.6",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- },
- "uuid": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "verror": {
- "version": "1.3.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "extsprintf": "1.0.2"
- }
- },
- "wide-align": {
- "version": "1.1.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "1.0.2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- }
- }
- },
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
@@ -4485,13 +3585,6 @@
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
"dev": true
},
- "nan": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz",
- "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=",
- "dev": true,
- "optional": true
- },
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
diff --git a/juick-www/src/main/java/com/juick/www/controllers/PageTemplates.java b/juick-www/src/main/java/com/juick/www/controllers/PageTemplates.java
index f62b7678..a01cbc55 100644
--- a/juick-www/src/main/java/com/juick/www/controllers/PageTemplates.java
+++ b/juick-www/src/main/java/com/juick/www/controllers/PageTemplates.java
@@ -286,13 +286,6 @@ public class PageTemplates {
}
}
- public String formatJSLocalTime(Date ts) {
- return "<script type=\"text/javascript\">"
- + "var d=new Date(" + ts.getTime() + ");"
- + "document.write((d.getDate()<10?'0':'')+d.getDate()+'.'+(d.getMonth()<9?'0':'')+(d.getMonth()+1)+'.'+d.getFullYear()+' '+(d.getHours()<10?'0':'')+d.getHours()+':'+(d.getMinutes()<10?'0':'')+d.getMinutes());"
- + "</script>";
- }
-
public String formatReplies(int replies) {
int ld = replies % 10;
int lh = replies % 100;
diff --git a/juick-www/src/main/java/com/juick/www/controllers/UserThread.java b/juick-www/src/main/java/com/juick/www/controllers/UserThread.java
index 543d52ad..9ab45333 100644
--- a/juick-www/src/main/java/com/juick/www/controllers/UserThread.java
+++ b/juick-www/src/main/java/com/juick/www/controllers/UserThread.java
@@ -17,7 +17,7 @@
package com.juick.www.controllers;
import com.juick.Message;
-import com.juick.server.helpers.TagStats;
+import com.juick.server.util.HttpForbiddenException;
import com.juick.server.util.HttpNotFoundException;
import com.juick.service.MessagesService;
import com.juick.service.TagService;
@@ -28,11 +28,10 @@ import com.juick.www.WebApp;
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 javax.inject.Inject;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -57,14 +56,14 @@ public class UserThread {
PageTemplates templates;
@GetMapping("/{uname}/{mid}")
- protected void doGetThread(HttpServletRequest request, HttpServletResponse response,
- @PathVariable String uname,
- @PathVariable int mid) throws IOException {
+ protected String threadAction(ModelMap model,
+ @PathVariable String uname,
+ @PathVariable int mid,
+ @RequestParam(required = false, value = "view") String paramView) throws IOException {
com.juick.User visitor = UserUtils.getCurrentUser();
if (!messagesService.canViewThread(mid, visitor.getUid())) {
- response.sendError(HttpServletResponse.SC_FORBIDDEN);
- return;
+ throw new HttpForbiddenException();
}
com.juick.Message msg = messagesService.getMessage(mid);
@@ -75,12 +74,16 @@ public class UserThread {
com.juick.User user = userService.getUserByName(uname);
if (user.getUid() == 0 || !msg.getUser().equals(user)) {
- response.sendRedirect(String.format("/%s/%d", msg.getUser().getName(), mid));
- return;
+ return String.format("redirect:/%s/%d", msg.getUser().getName(), mid);
}
+ msg.VisitorCanComment = visitor.getUid() > 0;
+ if (visitor.getUid() > 0) {
+ msg.VisitorCanComment = visitor.getUid() == msg.getUser().getUid()
+ || !userService.isInBL(msg.getUser().getUid(), visitor.getUid());
+ }
+ model.addAttribute("msg", msg);
boolean listview = false;
- String paramView = request.getParameter("view");
if (paramView != null) {
if (paramView.equals("list")) {
listview = true;
@@ -93,136 +96,31 @@ public class UserThread {
} else if (visitor.getUid() > 0 && userService.getUserOptionInt(visitor.getUid(), "repliesview", 0) == 1) {
listview = true;
}
-
+ model.addAttribute("listview", listview);
String title = msg.getUser().getName() + ": " + msg.getTagsString();
- response.setContentType("text/html; charset=UTF-8");
- try (PrintWriter out = response.getWriter()) {
- String headers = "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"@" + msg.getUser().getName() + "\" href=\"//rss.juick.com/" + msg.getUser().getName() + "/blog\"/>";
- if (paramView != null) {
- headers += "<link rel=\"canonical\" href=\"http://juick.com/" + msg.getUser().getName() + "/" + msg.getMid() + "\"/>";
- }
- if (msg.Hidden) {
- headers += "<meta name=\"robots\" content=\"noindex\"/>";
- }
- templates.pageHead(out, visitor, title, headers);
- templates.pageNavigation(out, visitor, null);
-
- out.println("<section id=\"content\" data-mid=\"" + msg.getMid() + "\" style=\"margin-left: 0; width: 100%\">");
- printMessage(out, msg, visitor);
- printReplies(out, msg, visitor, listview);
- out.println("</section>");
-
- templates.pageFooter(request, out, visitor, visitor.getUid() == 0);
-
- templates.pageEnd(out);
- }
- }
-
- // when message id is not fit to int
- @ExceptionHandler(NumberFormatException.class)
- public ResponseEntity<String> notfound() {
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
-
- public com.juick.Message printMessage(PrintWriter out, com.juick.Message msg, com.juick.User visitor) {
- msg.VisitorCanComment = visitor.getUid() > 0;
-
- List<TagStats> tags = tagService.getMessageTags(msg.getMid());
- String tagsStr = templates.formatTags(tags);
- if (msg.ReadOnly) {
- tagsStr += "<a>readonly</a>";
- msg.VisitorCanComment = false;
- }
- if (msg.getPrivacy() < 0) {
- tagsStr += "<a>friends</a>";
- }
-
- String txt;
- if (msg.getTags().stream().anyMatch(t -> t.getName().equals("code"))) {
- txt = MessageUtils.formatMessageCode(msg.getText());
- } else {
- txt = MessageUtils.formatMessage(msg.getText());
- }
-
- if (!tags.isEmpty()) {
- tagsStr = "<div class=\"msg-tags\">" + tagsStr + "</div>";
- }
-
- out.println("<ul>");
- out.println(" <li id=\"msg-" + msg.getMid() + "\" data-mid=\"" + msg.getMid() + "\" class=\"msg msgthread\">");
- out.println(" <div class=\"msg-cont\">");
- out.println(" <div class=\"msg-menu\"><a href=\"#\"></a></div>");
- out.println(" <div class=\"msg-ts\">" + templates.formatJSLocalTime(msg.getDate()) + "</div>");
- out.println(" <div class=\"msg-avatar\"><a href=\"/" + msg.getUser().getName() + "/\"><img src=\"//i.juick.com/a/" + msg.getUser().getUid() + ".png\" alt=\"" + msg.getUser().getName() + "\"/></a></div>");
- out.println(" <div class=\"msg-header\">@<a href=\"/" + msg.getUser().getName() + "/\">" + msg.getUser().getName() + "</a>:" + tagsStr + "</div>");
- out.println(" <div class=\"msg-txt\">" + txt + "</div>");
-
- if (msg.getAttachmentType() != null) {
- out.println(" <div class=\"msg-media\"><a href=\"//i.juick.com/p/" + msg.getMid() + "." + msg.getAttachmentType() + "\"><img src=\"//i.juick.com/photos-512/" + msg.getMid() + "." + msg.getAttachmentType() + "\" alt=\"\"/></a></div>");
- }
-
- boolean visitorInBL = false;
- if (visitor.getUid() > 0) {
- if (visitor.getUid() == msg.getUser().getUid()) {
- msg.VisitorCanComment = true;
- } else {
- visitorInBL = userService.isInBL(msg.getUser().getUid(), visitor.getUid());
- if (visitorInBL) {
- msg.VisitorCanComment = false;
- }
- }
- }
-
- if (msg.VisitorCanComment) {
- out.println(" <form action=\"/comment\" method=\"POST\" enctype=\"multipart/form-data\"><input type=\"hidden\" name=\"mid\" value=\"" + msg.getMid() + "\"/>");
- out.println(" <div class=\"msg-comment\"><div class=\"ta-wrapper\"><textarea name=\"body\" rows=\"1\" class=\"reply\" placeholder=\"Написать комментарий\"></textarea></div></div>");
- out.println(" </form>");
- }
-
- List<String> recomm = messagesService.getMessageRecommendations(msg.getMid());
- if (!recomm.isEmpty()) {
- out.print(" <div class=\"msg-recomms\">Рекомендовали (" + recomm.size() + "): ");
- for (int i = 0; i < recomm.size(); i++) {
- if (i > 0) {
- out.print(", ");
- }
- out.print("<a href=\"/" + recomm.get(i) + "/\">@" + recomm.get(i) + "</a>");
- }
- out.println("</div>");
+ 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\"/>";
+ if (paramView != null) {
+ headers += "<link rel=\"canonical\" href=\"http://juick.com/" + msg.getUser().getName() + "/" + msg.getMid() + "\"/>";
}
- out.println(" </div>");
- out.println(" </li>");
-
- out.println(" <li class=\"toolbar\"><ul>");
- out.println(" <li><a href=\"/" + msg.getMid() + "\"><div style=\"background-position: -64px 0\"></div>" + msg.getMid() + "</a></li>");
- if (visitor.getUid() > 0) {
- if (visitor.getUid() != msg.getUser().getUid()) {
- if (messagesService.isSubscribed(visitor.getUid(), msg.getMid())) {
- out.println(" <li><a href=\"/post?body=U+%23" + msg.getMid() + "\"><div style=\"background-position: -48px 0\"></div>Подписан</a></li>");
- } else {
- out.println(" <li><a href=\"/post?body=S+%23" + msg.getMid() + "\"><div style=\"background-position: -16px 0\"></div>Подписаться</a></li>");
- }
- if (!visitorInBL) {
- out.println(" <li><a href=\"/post?body=%21+%23" + msg.getMid() + "\"><div style=\"background-position: -32px 0\"></div>Рекомендовать</a></li>");
- }
- } else {
- out.println(" <li><a href=\"/post?body=D+%23" + msg.getMid() + "\"><div style=\"background-position: 0\"></div>Удалить</a></li>");
- }
+ if (msg.Hidden) {
+ headers += "<meta name=\"robots\" content=\"noindex\"/>";
}
- out.println(" </ul></li>");
- out.println("</ul>");
-
- return msg;
- }
-
- public void printReplies(PrintWriter out, com.juick.Message msg, com.juick.User visitor, boolean listview) {
+ model.addAttribute("headers", headers);
+ model.addAttribute("contentStyle", "margin-left: 0; width: 100%");
+ model.addAttribute("isModerator", visitor.getUid() == 3694);
+ 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<com.juick.Message> replies = messagesService.getReplies(msg.getMid());
- List<Integer> blUIDs = new ArrayList<Integer>();
+ List<Integer> blUIDs = new ArrayList<>();
for (int i = 0; i < replies.size(); i++) {
com.juick.Message reply = replies.get(i);
- if (reply.getUser().getUid() != msg.getUser().getUid() && !blUIDs.contains(reply.getUser().getUid())) {
+ if (reply.getUser().getUid() != msg.getUser().getUid()
+ && !blUIDs.contains(reply.getUser().getUid())) {
blUIDs.add(reply.getUser().getUid());
}
if (reply.getReplyto() > 0) {
@@ -238,6 +136,15 @@ public class UserThread {
reply.setReplyto(0);
}
}
+ if (visitor.getUid() > 0 && msg.getUser().getUid() == visitor.getUid()) {
+ reply.VisitorCanComment = true;
+ } else if (visitor.getUid() > 0 && msg.VisitorCanComment) {
+ List<Integer> blUIDs2 = userService.checkBL(visitor.getUid(), blUIDs);
+ reply.VisitorCanComment = reply.getUser().getUid() == visitor.getUid()
+ || !blUIDs2.contains(reply.getUser().getUid());
+ } else {
+ reply.VisitorCanComment = false;
+ }
}
boolean foldable = false;
@@ -249,145 +156,14 @@ public class UserThread {
}
}
}
-
- out.println("<div class=\"title2\">");
- out.print(" <div class=\"title2-right\">");
- if (listview) {
- out.print("<a href=\"?view=tree\" rel=\"nofollow\">Показать деревом</a>");
- } else {
- if (foldable) {
- out.print("<span id=\"unfoldall\"><a href=\"#\">Раскрыть все</a> &#183; </span>");
- }
- out.print("<a href=\"?view=list\" rel=\"nofollow\">Показать списком</a>");
- }
- out.print("</div>");
- out.println(" <h2>Ответы (" + replies.size() + ")</h2>");
- out.println("</div>");
-
- out.println("<ul id=\"replies\">");
-
- if (!replies.isEmpty()) {
- if (visitor.getUid() > 0 && msg.getUser().getUid() == visitor.getUid()) {
- for (Message reply : replies) {
- reply.VisitorCanComment = true;
- }
- } else if (visitor.getUid() > 0 && msg.VisitorCanComment) {
- blUIDs = userService.checkBL(visitor.getUid(), blUIDs);
- for (Message reply : replies) {
- reply.VisitorCanComment = reply.getUser().getUid() == visitor.getUid() || !blUIDs.contains(reply.getUser().getUid());
- }
- } else {
- for (Message reply : replies) {
- reply.VisitorCanComment = false;
- }
- }
-
- if (listview) {
- printList(out, replies, visitor);
- } else {
- printTree(out, replies, visitor, 0, 0, false);
- }
-
- for (Message reply : replies) {
- reply.cleanupChilds();
- }
- replies.clear();
- }
-
- out.println("</ul>");
+ model.addAttribute("replies", replies);
+ model.addAttribute("foldable", foldable);
+ return "views/thread";
}
- public void printTree(PrintWriter out, List<com.juick.Message> replies, com.juick.User visitor, int ReplyTo, int margin, boolean hidden) {
- if (margin > 240) {
- margin = 240;
- }
-
- for (int i = 0; i < replies.size(); i++) {
- com.juick.Message msg = replies.get(i);
- if (msg.getReplyto() == ReplyTo) {
-
- out.print(" <li id=\"" + msg.getRid() + "\" class=\"msg\" style=\"");
- if (margin > 0) {
- out.print("margin-left: " + margin + "px;");
- }
- if (hidden) {
- out.print("display:none;");
- }
- out.println("\">");
- out.println(" <div class=\"msg-cont\">");
- out.println(" <div class=\"msg-header\">");
- if (!msg.getUser().isBanned()) {
- out.println(" @<a href=\"/" + msg.getUser().getName() + "/\">" + msg.getUser().getName() + "</a>:");
- out.println(" <div class=\"msg-avatar\"><a href=\"/" + msg.getUser().getName() + "/\"><img src=\"//i.juick.com/a/" + msg.getUser().getUid() + ".png\" alt=\"" + msg.getUser().getName() + "\"/></a></div>");
- } else {
- out.println(" [удалено]:");
- out.println(" <div class=\"msg-avatar\"><img src=\"//i.juick.com/av-96.png\"/></div>");
- }
- out.println(" <div class=\"msg-menu\"><a href=\"#\" class=\"a-thread-links\"></a></div>");
- out.println(" <div class=\"msg-ts\"><a href=\"/" + msg.getMid() + "#" + msg.getRid() + "\" title=\"" + templates.sdfSQL.format(msg.getDate()) + " GMT\">" + templates.formatDate(msg.TimeAgo, msg.getDate()) + "</a></div>");
- out.println(" </div>");
- out.println(" <div class=\"msg-txt\">" + MessageUtils.formatMessage(msg.getText()) + "</div>");
- if (msg.getAttachmentType() != null) {
- out.println(" <div class=\"msg-media\"><a href=\"//i.juick.com/p/" + msg.getMid() + "-" + msg.getRid() + "." + msg.getAttachmentType() + "\"><img src=\"//i.juick.com/photos-512/" + msg.getMid() + "-" + msg.getRid() + "." + msg.getAttachmentType() + "\" alt=\"\"/></a></div>");
- }
- out.print(" <div class=\"msg-links\">/" + msg.getRid());
- if (msg.getReplyto() > 0) {
- out.print(" в ответ на <a href=\"#" + msg.getReplyto() + "\">/" + msg.getReplyto() + "</a>");
- }
- if (msg.VisitorCanComment) {
- out.println(" &#183; <a href=\"/post?body=%23" + msg.getMid() + "/" + msg.getRid() + "%20\" class=\"a-thread-comment\">Ответить</a></div>");
- out.println(" <div class=\"msg-comment\" style=\"display: none\"></div>");
- } else if (visitor.getUid() == 0) {
- out.println(" &#183; <a href=\"#\" class=\"a-login\">Ответить</a></div>");
- }
-
- int childs = msg.getChildsCount();
- if (ReplyTo == 0 && childs > 1 && replies.size() > 10) {
- out.println(" <div class=\"msg-comments\"><a href=\"#\">" + templates.formatReplies(childs) + "</a></div>");
- }
- out.println(" </div>");
- out.println(" </li>");
-
- if (ReplyTo == 0 && childs > 1 && replies.size() > 10) {
- printTree(out, msg.childs, visitor, msg.getRid(), margin + 20, true);
- } else if (childs > 0) {
- printTree(out, msg.childs, visitor, msg.getRid(), margin + 20, hidden);
- }
- }
- }
- }
-
- public void printList(PrintWriter out, List<com.juick.Message> replies, com.juick.User visitor) {
- for (Message msg : replies) {
- out.print(" <li id=\"" + msg.getRid() + "\" class=\"msg\">");
- out.println(" <div class=\"msg-cont\">");
- out.println(" <div class=\"msg-header\">");
- if (!msg.getUser().isBanned()) {
- out.println(" @<a href=\"/" + msg.getUser().getName() + "/\">" + msg.getUser().getName() + "</a>:");
- out.println(" <div class=\"msg-avatar\"><a href=\"/" + msg.getUser().getName() + "/\"><img src=\"//i.juick.com/a/" + msg.getUser().getUid() + ".png\" alt=\"" + msg.getUser().getName() + "\"/></a></div>");
- } else {
- out.println(" [удалено]:");
- out.println(" <div class=\"msg-avatar\"><img src=\"//i.juick.com/av-96.png\"/></div>");
- }
- out.println(" <div class=\"msg-menu\"><a href=\"#\" class=\"a-thread-links\"></a></div>");
- out.println(" <div class=\"msg-ts\"><a href=\"/" + msg.getMid() + "#" + msg.getRid() + "\" title=\"" + PageTemplates.sdfSQL.format(msg.getDate()) + " GMT\">" + templates.formatDate(msg.TimeAgo, msg.getDate()) + "</a></div>");
- out.println(" </div>");
- out.println(" <div class=\"msg-txt\">" + MessageUtils.formatMessage(msg.getText()) + "</div>");
- if (msg.getAttachmentType() != null) {
- out.println(" <div class=\"msg-media\"><a href=\"//i.juick.com/p/" + msg.getMid() + "-" + msg.getRid() + "." + msg.getAttachmentType() + "\"><img src=\"//i.juick.com/photos-512/" + msg.getMid() + "-" + msg.getRid() + "." + msg.getAttachmentType() + "\" alt=\"\"/></a></div>");
- }
- out.print(" <div class=\"msg-links\">/" + msg.getRid());
- if (msg.getReplyto() > 0) {
- out.print(" в ответ на <a href=\"#" + msg.getReplyto() + "\">/" + msg.getReplyto() + "</a>");
- }
- if (msg.VisitorCanComment) {
- out.println(" &#183; <a href=\"#\" class=\"a-thread-comment\">Ответить</a></div>");
- out.println(" <div class=\"msg-comment\" style=\"display: none\"></div>");
- } else if (visitor.getUid() == 0) {
- out.println(" <div class=\"msg-links\"><a href=\"/post?body=%23" + msg.getMid() + "/" + msg.getRid() + "%20\" class=\"a-thread-comment\">Ответить</a></div>");
- }
- out.println(" </div>");
- out.println(" </li>");
- }
+ // 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/mitchellbosecke/pebble/extension/FormatterExtension.java b/juick-www/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java
index 01b63b0a..7702a316 100644
--- a/juick-www/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java
+++ b/juick-www/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java
@@ -18,6 +18,7 @@
package com.mitchellbosecke.pebble.extension;
import com.mitchellbosecke.pebble.extension.filters.FormatMessageFilter;
+import com.mitchellbosecke.pebble.extension.filters.FormatRepliesFilter;
import com.mitchellbosecke.pebble.extension.filters.PrettyTimeFilter;
import com.mitchellbosecke.pebble.extension.filters.TagsListFilter;
@@ -32,6 +33,7 @@ public class FormatterExtension extends AbstractExtension {
public Map<String, Filter> getFilters() {
Map<String, Filter> filters = new HashMap<>();
filters.put("formatMessage", new FormatMessageFilter());
+ filters.put("formatReplies", new FormatRepliesFilter());
filters.put("prettyTime", new PrettyTimeFilter());
filters.put("tagsList", new TagsListFilter());
return filters;
diff --git a/juick-www/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatRepliesFilter.java b/juick-www/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatRepliesFilter.java
new file mode 100644
index 00000000..f375ecdb
--- /dev/null
+++ b/juick-www/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatRepliesFilter.java
@@ -0,0 +1,48 @@
+/*
+ * 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.mitchellbosecke.pebble.extension.filters;
+
+import com.juick.Message;
+import com.mitchellbosecke.pebble.extension.Filter;
+
+import java.util.List;
+import java.util.Map;
+
+public class FormatRepliesFilter implements Filter {
+ @Override
+ public Object apply(Object input, Map<String, Object> args) {
+ if (input instanceof Message) {
+ int replies = ((Message)input).getChildsCount();
+ int ld = replies % 10;
+ int lh = replies % 100;
+ if ((lh < 10 || lh > 20) && ld == 1) {
+ return replies + " ответ";
+ } else if ((lh < 10 || lh > 20) && ld > 1 && ld < 5) {
+ return replies + " ответа";
+ } else {
+ return replies + " ответов";
+ }
+ }
+ throw new IllegalArgumentException("invalid input");
+ }
+
+ @Override
+ public List<String> getArgumentNames() {
+ return null;
+ }
+}
diff --git a/juick-www/src/main/webapp/WEB-INF/layouts/content.html b/juick-www/src/main/webapp/WEB-INF/layouts/content.html
index 420d4e0c..c059678e 100644
--- a/juick-www/src/main/webapp/WEB-INF/layouts/content.html
+++ b/juick-www/src/main/webapp/WEB-INF/layouts/content.html
@@ -31,7 +31,8 @@
</head>
<body id="body">
{% include "views/partial/navigation" %}
-<section id="content">
+<section id="content" style="{{ contentStyle | default('') }}"
+ {% if msg | default('') is not empty %}data-mid="{{ msg.mid }}"{% endif %}>
{% block content %}
{% endblock %}
</section>
diff --git a/juick-www/src/main/webapp/WEB-INF/views/macros/tree.html b/juick-www/src/main/webapp/WEB-INF/views/macros/tree.html
new file mode 100644
index 00000000..3e35f92a
--- /dev/null
+++ b/juick-www/src/main/webapp/WEB-INF/views/macros/tree.html
@@ -0,0 +1,54 @@
+{% macro tree(replies, visitor, level, margin, hidden) %}
+{% for msg in replies %}
+ {% if msg.replyto == level %}
+ <li id="{{ msg.rid }}" style="margin-left: {{ margin }}px;{% if hidden %}display: none;{% endif %}" 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-menu">
+ <a href="#" class="a-thread-links"></a>
+ </div>
+ <div class="msg-ts">
+ <a href="/{{ msg.mid }}#{{ msg.rid }}">
+ <time datetime="{{ msg.date | date('yyyy-MM-dd HH:mm:ss') }}Z"
+ title="{{ msg.date | date('yyyy-MM-dd HH:mm:ss') }} GMT">
+ {{ msg.date | prettyTime }}
+ </time>
+ </a>
+ </div>
+ </div>
+ <div class="msg-txt">{{ msg | formatMessage }}</div>
+ <div class="msg-media"></div>
+ <div class="msg-links">/{{ msg.rid }}
+{% if msg.replyto > 0 %}
+ в ответ на <a href="#{{ msg.replyto }}">/{{ msg.replyto }}</a>
+{% endif %}
+{% if msg.VisitorCanComment %}
+ &#183; <a href="/post?body=%23{{ msg.mid }}/{{ msg.rid }}%20" class="a-thread-comment">Ответить</a></div>
+ <div class="msg-comment" style="display: none"></div>
+{% elseif visitor.uid == 0 %}
+ &#183; <a href="#" class="a-login">Ответить</a></div>
+{% endif %}
+
+{% if level == 0 and msg.childsCount > 1 and replies.size() > 10 %}
+ <div class="msg-comments"><a href="#">{{ msg | formatReplies }}</a></div>
+{% endif %}
+ </li>
+ {% if (level == 0 and msg.childsCount > 1 and replies.size() > 10) %}
+ {{ tree(msg.childs, visitor, msg.rid, margin + 20, true) }}
+ {% elseif (msg.childsCount > 0) %}
+ {{ tree(msg.childs, visitor, msg.rid, margin + 20, hidden) }}
+ {% endif %}
+ {% endif %}
+{% endfor %}
+{% endmacro %} \ No newline at end of file
diff --git a/juick-www/src/main/webapp/WEB-INF/views/partial/message.html b/juick-www/src/main/webapp/WEB-INF/views/partial/message.html
index cdfad6b6..f67362ce 100644
--- a/juick-www/src/main/webapp/WEB-INF/views/partial/message.html
+++ b/juick-www/src/main/webapp/WEB-INF/views/partial/message.html
@@ -1,7 +1,9 @@
<article data-mid="{{ msg.mid }}">
- <header class="h">@<a href="/{{ msg.user.name }}/">{{ msg.user.name }}</a>:
+ <header class="h">
+ @<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>
+ <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/></a>
+ </div>
<div class="msg-menu"><a href="#"></a></div>
<div class="msg-ts">
<a href="/{{ msg.user.name }}/{{ msg.mid }}">
diff --git a/juick-www/src/main/webapp/WEB-INF/views/partial/thread_list.html b/juick-www/src/main/webapp/WEB-INF/views/partial/thread_list.html
new file mode 100644
index 00000000..b59d8b22
--- /dev/null
+++ b/juick-www/src/main/webapp/WEB-INF/views/partial/thread_list.html
@@ -0,0 +1,44 @@
+{% 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-menu">
+ <a href="#" class="a-thread-links"></a>
+ </div>
+ <div class="msg-ts">
+ <a href="/{{ msg.mid }}#{{ msg.rid }}">
+ <time datetime="{{ msg.date | date('yyyy-MM-dd HH:mm:ss') }}Z"
+ title="{{ msg.date | date('yyyy-MM-dd HH:mm:ss') }} GMT">
+ {{ msg.date | prettyTime }}
+ </time>
+ </a>
+ </div>
+ </div>
+ <div class="msg-txt">{{ msg | formatMessage }}</div>
+ <div class="msg-media"></div>
+ <div class="msg-links">/{{ msg.rid }}
+ {% if msg.replyto > 0 %}
+ в ответ на <a href="#{{ msg.replyto }}">/{{ msg.replyto }}</a>
+ {% endif %}
+ {% if msg.VisitorCanComment %}
+ &#183; <a href="/post?body=%23{{ msg.mid }}/{{ msg.rid }}%20" class="a-thread-comment">Ответить</a>
+ </div>
+ <div class="msg-comment" style="display: none;"></div>
+ {% else %}
+ </div>
+ {% endif %}
+ </div>
+ </div>
+</li>
+{% endfor %} \ No newline at end of file
diff --git a/juick-www/src/main/webapp/WEB-INF/views/partial/thread_tree.html b/juick-www/src/main/webapp/WEB-INF/views/partial/thread_tree.html
new file mode 100644
index 00000000..f207b8e0
--- /dev/null
+++ b/juick-www/src/main/webapp/WEB-INF/views/partial/thread_tree.html
@@ -0,0 +1,2 @@
+{% import "views/macros/tree" %}
+{{ tree(replies, visitor, 0, 0, false) }} \ No newline at end of file
diff --git a/juick-www/src/main/webapp/WEB-INF/views/thread.html b/juick-www/src/main/webapp/WEB-INF/views/thread.html
new file mode 100644
index 00000000..301ad8f8
--- /dev/null
+++ b/juick-www/src/main/webapp/WEB-INF/views/thread.html
@@ -0,0 +1,108 @@
+{% extends "layouts/content" %}
+{% import "views/macros/tags" %}
+{% block content %}
+<ul>
+ <li id="msg-{{ msg.mid }}" class="msg msgthread">
+ <div class="msg-cont">
+ <div class="msg-menu"><a href="#"></a></div>
+ <div class="msg-ts"><a href="/{{ msg.user.name }}/{{ msg.mid }}">
+ <time datetime="{{ msg.date | date('yyyy-MM-dd HH:mm:ss') }}Z"
+ title="{{ msg.date | date('yyyy-MM-dd HH:mm:ss') }} GMT">
+ {{ msg.date | prettyTime }}
+ </time>
+ </a>
+ </div>
+ <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-header">@<a href="/{{ msg.user.name }}/">{{ msg.user.name }}</a>:
+ <div class="msg-tags">
+ {{ tags(msg.user.name, msg.tags | tagsList) }}
+ </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.AttachmentType }}">
+ <img src="//i.juick.com/photos-512/{{ msg.mid }}.{{ msg.AttachmentType }}" alt=""/>
+ </a>
+ </div>
+ {% endif %}
+ {% if msg.VisitorCanComment %}
+ <form action="/comment" method="POST" enctype="multipart/form-data">
+ <input type="hidden" name="mid" value="{{ msg.mid }}"/>
+ <div class="msg-comment">
+ <div class="ta-wrapper">
+ <textarea name="body" rows="1" class="reply" placeholder="Написать комментарий">
+
+ </textarea>
+ </div>
+ </div>
+ </form>
+ {% endif %}
+ {% if recomm is not empty %}
+ <div class="msg-recomms">Рекомендовали ({{ recomm.size() }}):
+ {% for rec in recomm %}
+ <a href="/{{ rec }}/">@{{ rec }}</a>{% if loop.index < (loop.length - 1) %}, {% endif %}
+ {% endfor %}
+ </div>
+ {% endif %}
+ </div>
+ </li>
+
+ <li class="toolbar">
+ <ul>
+ <li><a href="/{{ msg.mid }}">
+ <div style="background-position: -64px 0"></div>
+ {{ msg.mid }}</a>
+ </li>
+ {% if visitor.uid > 0 %}
+ {% if visitor.uid != msg.user.uid %}
+ {% if visitorSubscribed %}
+ <li><a href="/post?body=U+%23{{ msg.mid }}">
+ <div style="background-position: -48px 0"></div>
+ Подписан</a></li>
+ {% else %}
+ <li><a href="/post?body=S+%23{{ msg.mid }}">
+ <div style="background-position: -16px 0"></div>
+ Подписаться</a></li>
+ {% endif %}
+ {% if not visitorInBL %}
+ <li><a href="/post?body=%21+%23{{ msg.mid }}">
+ <div style="background-position: -32px 0"></div>
+ Рекомендовать</a></li>
+ {% endif %}
+ {% else %}
+ <li><a href="/post?body=D+%23{{ msg.mid }}">
+ <div style="background-position: 0"></div>
+ Удалить</a></li>
+ {% endif %}
+ {% endif %}
+ </ul>
+ </li>
+</ul>
+<div class="title2">
+ <div class="title2-right">
+ {% if listview %}
+ <a href="?view=tree" rel="nofollow">Показать деревом</a>
+ {% else %}
+ {% if foldable %}
+ <span id="unfoldall"><a href="#">Раскрыть все</a> &#183; </span>
+ {% endif %}
+ <a href="?view=list" rel="nofollow">Показать списком</a>
+ {% endif %}
+ </div>
+ <h2>Ответы ({{ replies.size() }})</h2>
+</div>
+
+<ul id="replies">
+ {% if (listview) %}
+ {% include "views/partial/thread_list" %}
+ {% else %}
+ {% include "views/partial/thread_tree" %}
+ {% endif %}
+</ul>
+{% endblock %}
+{% block "column" %}
+{% endblock %} \ No newline at end of file