Compare commits

...

53 Commits
2.2.0 ... 2.3.0

Author SHA1 Message Date
Athou
535f947f88 2.3.0 release 2016-03-02 11:21:10 +01:00
Athou
f27e243cc4 readme update 2016-03-02 11:20:57 +01:00
Athou
6a699ed5f1 Merge pull request #782 from badarg/improve-spanish-translation
Improved spanish translation.
2016-02-15 12:34:02 +01:00
badarg
9c1f5efab5 Improved spanish translation.
Fixed grammar, orthography, sundry mistakes, and added missing strings.
2016-02-14 20:36:28 +01:00
Athou
6b7ce56f6b Merge pull request #779 from canoine/patch-1
Update fr.js
2016-01-20 06:31:20 +01:00
canoine
b76ee4a2d0 Update fr.js
Translation of the sentences that were still in english.
Some stylistic, grammatical and orthographic corrections.
2016-01-20 01:55:09 +01:00
Athou
b444a74a44 change user agent so that self hosted instances don't point to commafeed.com 2016-01-18 10:01:05 +01:00
Athou
d43820cc82 change npm install log level to info 2016-01-08 16:12:15 +01:00
Athou
e74e8fe1c2 Merge pull request #769 from matrixik/patch-1
[add] css ids for toolbar
2015-11-30 10:09:33 +01:00
Dobrosław Żybort
9eb6e8ec27 [add] css ids for toolbar 2015-11-30 09:48:20 +01:00
Athou
fae94d3696 jdbc drivers upgrade 2015-11-25 16:50:42 +01:00
Athou
68e5ed64c9 dropwizard upgrade 2015-11-25 16:49:18 +01:00
Athou
f912d3b8bd swagger upgrade 2015-11-25 16:44:28 +01:00
Athou
fc03d2ee91 Merge pull request #768 from giucal/master
Little contribution to the Italian translation
2015-11-24 06:54:19 +01:00
Giuseppe Calabrese
523b2b8db4 Fixed a couple typos.
I felt free to change the `filtering_expression_help` and make it less ambiguous.
2015-11-23 22:30:11 +01:00
Giuseppe Calabrese
d547e9b6d7 Fixed some translation. 2015-11-23 21:22:49 +01:00
Athou
71efc9f854 fix #766 2015-11-07 23:45:04 +01:00
Athou
4f289f7467 Merge pull request #764 from JmsBnz/patch-2
Update it.js
2015-11-03 11:31:13 +01:00
JmsBnz
02ef8bee71 Update it.js
Some translation fixes
2015-10-31 11:38:21 +01:00
Athou
ff5c1b00d7 Merge pull request #762 from ebraminio/patch-4
Add magnet links support
2015-10-21 15:44:46 +02:00
ebraminio
30264be311 Add magnet links support 2015-10-19 23:45:53 +03:30
Athou
8ea44ab8c7 Merge pull request #761 from ebraminio/patch-3
Use correct characters for ru lang
2015-10-10 06:28:30 +02:00
ebraminio
1b8ff7ca61 Use correct characters for ru lang
It is similar but works better on some font setups
2015-10-10 00:45:50 +03:30
Athou
f00a066c22 languages should start with an uppercase (fix #759) 2015-09-01 16:58:18 +02:00
Athou
859cf468aa add openjdk ppa on ubuntu lts (fix #756) 2015-08-27 12:45:37 +02:00
Athou
5b486a917b dropwizard upgrade 2015-08-26 17:50:20 +02:00
Athou
9ace6b70f0 frontend-maven-plugin upgrade (#747) 2015-08-22 03:01:18 +02:00
Athou
447029ae70 skip jsoup parsing for null strings (#754) 2015-08-19 11:10:02 +02:00
Athou
8ac52690fd fix wrong parameter name (#752) 2015-08-18 13:14:35 +02:00
Athou
6934b2bd27 remove println 2015-08-17 16:30:27 +02:00
Athou
6647e4fcd4 additional timer metrics 2015-08-14 12:58:55 +02:00
Athou
21710f55f3 proxy image enclosures too (#750) 2015-08-07 10:07:42 +02:00
Athou
27bd9a7489 fix test (#750) 2015-08-06 10:42:04 +02:00
Athou
630d37125c hide enclosure if already in entry content (fix #748) 2015-08-05 09:45:07 +02:00
Athou
9424237534 cleanup 2015-07-27 14:38:52 +02:00
Athou
cba3fbeb5f generate swagger file before running gulp (#746) 2015-07-25 08:48:56 +02:00
Athou
58778ccf43 dropwizard upgrade 2015-07-10 09:05:01 +02:00
Athou
6c61d47d78 swagger.json no longer generated at runtime 2015-07-09 16:08:31 +02:00
Athou
35e02f9d98 querydsl upgrade 2015-07-09 12:34:54 +02:00
Athou
58c1650863 make mvnw executable 2015-07-05 14:10:04 +02:00
Athou
9b14ffa14c update readme to use maven wrapper 2015-07-04 17:10:16 +02:00
Athou
96c09bf4cd ending quote missing 2015-07-04 17:05:03 +02:00
Athou
737cec744a fix mvnw on windows 2015-07-04 17:02:23 +02:00
Athou
13ed92bb94 add maven-wrapper 2015-07-04 09:06:47 +02:00
Athou
076594c78e force filter expression to lowercase 2015-06-29 12:56:17 +02:00
Athou
b6b1b4ebbe fix build, 2.0.1 has been deleted (https://github.com/dlmanning/gulp-sass/issues/305) 2015-06-26 15:05:10 +02:00
Athou
4007f37492 maven 3.3 setup instructions 2015-06-26 14:13:14 +02:00
Athou
532d671feb upgrade frontend-maven-plugin (fix #743) 2015-06-26 14:01:42 +02:00
Athou
fed7a1ac84 rewrite query using subqueries 2015-06-25 11:20:50 +02:00
Athou
ddfd170ea8 make eclipse mars happy 2015-06-24 11:42:01 +02:00
Athou
bae5c67dfa dropwizard upgrade 2015-06-19 08:41:23 +02:00
Athou
84f51603fb bump version 2015-06-19 08:33:43 +02:00
Athou
f73ddc03e9 readme update 2015-06-19 08:33:12 +02:00
78 changed files with 1134 additions and 663 deletions

View File

@@ -1,3 +1,8 @@
v 2.3.0
- dropwizard upgrade 0.9.1
- feed enclosures are hidden if they already displayed in the content
- fix youtube favicons
- various internationalization fixes
v 2.2.0 v 2.2.0
- fix youtube and instagram favicon fetching - fix youtube and instagram favicon fetching
- mark as read filter was lost when a feed was rearranged with drag&drop - mark as read filter was lost when a feed was rearranged with drag&drop

View File

@@ -13,32 +13,43 @@ Browser extensions: [Chrome](https://github.com/Athou/commafeed-chrome) - [Firef
## Deployment on your own server ## Deployment on your own server
### The short version ### The very short version (download precompiled package)
mkdir commafeed && cd commafeed
wget https://github.com/Athou/commafeed/releases/download/2.2.0/commafeed.jar
wget https://raw.githubusercontent.com/Athou/commafeed/2.2.0/config.yml.example -O config.yml
vi config.yml
java -Djava.net.preferIPv4Stack=true -jar commafeed.jar server config.yml
### The short version (build from sources)
git clone https://github.com/Athou/commafeed.git git clone https://github.com/Athou/commafeed.git
cd commafeed cd commafeed
mvn clean package ./mvnw clean package
cp config.yml.example config.yml cp config.yml.example config.yml
vi config.yml vi config.yml
java -Djava.net.preferIPv4Stack=true -jar target/commafeed.jar server config.yml java -Djava.net.preferIPv4Stack=true -jar target/commafeed.jar server config.yml
### The long version ### The long version (same as the short version, but more detailed)
CommaFeed 2.0 has been rewritten to use Dropwizard and gulp instead of using tomee and wro4j. The latest version of the 1.x branch is available [here](https://github.com/Athou/commafeed/tree/1.x). CommaFeed 2.0 has been rewritten to use Dropwizard and gulp instead of using tomee and wro4j. The latest version of the 1.x branch is available [here](https://github.com/Athou/commafeed/tree/1.x).
For storage, you can either use an embedded H2 database (use it only to test CommaFeed) or an external MySQL, PostgreSQL or SQLServer database. For storage, you can either use an embedded H2 database (use it only to test CommaFeed) or an external MySQL, PostgreSQL or SQLServer database.
You also need Maven 3.x (and a Java 1.8+ JDK) installed in order to build the application. You also need the Java 1.8+ JDK in order to build the application.
To install maven and openjdk on Ubuntu, issue the following commands To install the required packages to build CommaFeed on Ubuntu, issue the following commands
# if openjdk-8-jdk is not available on your ubuntu version (14.04 LTS), add the following repo first
sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install g++ build-essential openjdk-8-jdk
sudo apt-get install g++ build-essential openjdk-8-jdk maven
# Make sure java8 is the selected java version # Make sure java8 is the selected java version
sudo update-alternatives --config java sudo update-alternatives --config java
sudo update-alternatives --config javac sudo update-alternatives --config javac
On Windows and other operating systems, just download maven 3.x from the [official site](http://maven.apache.org/), extract it somewhere and add the `bin` directory to your `PATH` environment variable.
Clone this repository. If you don't have git you can download the sources as a zip file from [here](https://github.com/Athou/commafeed/archive/master.zip) Clone this repository. If you don't have git you can download the sources as a zip file from [here](https://github.com/Athou/commafeed/archive/master.zip)
git clone https://github.com/Athou/commafeed.git git clone https://github.com/Athou/commafeed.git
@@ -46,7 +57,7 @@ Clone this repository. If you don't have git you can download the sources as a z
Now build the application Now build the application
mvn clean package ./mvnw clean package
Copy `config.yml.example` to `config.yml` then edit the file to your liking. Copy `config.yml.example` to `config.yml` then edit the file to your liking.
Issue the following command to run the app, the server will listen by default on `http://localhost:8082`. The default user is `admin` and the default password is `admin`. Issue the following command to run the app, the server will listen by default on `http://localhost:8082`. The default user is `admin` and the default password is `admin`.
@@ -112,7 +123,7 @@ Steps to configuring a development environment for CommaFeed may include, but ma
## Copyright and license ## Copyright and license
Copyright 2013-2014 CommaFeed. Copyright 2013-2015 CommaFeed.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this work except in compliance with the License. you may not use this work except in compliance with the License.

View File

@@ -28,7 +28,7 @@
"devicejs": "0.2.4", "devicejs": "0.2.4",
"readabilicons": "arc90/readability-readabilicons#34c55561c5b8ec6e90714b50237c06b13cb9d59c", "readabilicons": "arc90/readability-readabilicons#34c55561c5b8ec6e90714b50237c06b13cb9d59c",
"zocial-less": "1.0.0", "zocial-less": "1.0.0",
"swagger-ui": "2.1.8-M1" "swagger-ui": "2.1.0"
}, },
"resolutions": { "resolutions": {
"angular": "1.3.14", "angular": "1.3.14",

View File

@@ -49,8 +49,9 @@ gulp.task('select2', function() {
gulp.task('swagger-ui', function() { gulp.task('swagger-ui', function() {
var index_html = SRC_DIR + 'api/index.html'; var index_html = SRC_DIR + 'api/index.html';
var swagger_json = 'target/swagger/swagger.json';
var lib = SRC_DIR + 'lib/swagger-ui/dist/**/*'; var lib = SRC_DIR + 'lib/swagger-ui/dist/**/*';
return gulp.src([lib, index_html]).pipe(gulp.dest(BUILD_DIR + 'api')); return gulp.src([lib, index_html, swagger_json]).pipe(gulp.dest(BUILD_DIR + 'api'));
}); });
gulp.task('template-cache', function() { gulp.task('template-cache', function() {

BIN
maven/maven-wrapper.jar Normal file

Binary file not shown.

View File

@@ -0,0 +1,3 @@
#Maven download properties
#Sat Jul 04 09:06:32 CEST 2015
distributionUrl=https\://repository.apache.org/content/repositories/releases/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip

234
mvnw vendored Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
# Apple JDKs
#
export JAVA_HOME=`/usr/libexec/java_home`
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
local basedir=$(pwd)
local wdir=$(pwd)
while [ "$wdir" != '/' ] ; do
wdir=$(cd "$wdir/.."; pwd)
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER="org.apache.maven.wrapper.MavenWrapperMain"
exec "$JAVACMD" \
$MAVEN_OPTS \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
-classpath \
"$MAVEN_PROJECTBASEDIR/maven/maven-wrapper.jar" \
${WRAPPER_LAUNCHER} "$@"

141
mvnw.bat Normal file
View File

@@ -0,0 +1,141 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:init
set MAVEN_CMD_LINE_ARGS=%*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\maven\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% -Dmaven.multiModuleProjectDirectory="" %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

View File

@@ -13,7 +13,7 @@
"gulp-filter": "2.0.2", "gulp-filter": "2.0.2",
"gulp-connect": "2.2.0", "gulp-connect": "2.2.0",
"connect-modrewrite": "0.8.1", "connect-modrewrite": "0.8.1",
"gulp-sass": "2.0.1", "gulp-sass": "2.0.2",
"gulp-useref": "1.1.2", "gulp-useref": "1.1.2",
"gulp-angular-templatecache": "1.6.0" "gulp-angular-templatecache": "1.6.0"
} }

91
pom.xml
View File

@@ -4,20 +4,20 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>2.2.0</version> <version>2.3.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>CommaFeed</name> <name>CommaFeed</name>
<prerequisites> <prerequisites>
<maven>3.0.5</maven> <maven>3.1.0</maven>
</prerequisites> </prerequisites>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<dropwizard.version>0.8.1</dropwizard.version> <dropwizard.version>0.9.1</dropwizard.version>
<guice.version>4.0</guice.version> <guice.version>4.0</guice.version>
<querydsl.version>3.6.4</querydsl.version> <querydsl.version>4.0.2</querydsl.version>
<rome.version>1.5.0</rome.version> <rome.version>1.5.0</rome.version>
</properties> </properties>
@@ -129,20 +129,49 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<apiSources>
<apiSource>
<locations>com.commafeed.frontend.resource;com.commafeed.frontend.model;com.commafeed.frontend.model.request</locations>
<swaggerDirectory>target/swagger</swaggerDirectory>
<basePath>/rest</basePath>
<info>
<title>CommaFeed</title>
<version>${project.version}</version>
</info>
<typesToSkip>
<typeToSkip>com.commafeed.backend.model.User</typeToSkip>
</typesToSkip>
</apiSource>
</apiSources>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>com.github.eirslett</groupId> <groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId> <artifactId>frontend-maven-plugin</artifactId>
<version>0.0.22</version> <version>0.0.25</version>
<executions> <executions>
<execution> <execution>
<id>install node and npm</id> <id>install node and npm</id>
<goals> <goals>
<goal>install-node-and-npm</goal> <goal>install-node-and-npm</goal>
</goals> </goals>
<phase>generate-resources</phase> <phase>compile</phase>
<configuration> <configuration>
<nodeVersion>v0.12.4</nodeVersion> <nodeVersion>v0.10.39</nodeVersion>
<npmVersion>2.10.1</npmVersion> <npmVersion>2.12.1</npmVersion>
</configuration> </configuration>
</execution> </execution>
<execution> <execution>
@@ -150,13 +179,17 @@
<goals> <goals>
<goal>npm</goal> <goal>npm</goal>
</goals> </goals>
<phase>generate-resources</phase> <phase>compile</phase>
<configuration>
<arguments>install --loglevel info</arguments>
</configuration>
</execution> </execution>
<execution> <execution>
<id>bower install</id> <id>bower install</id>
<goals> <goals>
<goal>bower</goal> <goal>bower</goal>
</goals> </goals>
<phase>compile</phase>
<configuration> <configuration>
<arguments>install</arguments> <arguments>install</arguments>
</configuration> </configuration>
@@ -166,7 +199,7 @@
<goals> <goals>
<goal>gulp</goal> <goal>gulp</goal>
</goals> </goals>
<phase>generate-resources</phase> <phase>compile</phase>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
@@ -213,6 +246,16 @@
<groupId>io.dropwizard</groupId> <groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId> <artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version> <version>${dropwizard.version}</version>
<exclusions>
<exclusion>
<groupId>org.glassfish.hk2.external</groupId>
<artifactId>aopalliance-repackaged</artifactId>
</exclusion>
<exclusion>
<groupId>org.glassfish.hk2.external</groupId>
<artifactId>javax.inject</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.dropwizard</groupId> <groupId>io.dropwizard</groupId>
@@ -233,6 +276,12 @@
<groupId>io.dropwizard</groupId> <groupId>io.dropwizard</groupId>
<artifactId>dropwizard-forms</artifactId> <artifactId>dropwizard-forms</artifactId>
<version>${dropwizard.version}</version> <version>${dropwizard.version}</version>
<exclusions>
<exclusion>
<groupId>org.glassfish.hk2.external</groupId>
<artifactId>javax.inject</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
@@ -248,26 +297,20 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.wordnik</groupId> <groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId> <artifactId>swagger-annotations</artifactId>
<version>1.5.3-M1</version> <version>1.5.0</version>
<exclusions>
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.mysema.querydsl</groupId> <groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId> <artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version> <version>${querydsl.version}</version>
<scope>provided</scope> <scope>provided</scope>
<classifier>hibernate</classifier> <classifier>hibernate</classifier>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.mysema.querydsl</groupId> <groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId> <artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version> <version>${querydsl.version}</version>
</dependency> </dependency>
@@ -374,17 +417,17 @@
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
<version>1.4.187</version> <version>1.4.190</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version> <version>5.1.37</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version> <version>9.4-1205-jdbc42</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.sourceforge.jtds</groupId> <groupId>net.sourceforge.jtds</groupId>

View File

@@ -25,7 +25,7 @@
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
window.swaggerUi = new SwaggerUi({ window.swaggerUi = new SwaggerUi({
url: "../rest/swagger.json", url: "./swagger.json",
dom_id: "swagger-ui-container", dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete'], supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
onComplete: function(swaggerApi, swaggerUi){ onComplete: function(swaggerApi, swaggerUi){

View File

@@ -7,24 +7,26 @@
"download" : "Descargar", "download" : "Descargar",
"link" : "Enlace", "link" : "Enlace",
"bookmark" : "Marcador", "bookmark" : "Marcador",
"close" : "Close ", "close" : "Cerrar",
"tags" : "Tags " "tags" : "Etiquetas"
}, },
"tree" : { "tree" : {
"subscribe" : "Subscribir", "subscribe" : "Suscribirse",
"import" : "Importar", "import" : "Importar",
"new_category" : "Nueva categoría", "new_category" : "Nueva categoría",
"all" : "Todos", "all" : "Todos",
"starred" : "Destacado" "starred" : "Destacados"
}, },
"subscribe" : { "subscribe" : {
"feed_url" : "URL del Canal", "feed_url" : "URL del canal",
"feed_name" : "Nombre del Canal", "filtering_expression" : "Expresión de filtrado",
"filtering_expression_help" : "Si no está vacía, una expresión se evalúa como 'cierta' o 'falsa'. Si es falsa, las nueva entradas de este canal se marcarán como leídas automáticamente.\nLas variables disponibles son 'title' (título), 'content'(contenido), 'url' (URL), 'author' (autor), y 'categories' (categorías) y sus contenidos son convertidos a minúsculas para facilitar la comparación de strings (cadenas de texto).\nEjemplo: url.contains('youtube') or (author eq 'athou' and title.contains('github').\nLa sintaxis completa está disponible <a href='http://commons.apache.org/proper/commons-jexl/reference/syntax.html' target='_blank'>aquí</a>.",
"feed_name" : "Nombre del canal",
"category" : "Categoría" "category" : "Categoría"
}, },
"import" : { "import" : {
"google_reader_prefix" : "Déjame importar tus canales de tu", "google_reader_prefix" : "Déjame importar tus canales de tu cuenta ",
"google_reader_suffix" : " cuenta.", "google_reader_suffix" : ".",
"google_download" : "También puedes subir tu archivo subscriptions.xml.", "google_download" : "También puedes subir tu archivo subscriptions.xml.",
"google_download_link" : "Descárgalo de aquí.", "google_download_link" : "Descárgalo de aquí.",
"xml_file" : "Archivo OPML" "xml_file" : "Archivo OPML"
@@ -34,147 +36,147 @@
"parent" : "Padre" "parent" : "Padre"
}, },
"toolbar" : { "toolbar" : {
"unread" : "Sin Leer", "unread" : "No leídos",
"all" : "Todos", "all" : "Todos",
"previous_entry" : "Entrada Anterior", "previous_entry" : "Entrada anterior",
"next_entry" : "Próxima Entrada", "next_entry" : "Entrada siguiente",
"refresh" : "Atualizar", "refresh" : "Atualizar",
"refresh_all" : "Force refresh all my feeds ", "refresh_all" : "Forzar la actualización de todos mis canales.",
"sort_by_asc_desc" : "Ordenar por fecha asc/desc", "sort_by_asc_desc" : "Ordenar por fecha asc/desc.",
"titles_only" : "Sólo Títulos", "titles_only" : "Sólo títulos",
"expanded_view" : "Vista Expandida", "expanded_view" : "Vista expandida",
"mark_all_as_read" : "Marcar todos como leído", "mark_all_as_read" : "Marcar todos como leído",
"mark_all_older_12_hours" : "Items older than 12 hours ", "mark_all_older_12_hours" : "Entradas anteriores a 12 horas.",
"mark_all_older_day" : "Artículos anteriores a un día", "mark_all_older_day" : "Entradas anteriores a un día.",
"mark_all_older_week" : "Artículos más de una semana", "mark_all_older_week" : "Entradas anteriores a una semana.",
"mark_all_older_two_weeks" : "Artículos más de does semanas", "mark_all_older_two_weeks" : "Entradas anteriores a 2 semanas.",
"settings" : "Ajustes", "settings" : "Ajustes",
"profile" : "Perfil", "profile" : "Perfil",
"admin" : "Admin", "admin" : "Admin",
"about" : "Acerca de", "about" : "Acerca de...",
"logout" : "Cerrar sesión", "logout" : "Cerrar sesión",
"donate" : "Donar" "donate" : "Donar"
}, },
"view" : { "view" : {
"entry_source" : "from ", "entry_source" : "de ",
"entry_author" : "by ", "entry_author" : "por ",
"error_while_loading_feed" : "Error mientras se cargaba este canal", "error_while_loading_feed" : "Error mientras se cargaba este canal.",
"keep_unread" : "Guardar no leídos", "keep_unread" : "Mantener como no leído.",
"no_unread_items" : "no tiene items sin leer.", "no_unread_items" : "no tiene entradas sin leer.",
"mark_up_to_here" : "Mark as read up to here ", "mark_up_to_here" : "Marcar como leídos hasta aquí.",
"search_for" : "searching for: ", "search_for" : "buscando: ",
"no_search_results" : "No match found for the requested keywords " "no_search_results" : "No se han encontrado resultados para las palabras clave especificadas."
}, },
"feedsearch" : { "feedsearch" : {
"hint" : "Type in a subscription... ", "hint" : "Introduce una suscripción...",
"help" : "Use the return key to select and arrow keys to navigate. ", "help" : "Usa la tecla Intro para seleccionar y las teclas de flecha para navegar.",
"result_prefix" : "Your subscriptions: " "result_prefix" : "Tus suscripciones:"
}, },
"settings" : { "settings" : {
"general" : { "general" : {
"value" : "General", "value" : "General",
"language" : "Lenguaje", "language" : "Idioma",
"language_contribute" : "Contribuye con traducciones", "language_contribute" : "Contribuye con traducciones.",
"show_unread" : "Mostrar canales y categorías sin entradas no leídas.", "show_unread" : "Mostrar canales y categorías sin entradas no leídas.",
"social_buttons" : "Mostrar botones de compartir de redes sociales.", "social_buttons" : "Mostrar botones para compartir de redes sociales.",
"scroll_marks" : "En vista expandida, el desplazamiento por las entradas las marca como leídas" "scroll_marks" : "En vista expandida, el desplazamiento por las entradas las marca como leídas."
}, },
"appearance" : "Appearance ", "appearance" : "Apariencia",
"scroll_speed" : "Scrolling speed when navigating between entries (in milliseconds) ", "scroll_speed" : "Velocidad de desplazamiento al navegar entre entradas (en milisegundos)",
"scroll_speed_help" : "set to 0 to disable ", "scroll_speed_help" : "ponlo a 0 para desactivarlo",
"theme" : "Theme ", "theme" : "Tema",
"submit_your_theme" : "Submit your theme ", "submit_your_theme" : "Envía tu tema ",
"custom_css" : "CSS Personalizado" "custom_css" : "CSS personalizado"
}, },
"details" : { "details" : {
"feed_details" : "Detalles de Canales", "feed_details" : "Detalles del canal",
"url" : "URL", "url" : "URL",
"website" : "Website ", "website" : "Sitio web",
"name" : "Nombre", "name" : "Nombre",
"category" : "Categoría", "category" : "Categoría",
"position" : "Position ", "position" : "Posicióon",
"last_refresh" : "Última actualización", "last_refresh" : "Última actualización",
"message" : "Last refresh message ", "message" : "Último mensaje de actualización",
"next_refresh" : "Next refresh ", "next_refresh" : "Próxima actualización",
"queued_for_refresh" : "Queued for refresh ", "queued_for_refresh" : "En cola para actualizar",
"feed_url" : "URL del Canal", "feed_url" : "URL del canal",
"generate_api_key_first" : "Genera una llave API en tu perfil primero.", "generate_api_key_first" : "Genera una clave API en tu perfil primero.",
"unsubscribe" : "Terminar subscripción", "unsubscribe" : "Terminar suscripción",
"unsubscribe_confirmation" : "Are you sure you want to unsubscribe from this feed? ", "unsubscribe_confirmation" : "¿Estás seguro de querer terminar tu suscripción a este canal?",
"delete_category_confirmation" : "Are you sure you want to delete this category? ", "delete_category_confirmation" : "¿Estás seguro de querer eliminar esta categoría?",
"category_details" : "Detalles de la categoría", "category_details" : "Detalles de la categoría",
"tag_details" : "Tag details ", "tag_details" : "Detalles de las etiuqetas ",
"parent_category" : "Categoría principal" "parent_category" : "Categoría principal"
}, },
"profile" : { "profile" : {
"user_name" : "Nombre de usuario", "user_name" : "Nombre de usuario",
"email" : "Correo", "email" : "Correo electrónico",
"change_password" : "Cambiar contraseña", "change_password" : "Cambiar contraseña",
"confirm_password" : "Confirmar contraseña", "confirm_password" : "Confirmar contraseña",
"minimum_6_chars" : "Mínimo 6 caracteres", "minimum_6_chars" : "Mínimo 6 caracteres",
"passwords_do_not_match" : "Las contraseñas no coinciden", "passwords_do_not_match" : "Las contraseñas no coinciden",
"api_key" : "Llave API", "api_key" : "Clave API",
"api_key_not_generated" : "No generado todavía", "api_key_not_generated" : "No generado todavía",
"generate_new_api_key" : "Generar nueva llave API", "generate_new_api_key" : "Generar nueva clave API",
"generate_new_api_key_info" : "Al cambiar la contraseña se generará una nueva llave API", "generate_new_api_key_info" : "Al cambiar la contraseña se generará una nueva clave API.",
"opml_export" : "Exportación de OPML", "opml_export" : "Exportación de OPML",
"delete_account" : "Eliminar cuenta", "delete_account" : "Eliminar cuenta",
"delete_account_confirmation" : "Delete your acount? There's no turning back! " "delete_account_confirmation" : "¿Eliminar tu cuenta? ¡No habrá vuelta atrás! "
}, },
"about" : { "about" : {
"rest_api" : { "rest_api" : {
"value" : "REST API", "value" : "REST API",
"line1" : "CommaFeed está construido con el uso de JAX-RS y AngularJS. Por eso, un REST API esta disponible.", "line1" : "CommaFeed está construido sobre JAX-RS y AngularJS. Por lo tanto, una REST API está disponible.",
"link_to_documentation" : "Vínculo de la documentación." "link_to_documentation" : "Enlace a la documentación."
}, },
"keyboard_shortcuts" : "Atajos de teclado", "keyboard_shortcuts" : "Atajos de teclado",
"version" : "CommaFeed version ", "version" : "Versión de CommaFeed",
"line1_prefix" : "CommaFeed es un proyecto open-source. El código se encuentra en ", "line1_prefix" : "CommaFeed es un proyecto de código abierto. El código se encuentra en ",
"line1_suffix" : ".", "line1_suffix" : ".",
"line2_prefix" : "Si encuentras un problema, por favor reportalo en la página de problemas de ", "line2_prefix" : "Si encuentras un problema, por favor repórtalo en la página de problemas de ",
"line2_suffix" : " del proyecto.", "line2_suffix" : " del proyecto.",
"line3" : "Si te gusta este proyecto, por favor considera realizar una donacion para apoyar al desarrollador y ayudar a cubrir los costos de mantenimiento.", "line3" : "Si te gusta este proyecto, por favor considera realizar una donación para apoyar al desarrollador y ayudar a cubrir los costes de mantenimiento.",
"line4" : "For those of you who prefer bitcoin, here is the address ", "line4" : "Para aquellos de vosotros que prefieran bitcoin, aquí está la dirección ",
"goodies" : { "goodies" : {
"value" : "Goodies", "value" : "Extras",
"android_app" : "Android app ", "android_app" : "Apps para Android",
"subscribe_url" : "Subscribe URL ", "subscribe_url" : "URL para suscribirse ",
"chrome_extension" : "Extensión de Chrome", "chrome_extension" : "Extensión para Chrome.",
"firefox_extension" : "Extensión de Firefox", "firefox_extension" : "Extensión para Firefox.",
"opera_extension" : "Opera extension ", "opera_extension" : "Extensón para Opera.",
"subscribe_bookmarklet" : "Add subscription bookmarklet (click) ", "subscribe_bookmarklet" : "Añadir marcador de suscripción (clic).",
"subscribe_bookmarklet_asc" : "Oldest first ", "subscribe_bookmarklet_asc" : "Más antiguos primero",
"subscribe_bookmarklet_desc" : "Newest first ", "subscribe_bookmarklet_desc" : "Más recientes primero",
"next_unread_bookmarklet" : "Next unread item bookmarklet (drag to bookmark bar) " "next_unread_bookmarklet" : "Marcador a la siguiente entrada no leída (arástralo a la barra de marcadores) "
}, },
"translation" : { "translation" : {
"value" : "Traducción", "value" : "Traducción",
"message" : "Necesitamos tu ayuda para ayudar a traducir CommaFeed.", "message" : "Necesitamos tu ayuda para ayudar a traducir CommaFeed.",
"link" : "Ver como contribuir con traducciones." "link" : "Ver cómo contribuir con traducciones."
}, },
"announcements" : "Anuncios", "announcements" : "Anuncios",
"shortcuts" : { "shortcuts" : {
"mouse_middleclick" : "ratón botón medio", "mouse_middleclick" : "click medio",
"open_next_entry" : "abrir próxima entrada", "open_next_entry" : "abrir la siguiente entrada",
"open_previous_entry" : "abrir entrada previa", "open_previous_entry" : "abrir la entrada anterior",
"spacebar" : "space/shift+space ", "spacebar" : "espacio/mayúsculas+espacio",
"move_page_down_up" : "moves the page down/up ", "move_page_down_up" : "mueve la página arriba/abajo",
"focus_next_entry" : "Establecer el foco en la próxima entrada sin abrirlo", "focus_next_entry" : "establecer el foco en la siguiente entrada sin abrirla",
"focus_previous_entry" : "Establecer el foco en la entrada anterior sin abrirlo", "focus_previous_entry" : "establecer el foco en la entrada anterior sin abrirla",
"open_next_feed" : "open next feed or category ", "open_next_feed" : "abrir el siguiente canal o categoría",
"open_previous_feed" : "open previous feed or category ", "open_previous_feed" : "abrir el canal o categoría previo",
"open_close_current_entry" : "abrir/cerrar entrada actual", "open_close_current_entry" : "abrir/cerrar la entrada actual",
"open_current_entry_in_new_window" : "abrir entrada actual en una nueva ventana", "open_current_entry_in_new_window" : "abrir la entrada actual en una nueva ventana",
"open_current_entry_in_new_window_background" : "open current entry in a new window in the background ", "open_current_entry_in_new_window_background" : "abrir la entrada actual en una nueva ventana en segundo plano",
"star_unstar" : "marcar/no marcar la entrada actual", "star_unstar" : "destacar la entrada actual",
"mark_current_entry" : "marcar como leída/no la leída entrada actual", "mark_current_entry" : "marcar la entrada actual como leída/no la leída",
"mark_all_as_read" : "marcar todas las entradas como leídas", "mark_all_as_read" : "marcar todas las entradas como leídas",
"open_in_new_tab_mark_as_read" : "abrir entrada en una nueva pestaña y marcar como leída", "open_in_new_tab_mark_as_read" : "abrir entrada en una nueva pestaña y marcar como leída",
"fullscreen" : "toggle full screen mode ", "fullscreen" : "activar/desactivar el modo pantalla completa ",
"font_size" : "increase/decrease font size of the current entry ", "font_size" : "aumentar/reducir el tamaño de la fuente de la entrada actual",
"go_to_all" : "go to the All view ", "go_to_all" : "ver Todos",
"go_to_starred" : "go to the Starred view ", "go_to_starred" : "ver Destacados",
"feed_search" : "navigate to a subscription by entering the subscription name " "feed_search" : "navega a una suscripción al introducir su nombre"
} }
} }
} }

View File

@@ -34,24 +34,24 @@
"parent" : "Parent" "parent" : "Parent"
}, },
"toolbar" : { "toolbar" : {
"unread" : "Non-lus", "unread" : "Non lus",
"all" : "Tous", "all" : "Tous",
"previous_entry" : "Article précédent", "previous_entry" : "Article précédent",
"next_entry" : "Article suivant", "next_entry" : "Article suivant",
"refresh" : "Rafraîchir", "refresh" : "Rafraîchir",
"refresh_all" : "Force refresh all my feeds ", "refresh_all" : "Rafraîchir tous les flux",
"sort_by_asc_desc" : "Trier par date croissante/décroissante", "sort_by_asc_desc" : "Trier par date croissante/décroissante",
"titles_only" : "Titres uniquement", "titles_only" : "Titres uniquement",
"expanded_view" : "Vue étendue", "expanded_view" : "Vue étendue",
"mark_all_as_read" : "Tout marquer comme lu", "mark_all_as_read" : "Tout marquer comme lu",
"mark_all_older_12_hours" : "Items older than 12 hours ", "mark_all_older_12_hours" : "Articles de plus de 12 heures",
"mark_all_older_day" : "Articles de plus d'un jour", "mark_all_older_day" : "Articles de plus d'une journée",
"mark_all_older_week" : "Articles de plus d'une semaine", "mark_all_older_week" : "Articles de plus d'une semaine",
"mark_all_older_two_weeks" : "Articles de plus d'un mois", "mark_all_older_two_weeks" : "Articles de plus d'un mois",
"settings" : "Préférences", "settings" : "Préférences",
"profile" : "Profil", "profile" : "Profil",
"admin" : "Administration", "admin" : "Administration",
"about" : "A propos", "about" : "À propos",
"logout" : "Déconnexion", "logout" : "Déconnexion",
"donate" : "Faire un don" "donate" : "Faire un don"
}, },
@@ -59,16 +59,16 @@
"entry_source" : "sur", "entry_source" : "sur",
"entry_author" : "par ", "entry_author" : "par ",
"error_while_loading_feed" : "Erreur durant le chargement de ce flux", "error_while_loading_feed" : "Erreur durant le chargement de ce flux",
"keep_unread" : "Garder non-lu", "keep_unread" : "Garder non lu",
"no_unread_items" : "n'a pas d'articles non-lus.", "no_unread_items" : "n'a pas d'articles non lus.",
"mark_up_to_here" : "Marquer comme lu jusqu'ici", "mark_up_to_here" : "Marquer comme lu jusqu'ici",
"search_for" : "searching for: ", "search_for" : "recherche : ",
"no_search_results" : "No match found for the requested keywords " "no_search_results" : "Pas de résultats avec le terme indiqué."
}, },
"feedsearch" : { "feedsearch" : {
"hint" : "Tapez un nom de flux", "hint" : "Tapez un nom de flux",
"help" : "Utilisez la touche entrée pour sélectionner et les flèches pour naviguer", "help" : "Utilisez la touche entrée pour sélectionner et les flèches pour naviguer",
"result_prefix" : "Vos flux:" "result_prefix" : "Vos flux :"
}, },
"settings" : { "settings" : {
"general" : { "general" : {
@@ -76,12 +76,12 @@
"language" : "Langue", "language" : "Langue",
"language_contribute" : "Contribuer aux traductions", "language_contribute" : "Contribuer aux traductions",
"show_unread" : "Afficher les flux et les catégories pour lesquels tout est déjà lu", "show_unread" : "Afficher les flux et les catégories pour lesquels tout est déjà lu",
"social_buttons" : "Afficher les boutons de partage sur réseaux sociaux", "social_buttons" : "Afficher les boutons de partage sur les réseaux sociaux",
"scroll_marks" : "En mode de lecture étendu, marquer comme lu les éléments lorsque la fenêtre descend." "scroll_marks" : "En mode de lecture étendu, marquer les éléments comme lus lorsque la fenêtre descend."
}, },
"appearance" : "Apparence", "appearance" : "Apparence",
"scroll_speed" : "Scrolling speed when navigating between entries (in milliseconds) ", "scroll_speed" : "Vitesse de défilement entre les entrées (en millisecondes) ",
"scroll_speed_help" : "set to 0 to disable ", "scroll_speed_help" : "Mettez 0 pour désactiver",
"theme" : "Thème", "theme" : "Thème",
"submit_your_theme" : "Soumettez votre thème.", "submit_your_theme" : "Soumettez votre thème.",
"custom_css" : "CSS personnelle" "custom_css" : "CSS personnelle"
@@ -94,16 +94,16 @@
"category" : "Catégorie", "category" : "Catégorie",
"position" : "Position", "position" : "Position",
"last_refresh" : "Dernière mise à jour", "last_refresh" : "Dernière mise à jour",
"message" : "Last refresh message ", "message" : "Message de la dernière mise à jour ",
"next_refresh" : "Prochaine mise à jour", "next_refresh" : "Prochaine mise à jour",
"queued_for_refresh" : "En file d'attente", "queued_for_refresh" : "En file d'attente",
"feed_url" : "URL du flux", "feed_url" : "URL du flux",
"generate_api_key_first" : "Générez une clé API dans votre profil d'abord.", "generate_api_key_first" : "Générez d'abord une clé API dans votre profil.",
"unsubscribe" : "Se désabonner", "unsubscribe" : "Se désabonner",
"unsubscribe_confirmation" : "Are you sure you want to unsubscribe from this feed? ", "unsubscribe_confirmation" : "Êtes-vous sûr de vouloir vous désabonner de de flux ? ",
"delete_category_confirmation" : "Are you sure you want to delete this category? ", "delete_category_confirmation" : "Êtes-vous sûr de vouloir supprimer cette catégorie ? ",
"category_details" : "Détails de la catégorie", "category_details" : "Détails de la catégorie",
"tag_details" : "Tag details ", "tag_details" : "Détails du tag",
"parent_category" : "Catégorie parente" "parent_category" : "Catégorie parente"
}, },
"profile" : { "profile" : {
@@ -116,15 +116,15 @@
"api_key" : "Clé API", "api_key" : "Clé API",
"api_key_not_generated" : "Pas encore générée", "api_key_not_generated" : "Pas encore générée",
"generate_new_api_key" : "Générer une nouvelle clé API", "generate_new_api_key" : "Générer une nouvelle clé API",
"generate_new_api_key_info" : "Changer de mot de passe va générer une nouvelle clé API", "generate_new_api_key_info" : "Changer de mot de passe générera une nouvelle clé API",
"opml_export" : "Export du fichier OPML", "opml_export" : "Export du fichier OPML",
"delete_account" : "Effacer le compte", "delete_account" : "Effacer le compte",
"delete_account_confirmation" : "Delete your acount? There's no turning back! " "delete_account_confirmation" : "Êtes-vous sûr de vouloir supprimer définitivement votre compte ?"
}, },
"about" : { "about" : {
"rest_api" : { "rest_api" : {
"value" : "API REST", "value" : "API REST",
"line1" : "CommaFeed utilise JAX-RS et AngularJS, donc une API REST est disponible.", "line1" : "CommaFeed utilise JAX-RS et AngularJS, une API REST est donc disponible.",
"link_to_documentation" : "Lien vers la documentation." "link_to_documentation" : "Lien vers la documentation."
}, },
"keyboard_shortcuts" : "Raccourcis clavier", "keyboard_shortcuts" : "Raccourcis clavier",
@@ -145,7 +145,7 @@
"subscribe_bookmarklet" : "Bookmarklet d'ajout d'abonnement", "subscribe_bookmarklet" : "Bookmarklet d'ajout d'abonnement",
"subscribe_bookmarklet_asc" : "Du plus ancien au plus récent", "subscribe_bookmarklet_asc" : "Du plus ancien au plus récent",
"subscribe_bookmarklet_desc" : "Du plus récent au plus ancien", "subscribe_bookmarklet_desc" : "Du plus récent au plus ancien",
"next_unread_bookmarklet" : "Bookmarklet vers le prochain article non-lu" "next_unread_bookmarklet" : "Bookmarklet vers le prochain article non lu"
}, },
"translation" : { "translation" : {
"value" : "Traduction", "value" : "Traduction",
@@ -165,9 +165,9 @@
"open_previous_feed" : "Sélectionner le flux ou la catégorie précédente", "open_previous_feed" : "Sélectionner le flux ou la catégorie précédente",
"open_close_current_entry" : "Ouvrir/fermer l'article courant", "open_close_current_entry" : "Ouvrir/fermer l'article courant",
"open_current_entry_in_new_window" : "Ouvrir l'article courant dans une nouvelle fenêtre", "open_current_entry_in_new_window" : "Ouvrir l'article courant dans une nouvelle fenêtre",
"open_current_entry_in_new_window_background" : "Ouvrir l'article courant dans une nouvelle fenêtre en arrière plan", "open_current_entry_in_new_window_background" : "Ouvrir l'article courant dans une nouvelle fenêtre en arrière-plan",
"star_unstar" : "Ajouter/enlever l'article courant des favoris", "star_unstar" : "Ajouter/enlever l'article courant des favoris",
"mark_current_entry" : "Marquer comme lue/non-lue l'article courant", "mark_current_entry" : "Marquer comme lu/non lu l'article courant",
"mark_all_as_read" : "Marquer tous les articles comme lus", "mark_all_as_read" : "Marquer tous les articles comme lus",
"open_in_new_tab_mark_as_read" : "Ouvrir l'article courant dans une nouvelle fenêtre et marquer comme lu", "open_in_new_tab_mark_as_read" : "Ouvrir l'article courant dans une nouvelle fenêtre et marquer comme lu",
"fullscreen" : "Activer/désactiver le mode plein-écran", "fullscreen" : "Activer/désactiver le mode plein-écran",
@@ -177,4 +177,4 @@
"feed_search" : "Naviguer vers un flux en entrant son nom" "feed_search" : "Naviguer vers un flux en entrant son nom"
} }
} }
} }

View File

@@ -1,33 +1,33 @@
{ {
"global" : { "global" : {
"save" : "Salva", "save" : "Salva",
"cancel" : "Cancella", "cancel" : "Annulla",
"delete" : "Elimina", "delete" : "Elimina",
"required" : "Richiesto", "required" : "Richiesto",
"download" : "Download", "download" : "Download",
"link" : "Link", "link" : "Link",
"bookmark" : "Segnalibro", "bookmark" : "Segnalibro",
"close" : "Chiudi", "close" : "Chiudi",
"tags" : "Etichette " "tags" : "Tag"
}, },
"tree" : { "tree" : {
"subscribe" : "Abbonati", "subscribe" : "Iscriviti",
"import" : "Importa", "import" : "Importa",
"new_category" : "Nuova categoria", "new_category" : "Nuova categoria",
"all" : "Tutto", "all" : "Tutti",
"starred" : "Preferiti" "starred" : "Preferiti"
}, },
"subscribe" : { "subscribe" : {
"feed_url" : "Feed URL", "feed_url" : "URL feed",
"feed_name" : "Nome feed", "feed_name" : "Nome feed",
"category" : "Categoria" "category" : "Categoria"
}, },
"import" : { "import" : {
"google_reader_prefix" : "Permettimi di importare i tuoi feed dal tuo ", "google_reader_prefix" : "Permettimi di importare i feed dal tuo account ",
"google_reader_suffix" : " account.", "google_reader_suffix" : ".",
"google_download" : "Oppure, carica il tuo file subscriptions.xml.", "google_download" : "Oppure carica il tuo file subscriptions.xml.",
"google_download_link" : "Scaricalo da qui.", "google_download_link" : "Puoi scaricalo da qui.",
"xml_file" : "OPML File" "xml_file" : "File OPML"
}, },
"new_category" : { "new_category" : {
"name" : "Nome", "name" : "Nome",
@@ -38,12 +38,12 @@
"all" : "Tutti", "all" : "Tutti",
"previous_entry" : "Precedente", "previous_entry" : "Precedente",
"next_entry" : "Successivo", "next_entry" : "Successivo",
"refresh" : "Ricarica", "refresh" : "Aggiorna",
"refresh_all" : "Forza l'aggiornamento di tutte i miei feed", "refresh_all" : "Forza l'aggiornamento di tutti i feed",
"sort_by_asc_desc" : "Ordina per data ascendente/decrescente", "sort_by_asc_desc" : "Ordina per data crescente/decrescente",
"titles_only" : "Solo i titoli", "titles_only" : "Solo i titoli",
"expanded_view" : "Espandi", "expanded_view" : "Espandi",
"mark_all_as_read" : "Segna tutto come già letto", "mark_all_as_read" : "Segna tutti come già letti",
"mark_all_older_12_hours" : "Elementi più vecchi di 12 ore", "mark_all_older_12_hours" : "Elementi più vecchi di 12 ore",
"mark_all_older_day" : "Elementi più vecchi di un giorno", "mark_all_older_day" : "Elementi più vecchi di un giorno",
"mark_all_older_week" : "Elementi più vecchi di una settimana", "mark_all_older_week" : "Elementi più vecchi di una settimana",
@@ -56,56 +56,56 @@
"donate" : "Dona" "donate" : "Dona"
}, },
"view" : { "view" : {
"entry_source" : "da ", "entry_source" : "da",
"entry_author" : "di ", "entry_author" : "di",
"error_while_loading_feed" : "Si è verificato un errore durante il caricamento di questo feed", "error_while_loading_feed" : "Si è verificato un errore durante il caricamento del feed",
"keep_unread" : "Mantiene come non leggere", "keep_unread" : "Mantieni come da leggere",
"no_unread_items" : "Non ci sono elementi da leggere.", "no_unread_items" : "non contiene elementi da leggere",
"mark_up_to_here" : "Segna come letto fino qui", "mark_up_to_here" : "Segna come letto fin qui",
"search_for" : "cercando: ", "search_for" : "Cerca: ",
"no_search_results" : "Nessun risultato trovato per le parole chiave cercate" "no_search_results" : "Nessun risultato per le parole chiave cercate"
}, },
"feedsearch" : { "feedsearch" : {
"hint" : "Digita in una sottoscrizione... ", "hint" : "Digita il nome di una sottoscrizione... ",
"help" : "Usa il tasto invio per selezionare e le frecce per navigare.", "help" : "Usa il tasto Invio per selezionare e le frecce per navigare.",
"result_prefix" : "Le tue sottoscrizioni:" "result_prefix" : "Le tue sottoscrizioni:"
}, },
"settings" : { "settings" : {
"general" : { "general" : {
"value" : "Generali", "value" : "Generali",
"language" : "Lingua", "language" : "Lingua",
"language_contribute" : "Contribuisci nelle traduzioni", "language_contribute" : "Contribuisci alle traduzioni",
"show_unread" : "Mostra i feed e le categorie con elementi non letti", "show_unread" : "Mostra i feed e le categorie con voci non lette",
"social_buttons" : "Mostra i pulsanti social network di condivisione", "social_buttons" : "Mostra i pulsanti di condivisione social",
"scroll_marks" : "In modalità estesa, segna come letto le voci quando scorri" "scroll_marks" : "In vista estesa, segna come lette le voci che scorri"
}, },
"appearance" : "Aspetto", "appearance" : "Aspetto",
"scroll_speed" : "Velocità dello scorrimento durante la navigazione tra i feed (in millisecondi) ", "scroll_speed" : "Velocità di scorrimento quando navighi tra i feed (in millisecondi)",
"scroll_speed_help" : "Imposta 0 per disabilitare", "scroll_speed_help" : "Imposta su 0 per disabilitare",
"theme" : "Tema", "theme" : "Tema",
"submit_your_theme" : "Proponi il tuo tema", "submit_your_theme" : "Sottoponi il tuo tema",
"custom_css" : "CSS personalizzato" "custom_css" : "CSS personalizzato"
}, },
"details" : { "details" : {
"feed_details" : "Dettagli feed", "feed_details" : "Dettagli feed",
"url" : "URL ", "url" : "URL",
"website" : "Sito Web", "website" : "Sito web",
"name" : "Nome", "name" : "Nome",
"category" : "Categoria", "category" : "Categoria",
"position" : "Posizione", "position" : "Posizione",
"last_refresh" : "Ultimo aggiornamento", "last_refresh" : "Ultimo aggiornamento",
"message" : "Ultimo messaggio di aggiornamento", "message" : "Ultimo messaggio di aggiornamento",
"next_refresh" : "Prossimo aggiornamento", "next_refresh" : "Prossimo aggiornamento",
"queued_for_refresh" : "In attesa per l'aggiornamento", "queued_for_refresh" : "In coda per l'aggiornamento",
"feed_url" : "URL del feed ", "feed_url" : "URL feed",
"filtering_expression" : "Espressione del filtro", "filtering_expression" : "Espressione filtro",
"filtering_expression_help" : "Se non è vuoto, una espressione viene misurata in 'true' o 'false'. Se falsa, i nuovi elementi di questo feed verranno segnati automaticamente come letti.\nLe variabili accettate sono 'title', 'content', 'url' 'author' e 'categories' e il loro contenuto è convertito in minuscolo per una facile confronto di stringhe.\Esempio: url.contains('youtube') o (author eq 'athou' and title.contains('github').\nLa sintassi completa è disponibile <a href='http://commons.apache.org/proper/commons-jexl/reference/syntax.html' target='_blank'>qui</a>.", "filtering_expression_help" : "Quando non è vuota, l'espressione viene applicata a ogni nuovo elemento e valutata come 'vera' o 'falsa'. Se falsa, l'elemento verrà segnato automaticamente come letto.\nLe variabili accettate sono 'title' (titolo), 'content' (contenuto), 'url', 'author' (autore) e 'categories' (categorie); il loro contenuto è convertito in minuscolo per facilitarne il confronto.\nEsempio: url.contains('youtube') or (author eq 'athou' and title.contains('github')).\nLa sintassi completa è disponibile <a href='http://commons.apache.org/proper/commons-jexl/reference/syntax.html' target='_blank'>qui</a> (in inglese).",
"generate_api_key_first" : "Genera prima una chiave API nelle impostazioni del tuo profilo.", "generate_api_key_first" : "Genera prima una chiave API nelle impostazioni del tuo profilo.",
"unsubscribe" : "Annulla la sottoscrizione", "unsubscribe" : "Disiscriviti",
"unsubscribe_confirmation" : "Sei sicuro di voler annullare la sottoscrizione da questo feed?", "unsubscribe_confirmation" : "Sei sicuro di voler annullare la sottoscrizione al feed?",
"delete_category_confirmation" : "Sei sicuro di voler eliminare questa categoria?", "delete_category_confirmation" : "Sei sicuro di voler eliminare questa categoria?",
"category_details" : "Dettagli categoria", "category_details" : "Dettagli categoria",
"tag_details" : "Dettagli etichette ", "tag_details" : "Dettagli tag",
"parent_category" : "Categoria principale" "parent_category" : "Categoria principale"
}, },
"profile" : { "profile" : {
@@ -117,66 +117,66 @@
"passwords_do_not_match" : "Le password non corrispondono", "passwords_do_not_match" : "Le password non corrispondono",
"api_key" : "chiave API", "api_key" : "chiave API",
"api_key_not_generated" : "Non ancora generata", "api_key_not_generated" : "Non ancora generata",
"generate_new_api_key" : "Genera una nuova chiave API ", "generate_new_api_key" : "Genera una nuova chiave API",
"generate_new_api_key_info" : "Cambiando la password sarà generata una nuova chiave API ì", "generate_new_api_key_info" : "Cambiando la password sarà generata una nuova chiave API",
"opml_export" : "Esporta OPML", "opml_export" : "Esporta OPML",
"delete_account" : "Elimina il profilo", "delete_account" : "Elimina account",
"delete_account_confirmation" : "Eliminare il tuo profilo? Non si può tornare indietro!" "delete_account_confirmation" : "Vuoi eliminare il tuo account? Non si può tornare indietro!"
}, },
"about" : { "about" : {
"rest_api" : { "rest_api" : {
"value" : "REST API", "value" : "REST API",
"line1" : "CommaFeed è costruito sopra JAX-RS e AngularJS. Ed ovviamente, una REST API è disponibile.", "line1" : "CommaFeed è basato su JAX-RS e AngularJS. Pertanto è disponibile una REST API.",
"link_to_documentation" : "Collegamento alla documentazione." "link_to_documentation" : "Link alla documentazione."
}, },
"keyboard_shortcuts" : "Scorciatoie da tastiera", "keyboard_shortcuts" : "Scorciatoie da tastiera",
"version" : "Versione di CommaFeed", "version" : "Versione di CommaFeed",
"line1_prefix" : "CommaFeed è un progetto open source. I codici sono ospitati su ", "line1_prefix" : "CommaFeed è un progetto open source. Trovi i sorgenti su ",
"line1_suffix" : ".", "line1_suffix" : ".",
"line2_prefix" : "Se hai qualche problema, segnalalo sulla pagina del ", "line2_prefix" : "Se hai qualche problema, segnalalo sulla pagina del progetto ",
"line2_suffix" : " progetto.", "line2_suffix" : ".",
"line3" : "Se ti piace il progetto, considera una donazione per supportare lo sviluppatore ed a aiutare per coprire i costi di mantenenimento di questo sito online.", "line3" : "Se ti piace questo progetto, considera una donazione per supportare lo sviluppatore e aiutare a coprire i costi di manutenzione di questo sito.",
"line4" : "Se preferisci i Bitcoin, questo è l'indirizzo", "line4" : "Se preferisci Bitcoin, questo è l'indirizzo",
"goodies" : { "goodies" : {
"value" : "Goodies", "value" : "Cose che potrebbero interessarti",
"android_app" : "Applicazione Android", "android_app" : "Applicazione Android",
"subscribe_url" : "Sottoscrivi URL", "subscribe_url" : "Sottoscrivi URL",
"chrome_extension" : "Estensione per Chrome", "chrome_extension" : "Estensione per Chrome",
"firefox_extension" : "Estensione per Firefox", "firefox_extension" : "Estensione per Firefox",
"opera_extension" : "Estensione per Opera", "opera_extension" : "Estensione per Opera",
"subscribe_bookmarklet" : "Aggiungi la sottoscrizione ai segnalibri (clicca)", "subscribe_bookmarklet" : "Aggiungi la sottoscrizione ai segnalibri (clicca)",
"subscribe_bookmarklet_asc" : "I più vecchi prima", "subscribe_bookmarklet_asc" : "Prima i vecchi",
"subscribe_bookmarklet_desc" : "I più nuovi prima", "subscribe_bookmarklet_desc" : "Prima i recenti",
"next_unread_bookmarklet" : "Prossimo elemento non letto nei segnalibri (trascinali nella barra dei segnalibri)" "next_unread_bookmarklet" : "Bookmarklet al prossimo elemento da leggere (trascinalo nella barra dei segnalibri)"
}, },
"translation" : { "translation" : {
"value" : "Traduzioni", "value" : "Traduzioni",
"message" : "Abbiamo bisogno del tuo aiuto per tradurre CommaFeed.", "message" : "Abbiamo bisogno del tuo aiuto per tradurre CommaFeed.",
"link" : "Vedi come aiutarci nella traduzioni." "link" : "Scopri come aiutarci nella traduzioni."
}, },
"announcements" : "Annunci", "announcements" : "Annunci",
"shortcuts" : { "shortcuts" : {
"mouse_middleclick" : "click centrale del mouse", "mouse_middleclick" : "click centrale del mouse",
"open_next_entry" : "apri l'elemento successivo", "open_next_entry" : "apri successivo",
"open_previous_entry" : "apri l'elemento precedente", "open_previous_entry" : "apri precedente",
"spacebar" : "spazio/shift+spazio", "spacebar" : "SPAZIO/MAIUSC+SPAZIO",
"move_page_down_up" : "muovi la pagina sopra/sotto", "move_page_down_up" : "muove la pagina in su/giù",
"focus_next_entry" : "imposta il fuoco sull'elemento successivo senza aprirlo", "focus_next_entry" : "metti a fuoco l'elemento successivo senza aprirlo",
"focus_previous_entry" : "imposta il fuoco sull'elemento precedente senza aprirlo", "focus_previous_entry" : "metti a fuoco l'elemento precedente senza aprirlo",
"open_next_feed" : "apri il feed successivo od una categoria", "open_next_feed" : "apri il prossimo feed o categoria",
"open_previous_feed" : "apri il feed precedente od una categoria", "open_previous_feed" : "apri il feed o la categoria precedente",
"open_close_current_entry" : "apri/chiusi la categoria corrente", "open_close_current_entry" : "apri/chiudi la voce corrente",
"open_current_entry_in_new_window" : "apri il corrente elemento in una nuova finestra", "open_current_entry_in_new_window" : "apri la voce corrente in una nuova finestra",
"open_current_entry_in_new_window_background" : "apri il corrente elemento in una nuova finestra in secondo piano", "open_current_entry_in_new_window_background" : "apri la voce corrente in una nuova finestra in secondo piano",
"star_unstar" : "segna/togli il segno all'elemento corrente", "star_unstar" : "metti/togli la tua preferenza alla voce corrente",
"mark_current_entry" : "segna come letto/non letto l'elemento corrente", "mark_current_entry" : "segna la voce corrente come letta/non letta",
"mark_all_as_read" : "segna come letti tutti gli elementi", "mark_all_as_read" : "segna tutte le voci come lette",
"open_in_new_tab_mark_as_read" : "apri l'elemento in una nuova finestra e segnala come letta", "open_in_new_tab_mark_as_read" : "apri voce in un nuovo tab e segna come letta",
"fullscreen" : "alterna la modalità a schermo intero", "fullscreen" : "commuta la modalità a schermo intero",
"font_size" : "aumenta/decrementa la grandezza del font dell'elemento corrente", "font_size" : "aumenta/decrementa la dimensione del font per la voce corrente",
"go_to_all" : "vai nella visione totale", "go_to_all" : "vai alla vista Tutti",
"go_to_starred" : "vai nella visione dei preferiti", "go_to_starred" : "vai alla vista Preferiti",
"feed_search" : "naviga in una sottoscrizione scrivendo il suo nome" "feed_search" : "raggiungi una sottoscrizione scrivendo il suo nome"
} }
} }
} }

View File

@@ -360,3 +360,14 @@ module.directive('metricGauge', function() {
templateUrl : 'templates/_metrics.gauge.html' templateUrl : 'templates/_metrics.gauge.html'
}; };
}); });
module.directive('metricTimer', function() {
return {
scope : {
metric : '=',
label : '='
},
restrict : 'E',
templateUrl : 'templates/_metrics.timer.html'
};
});

View File

@@ -19,7 +19,7 @@ module.service('LangService', [function() {
'nn': 'Norsk (nynorsk)', 'nn': 'Norsk (nynorsk)',
'pt': 'Português', 'pt': 'Português',
'pl': 'Polski', 'pl': 'Polski',
'ru': 'русский', 'ru': 'Русский',
'fi': 'Suomi', 'fi': 'Suomi',
'sv': 'Svenska', 'sv': 'Svenska',
'zh': '简体中文', 'zh': '简体中文',
@@ -31,4 +31,4 @@ module.service('LangService', [function() {
'cs': 'Čeština', 'cs': 'Čeština',
'ms': 'Bahasa Malaysian' 'ms': 'Bahasa Malaysian'
} }
}]); }]);

View File

@@ -4,14 +4,8 @@
<dt>Mean</dt> <dt>Mean</dt>
<dd>{{metric.meanRate | number:2}}</dd> <dd>{{metric.meanRate | number:2}}</dd>
<dt>1 min</dt> <dt>1/5/15 min</dt>
<dd>{{metric.oneMinuteRate | number:2}}</dd> <dd>{{metric.oneMinuteRate | number:2}} {{metric.fiveMinuteRate | number:2}} {{metric.fifteenMinuteRate | number:2}}</dd>
<dt>5 min</dt>
<dd>{{metric.fiveMinuteRate | number:2}}</dd>
<dt>15 min</dt>
<dd>{{metric.fifteenMinuteRate | number:2}}</dd>
<dt>Total</dt> <dt>Total</dt>
<dd>{{metric.count}}</dd> <dd>{{metric.count}}</dd>

View File

@@ -0,0 +1,17 @@
<div>
<span>{{label}}</span>
<dl class="dl-horizontal">
<dt>Mean</dt>
<dd>{{metric.meanRate | number:2}}</dd>
<dt>1/5/15 min</dt>
<dd>{{metric.oneMinuteRate | number:2}} {{metric.fiveMinuteRate | number:2}} {{metric.fifteenMinuteRate | number:2}}</dd>
<dt>Total</dt>
<dd>{{metric.count}}</dd>
<dt>min/max/mean (ms)</dt>
<dd>{{metric.snapshot.min/1000000 | number:0}} {{metric.snapshot.max/1000000 | number:0}} {{metric.snapshot.mean/1000000 | number:0}}</dd>
</dl>
</div>

View File

@@ -6,14 +6,14 @@
</button> </button>
</div> </div>
<div class="btn-group"> <div class="btn-group" id="toolbar-nav">
<a type="button" class="btn btn-default" ng-click="previousEntry()" title="{{ 'toolbar.previous_entry' | translate }}"> <a type="button" class="btn btn-default" ng-click="previousEntry()" title="{{ 'toolbar.previous_entry' | translate }}">
<i class="icon-chevron-up"></i> <i class="icon-chevron-up"></i>
</a> </a>
<a type="button" class="btn btn-default" ng-click="nextEntry()" title="{{ 'toolbar.next_entry' | translate }}"> <a type="button" class="btn btn-default" ng-click="nextEntry()" title="{{ 'toolbar.next_entry' | translate }}">
<i class="icon-chevron-down"></i> <i class="icon-chevron-down"></i>
</a> </a>
<div class="btn-group"> <div class="btn-group" id="toolbar-refresh">
<a type="button" class="btn btn-default" ng-click="refresh()" title="{{ 'toolbar.refresh' | translate }}"> <a type="button" class="btn btn-default" ng-click="refresh()" title="{{ 'toolbar.refresh' | translate }}">
<i class="icon-refresh"></i> <i class="icon-refresh"></i>
</a> </a>
@@ -28,7 +28,7 @@
</div> </div>
</div> </div>
<div class="btn-group"> <div class="btn-group" id="toolbar-mark-read">
<a type="button" class="btn btn-default" ng-click="markAllAsRead()" title="{{ 'toolbar.mark_all_as_read' | translate }}"> <a type="button" class="btn btn-default" ng-click="markAllAsRead()" title="{{ 'toolbar.mark_all_as_read' | translate }}">
<i class="icon-ok"></i> <i class="icon-ok"></i>
</a> </a>
@@ -57,7 +57,7 @@
</button> </button>
</div> </div>
<div class="actions btn-group"> <div class="actions btn-group" id="toolbar-read-mode">
<div ng-if="!MobileService.mobile || MobileService.rightMenu"> <div ng-if="!MobileService.mobile || MobileService.rightMenu">
<div class="btn-group read-mode"> <div class="btn-group read-mode">
<button type="button" class="btn btn-default" ng-click="settingsService.settings.readingMode = 'unread'" <button type="button" class="btn btn-default" ng-click="settingsService.settings.readingMode = 'unread'"
@@ -66,14 +66,14 @@
ng-class="{'active': settingsService.settings.readingMode == 'all'}">{{ 'toolbar.all' | translate }}</button> ng-class="{'active': settingsService.settings.readingMode == 'all'}">{{ 'toolbar.all' | translate }}</button>
</div> </div>
<div class="btn-group"> <div class="btn-group" id="toolbar-read-order">
<a type="button" class="btn btn-default" ng-click="toggleOrder()" title="{{ 'toolbar.sort_by_asc_desc' | translate }}"> <a type="button" class="btn btn-default" ng-click="toggleOrder()" title="{{ 'toolbar.sort_by_asc_desc' | translate }}">
<i <i
ng-class="{'icon-arrow-up' : settingsService.settings.readingOrder == 'asc', 'icon-arrow-down': settingsService.settings.readingOrder == 'desc'}"></i> ng-class="{'icon-arrow-up' : settingsService.settings.readingOrder == 'asc', 'icon-arrow-down': settingsService.settings.readingOrder == 'desc'}"></i>
</a> </a>
</div> </div>
<div class="btn-group"> <div class="btn-group" id="toolbar-read-view-settings">
<a type="button" class="btn btn-default" ng-click="settingsService.settings.viewMode = 'title'" <a type="button" class="btn btn-default" ng-click="settingsService.settings.viewMode = 'title'"
ng-class="{'active': settingsService.settings.viewMode == 'title'}" title="{{ 'toolbar.titles_only' | translate }}"> ng-class="{'active': settingsService.settings.viewMode == 'title'}" title="{{ 'toolbar.titles_only' | translate }}">
<i class="icon-list"></i> <i class="icon-list"></i>
@@ -84,7 +84,7 @@
</a> </a>
</div> </div>
<div class="btn-group"> <div class="btn-group" id="toolbar-settings">
<a class="btn btn-default" ng-click="toSettings()" title="{{ 'toolbar.settings' | translate }}"> <a class="btn btn-default" ng-click="toSettings()" title="{{ 'toolbar.settings' | translate }}">
<i class="icon-cog"></i> <i class="icon-cog"></i>
</a> </a>
@@ -133,4 +133,4 @@
</div> </div>
<span ng-if="!MobileService.mobile" ng-bind-html="ServerService.announcement | trustHtml"></span> <span ng-if="!MobileService.mobile" ng-bind-html="ServerService.announcement | trustHtml"></span>
</div> </div>
</div> </div>

View File

@@ -1,21 +1,28 @@
<div> <div>
<metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedQueues.refill']" label="'Refresh queue refill rate (/sec)'"></metric-meter> <div class="col-md-6">
<metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedRefreshTaskGiver.feedRefreshed']" label="'Feed refreshed (/sec)'"></metric-meter> <metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedQueues.refill']" label="'Refresh queue refill rate (/sec)'"></metric-meter>
<metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedRefreshUpdater.feedUpdated']" label="'Feed updated (/sec)'"></metric-meter> <metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedRefreshTaskGiver.feedRefreshed']" label="'Feed refreshed (/sec)'"></metric-meter>
<metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheHit']" label="'Entry cache hit (/sec)'"></metric-meter> <metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedRefreshUpdater.feedUpdated']" label="'Feed updated (/sec)'"></metric-meter>
<metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheMiss']" label="'Entry cache miss (/sec)'"></metric-meter> <metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheHit']" label="'Entry cache hit (/sec)'"></metric-meter>
<metric-meter metric="metrics.meters['com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheMiss']" label="'Entry cache miss (/sec)'"></metric-meter>
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-updater.active']"
label="'Feed Updater active'"></metric-gauge>
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-updater.pending']"
label="'Feed Updater queued'"></metric-gauge>
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-updater.active']" <metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.active']"
label="'Feed Updater active'"></metric-gauge> label="'Feed Worker active'"></metric-gauge>
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-updater.pending']" <metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.pending']"
label="'Feed Updater queued'"></metric-gauge> label="'Feed Worker queued'"></metric-gauge>
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.active']" <metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedQueues.addQueue']" label="'Task Giver Add Queue'"></metric-gauge>
label="'Feed Worker active'"></metric-gauge> <metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedQueues.takeQueue']" label="'Task Giver Take Queue'"></metric-gauge>
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.pending']" <metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedQueues.giveBackQueue']" label="'Task Giver Give Back Queue'"></metric-gauge>
label="'Feed Worker queued'"></metric-gauge> </div>
<div class="col-md-6">
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedQueues.addQueue']" label="'Task Giver Add Queue'"></metric-gauge> <div ng-repeat="(name, timer) in metrics.timers">
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedQueues.takeQueue']" label="'Task Giver Take Queue'"></metric-gauge> <metric-timer metric="timer" label="name"></metric-gauge>
<metric-gauge metric="metrics.gauges['com.commafeed.backend.feed.FeedQueues.giveBackQueue']" label="'Task Giver Give Back Queue'"></metric-gauge> </div>
</div>
</div> </div>

View File

@@ -61,8 +61,7 @@
<div class="entry-body-content"> <div class="entry-body-content">
<div ng-if="!MobileService.mobile" ng-bind-html="entry.content | iframeHttpsRewrite| highlight:keywords | trustHtml"></div> <div ng-if="!MobileService.mobile" ng-bind-html="entry.content | iframeHttpsRewrite| highlight:keywords | trustHtml"></div>
<div ng-if="MobileService.mobile" ng-bind-html="entry.content | iframeHttpsRewrite| highlight:keywords | appendImageTitles | trustHtml"></div> <div ng-if="MobileService.mobile" ng-bind-html="entry.content | iframeHttpsRewrite| highlight:keywords | appendImageTitles | trustHtml"></div>
<div class="entry-enclosure" ng-if="entry.enclosureType && (entry.enclosureUrl && entry.content && entry.content.indexOf(entry.enclosureUrl) == -1)">
<div class="entry-enclosure" ng-if="entry.enclosureType">
<video controls ng-if="entry.enclosureType && entry.enclosureType.indexOf('video') == 0"> <video controls ng-if="entry.enclosureType && entry.enclosureType.indexOf('video') == 0">
<source ng-src="{{entry.enclosureUrl | trustUrl}}" type="{{entry.enclosureType}}" /> <source ng-src="{{entry.enclosureUrl | trustUrl}}" type="{{entry.enclosureType}}" />
</video> </video>

View File

@@ -1,21 +1,8 @@
package com.commafeed; package com.commafeed;
import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.forms.MultiPartBundle;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.migrations.MigrationsBundle;
import io.dropwizard.server.DefaultServerFactory;
import io.dropwizard.servlets.CacheBustingFilter;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@@ -26,7 +13,6 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.server.session.SessionHandler;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
@@ -60,13 +46,21 @@ import com.commafeed.frontend.servlet.CustomCssServlet;
import com.commafeed.frontend.servlet.LogoutServlet; import com.commafeed.frontend.servlet.LogoutServlet;
import com.commafeed.frontend.servlet.NextUnreadServlet; import com.commafeed.frontend.servlet.NextUnreadServlet;
import com.commafeed.frontend.session.SessionHelperFactoryProvider; import com.commafeed.frontend.session.SessionHelperFactoryProvider;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.wordnik.swagger.jaxrs.config.BeanConfig;
import com.wordnik.swagger.jaxrs.listing.ApiListingResource; import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.forms.MultiPartBundle;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.migrations.MigrationsBundle;
import io.dropwizard.server.DefaultServerFactory;
import io.dropwizard.servlets.CacheBustingFilter;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
public class CommaFeedApplication extends Application<CommaFeedConfiguration> { public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
@@ -158,21 +152,6 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
environment.lifecycle().manage(injector.getInstance(FeedRefreshWorker.class)); environment.lifecycle().manage(injector.getInstance(FeedRefreshWorker.class));
environment.lifecycle().manage(injector.getInstance(FeedRefreshUpdater.class)); environment.lifecycle().manage(injector.getInstance(FeedRefreshUpdater.class));
// Swagger
environment.jersey().register(new ApiListingResource());
environment.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
String modelsPackage = "com.commafeed.frontend.model";
String requestsPackage = "com.commafeed.frontend.model.request";
String endpointsPackage = "com.commafeed.frontend.resource";
List<String> packages = Arrays.asList(modelsPackage, requestsPackage, endpointsPackage);
BeanConfig swaggerConfig = new BeanConfig();
swaggerConfig.setTitle("CommaFeed");
swaggerConfig.setVersion("1");
swaggerConfig.setBasePath("/rest");
swaggerConfig.setResourcePackage(StringUtils.join(packages, ","));
swaggerConfig.setScan(true);
// cache configuration // cache configuration
// prevent caching on REST resources, except for favicons // prevent caching on REST resources, except for favicons
environment.servlets().addFilter("cache-filter", new CacheBustingFilter() { environment.servlets().addFilter("cache-filter", new CacheBustingFilter() {

View File

@@ -16,7 +16,7 @@ import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
class ContentEncodingInterceptor implements HttpResponseInterceptor { class ContentEncodingInterceptor implements HttpResponseInterceptor {
private static final Set<String> ALLOWED_CONTENT_ENCODINGS = new HashSet<>(Arrays.asList("gzip", "x-gzip", "deflate", "identity")); private static final Set<String> ALLOWED_CONTENT_ENCODINGS = new HashSet<>(Arrays.asList("gzip", "x-gzip", "deflate", "identity"));
@Override @Override
@@ -28,17 +28,17 @@ class ContentEncodingInterceptor implements HttpResponseInterceptor {
} }
} }
} }
private boolean containsUnsupportedEncodings(Header contentEncodingHeader) { private boolean containsUnsupportedEncodings(Header contentEncodingHeader) {
HeaderElement[] codecs = contentEncodingHeader.getElements(); HeaderElement[] codecs = contentEncodingHeader.getElements();
for (final HeaderElement codec : codecs) { for (final HeaderElement codec : codecs) {
String codecName = codec.getName().toLowerCase(Locale.US); String codecName = codec.getName().toLowerCase(Locale.US);
if (!ALLOWED_CONTENT_ENCODINGS.contains(codecName)) { if (!ALLOWED_CONTENT_ENCODINGS.contains(codecName)) {
return true; return true;
} }
} }
return false; return false;
} }
@@ -47,9 +47,9 @@ class ContentEncodingInterceptor implements HttpResponseInterceptor {
@Override @Override
public Header getContentEncoding() { public Header getContentEncoding() {
return null; return null;
}; }
}; };
response.setEntity(wrapped); response.setEntity(wrapped);
} }

View File

@@ -70,7 +70,7 @@ public class HttpGetter {
@Inject @Inject
public HttpGetter(CommaFeedConfiguration config) { public HttpGetter(CommaFeedConfiguration config) {
this.userAgent = String.format("CommaFeed/%s (https://www.commafeed.com)", config.getVersion()); this.userAgent = String.format("CommaFeed/%s (https://github.com/Athou/commafeed)", config.getVersion());
} }
public HttpResult getBinary(String url, int timeout) throws ClientProtocolException, IOException, NotModifiedException { public HttpResult getBinary(String url, int timeout) throws ClientProtocolException, IOException, NotModifiedException {

View File

@@ -13,7 +13,7 @@ import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.QFeedCategory; import com.commafeed.backend.model.QFeedCategory;
import com.commafeed.backend.model.QUser; import com.commafeed.backend.model.QUser;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.mysema.query.types.Predicate; import com.querydsl.core.types.Predicate;
@Singleton @Singleton
public class FeedCategoryDAO extends GenericDAO<FeedCategory> { public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
@@ -26,11 +26,11 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
} }
public List<FeedCategory> findAll(User user) { public List<FeedCategory> findAll(User user) {
return newQuery().from(category).where(category.user.eq(user)).join(category.user, QUser.user).fetch().list(category); return query().selectFrom(category).where(category.user.eq(user)).join(category.user, QUser.user).fetchJoin().fetch();
} }
public FeedCategory findById(User user, Long id) { public FeedCategory findById(User user, Long id) {
return newQuery().from(category).where(category.user.eq(user), category.id.eq(id)).uniqueResult(category); return query().selectFrom(category).where(category.user.eq(user), category.id.eq(id)).fetchOne();
} }
public FeedCategory findByName(User user, String name, FeedCategory parent) { public FeedCategory findByName(User user, String name, FeedCategory parent) {
@@ -40,7 +40,7 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
} else { } else {
parentPredicate = category.parent.eq(parent); parentPredicate = category.parent.eq(parent);
} }
return newQuery().from(category).where(category.user.eq(user), category.name.eq(name), parentPredicate).uniqueResult(category); return query().selectFrom(category).where(category.user.eq(user), category.name.eq(name), parentPredicate).fetchOne();
} }
public List<FeedCategory> findByParent(User user, FeedCategory parent) { public List<FeedCategory> findByParent(User user, FeedCategory parent) {
@@ -50,7 +50,7 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
} else { } else {
parentPredicate = category.parent.eq(parent); parentPredicate = category.parent.eq(parent);
} }
return newQuery().from(category).where(category.user.eq(user), parentPredicate).list(category); return query().selectFrom(category).where(category.user.eq(user), parentPredicate).fetch();
} }
public List<FeedCategory> findAllChildrenCategories(User user, FeedCategory parent) { public List<FeedCategory> findAllChildrenCategories(User user, FeedCategory parent) {

View File

@@ -15,9 +15,9 @@ import com.commafeed.backend.model.QFeed;
import com.commafeed.backend.model.QFeedSubscription; import com.commafeed.backend.model.QFeedSubscription;
import com.commafeed.backend.model.QUser; import com.commafeed.backend.model.QUser;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.mysema.query.BooleanBuilder; import com.querydsl.jpa.JPAExpressions;
import com.mysema.query.jpa.hibernate.HibernateQuery; import com.querydsl.jpa.JPQLQuery;
import com.mysema.query.jpa.hibernate.HibernateSubQuery; import com.querydsl.jpa.hibernate.HibernateQuery;
@Singleton @Singleton
public class FeedDAO extends GenericDAO<Feed> { public class FeedDAO extends GenericDAO<Feed> {
@@ -30,26 +30,23 @@ public class FeedDAO extends GenericDAO<Feed> {
} }
public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) { public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) {
BooleanBuilder disabledDatePredicate = new BooleanBuilder(); HibernateQuery<Feed> query = query().selectFrom(feed);
disabledDatePredicate.or(feed.disabledUntil.isNull()); query.where(feed.disabledUntil.isNull().or(feed.disabledUntil.lt(new Date())));
disabledDatePredicate.or(feed.disabledUntil.lt(new Date()));
HibernateQuery query = null;
if (lastLoginThreshold != null) { if (lastLoginThreshold != null) {
QFeedSubscription subs = QFeedSubscription.feedSubscription; QFeedSubscription subs = QFeedSubscription.feedSubscription;
QUser user = QUser.user; QUser user = QUser.user;
query = newQuery().from(subs);
query.join(subs.feed, feed).join(subs.user, user).where(disabledDatePredicate, user.lastLogin.gt(lastLoginThreshold)); JPQLQuery<Integer> subQuery = JPAExpressions.selectOne().from(subs);
} else { subQuery.join(subs.user, user).where(user.lastLogin.gt(lastLoginThreshold));
query = newQuery().from(feed); query.where(subQuery.exists());
query.where(disabledDatePredicate);
} }
return query.orderBy(feed.disabledUntil.asc()).limit(count).distinct().list(feed); return query.orderBy(feed.disabledUntil.asc()).limit(count).distinct().fetch();
} }
public Feed findByUrl(String normalizedUrl) { public Feed findByUrl(String normalizedUrl) {
List<Feed> feeds = newQuery().from(feed).where(feed.normalizedUrlHash.eq(DigestUtils.sha1Hex(normalizedUrl))).list(feed); List<Feed> feeds = query().selectFrom(feed).where(feed.normalizedUrlHash.eq(DigestUtils.sha1Hex(normalizedUrl))).fetch();
Feed feed = Iterables.getFirst(feeds, null); Feed feed = Iterables.getFirst(feeds, null);
if (feed != null && StringUtils.equals(normalizedUrl, feed.getNormalizedUrl())) { if (feed != null && StringUtils.equals(normalizedUrl, feed.getNormalizedUrl())) {
return feed; return feed;
@@ -58,12 +55,12 @@ public class FeedDAO extends GenericDAO<Feed> {
} }
public List<Feed> findByTopic(String topic) { public List<Feed> findByTopic(String topic) {
return newQuery().from(feed).where(feed.pushTopicHash.eq(DigestUtils.sha1Hex(topic))).list(feed); return query().selectFrom(feed).where(feed.pushTopicHash.eq(DigestUtils.sha1Hex(topic))).fetch();
} }
public List<Feed> findWithoutSubscriptions(int max) { public List<Feed> findWithoutSubscriptions(int max) {
QFeedSubscription sub = QFeedSubscription.feedSubscription; QFeedSubscription sub = QFeedSubscription.feedSubscription;
return newQuery().from(feed).where(new HibernateSubQuery().from(sub).where(sub.feed.eq(feed)).notExists()).limit(max).list(feed); return query().selectFrom(feed).where(JPAExpressions.selectOne().from(sub).where(sub.feed.eq(feed)).notExists()).limit(max)
// return newQuery().from(feed).leftJoin(feed.subscriptions, sub).where(sub.id.isNull()).limit(max).list(feed); .fetch();
} }
} }

View File

@@ -10,13 +10,14 @@ import org.hibernate.SessionFactory;
import com.commafeed.backend.model.FeedEntryContent; import com.commafeed.backend.model.FeedEntryContent;
import com.commafeed.backend.model.QFeedEntry; import com.commafeed.backend.model.QFeedEntry;
import com.commafeed.backend.model.QFeedEntryContent; import com.commafeed.backend.model.QFeedEntryContent;
import com.google.common.collect.Iterables; import com.querydsl.jpa.JPAExpressions;
import com.mysema.query.jpa.hibernate.HibernateSubQuery; import com.querydsl.jpa.JPQLQuery;
@Singleton @Singleton
public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> { public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
private QFeedEntryContent content = QFeedEntryContent.feedEntryContent; private QFeedEntryContent content = QFeedEntryContent.feedEntryContent;
private QFeedEntry entry = QFeedEntry.feedEntry;
@Inject @Inject
public FeedEntryContentDAO(SessionFactory sessionFactory) { public FeedEntryContentDAO(SessionFactory sessionFactory) {
@@ -24,16 +25,14 @@ public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
} }
public Long findExisting(String contentHash, String titleHash) { public Long findExisting(String contentHash, String titleHash) {
List<Long> list = newQuery().from(content).where(content.contentHash.eq(contentHash), content.titleHash.eq(titleHash)).limit(1) return query().select(content.id).from(content).where(content.contentHash.eq(contentHash), content.titleHash.eq(titleHash))
.list(content.id); .fetchFirst();
return Iterables.getFirst(list, null);
} }
public int deleteWithoutEntries(int max) { public int deleteWithoutEntries(int max) {
QFeedEntry entry = QFeedEntry.feedEntry;
HibernateSubQuery subQuery = new HibernateSubQuery().from(entry).where(entry.content.id.eq(content.id)); JPQLQuery<Integer> subQuery = JPAExpressions.selectOne().from(entry).where(entry.content.id.eq(content.id));
List<FeedEntryContent> list = newQuery().from(content).where(subQuery.notExists()).limit(max).list(content); List<FeedEntryContent> list = query().selectFrom(content).where(subQuery.notExists()).limit(max).fetch();
int deleted = list.size(); int deleted = list.size();
delete(list); delete(list);

View File

@@ -6,18 +6,17 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.QFeedEntry; import com.commafeed.backend.model.QFeedEntry;
import com.google.common.collect.Iterables; import com.querydsl.core.Tuple;
import com.mysema.query.Tuple; import com.querydsl.core.types.dsl.NumberExpression;
import com.mysema.query.types.expr.NumberExpression;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Singleton @Singleton
public class FeedEntryDAO extends GenericDAO<FeedEntry> { public class FeedEntryDAO extends GenericDAO<FeedEntry> {
@@ -30,24 +29,25 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
} }
public Long findExisting(String guid, Feed feed) { public Long findExisting(String guid, Feed feed) {
List<Long> list = newQuery().from(entry).where(entry.guidHash.eq(DigestUtils.sha1Hex(guid)), entry.feed.eq(feed)).limit(1) return query().select(entry.id).from(entry).where(entry.guidHash.eq(DigestUtils.sha1Hex(guid)), entry.feed.eq(feed)).limit(1)
.list(entry.id); .fetchOne();
return Iterables.getFirst(list, null);
} }
public List<FeedCapacity> findFeedsExceedingCapacity(long maxCapacity, long max) { public List<FeedCapacity> findFeedsExceedingCapacity(long maxCapacity, long max) {
NumberExpression<Long> count = entry.id.count(); NumberExpression<Long> count = entry.id.count();
List<Tuple> tuples = newQuery().from(entry).groupBy(entry.feed).having(count.gt(maxCapacity)).limit(max).list(entry.feed.id, count); List<Tuple> tuples = query().select(entry.feed.id, count).from(entry).groupBy(entry.feed).having(count.gt(maxCapacity)).limit(max)
.fetch();
return tuples.stream().map(t -> new FeedCapacity(t.get(entry.feed.id), t.get(count))).collect(Collectors.toList()); return tuples.stream().map(t -> new FeedCapacity(t.get(entry.feed.id), t.get(count))).collect(Collectors.toList());
} }
public int delete(Long feedId, long max) { public int delete(Long feedId, long max) {
List<FeedEntry> list = newQuery().from(entry).where(entry.feed.id.eq(feedId)).limit(max).list(entry);
List<FeedEntry> list = query().selectFrom(entry).where(entry.feed.id.eq(feedId)).limit(max).fetch();
return delete(list); return delete(list);
} }
public int deleteOldEntries(Long feedId, long max) { public int deleteOldEntries(Long feedId, long max) {
List<FeedEntry> list = newQuery().from(entry).where(entry.feed.id.eq(feedId)).orderBy(entry.updated.asc()).limit(max).list(entry); List<FeedEntry> list = query().selectFrom(entry).where(entry.feed.id.eq(feedId)).orderBy(entry.updated.asc()).limit(max).fetch();
return delete(list); return delete(list);
} }

View File

@@ -30,9 +30,9 @@ import com.commafeed.backend.model.UserSettings.ReadingOrder;
import com.commafeed.frontend.model.UnreadCount; import com.commafeed.frontend.model.UnreadCount;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.mysema.query.BooleanBuilder; import com.querydsl.core.BooleanBuilder;
import com.mysema.query.Tuple; import com.querydsl.core.Tuple;
import com.mysema.query.jpa.hibernate.HibernateQuery; import com.querydsl.jpa.hibernate.HibernateQuery;
@Singleton @Singleton
public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> { public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
@@ -62,13 +62,13 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
builder.append(o2.getEntryUpdated(), o1.getEntryUpdated()); builder.append(o2.getEntryUpdated(), o1.getEntryUpdated());
builder.append(o2.getId(), o1.getId()); builder.append(o2.getId(), o1.getId());
return builder.toComparison(); return builder.toComparison();
}; }
}; };
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ASC = Ordering.from(STATUS_COMPARATOR_DESC).reverse(); private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ASC = Ordering.from(STATUS_COMPARATOR_DESC).reverse();
public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) { public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) {
List<FeedEntryStatus> statuses = newQuery().from(status).where(status.entry.eq(entry), status.subscription.eq(sub)).list(status); List<FeedEntryStatus> statuses = query().selectFrom(status).where(status.entry.eq(entry), status.subscription.eq(sub)).fetch();
FeedEntryStatus status = Iterables.getFirst(statuses, null); FeedEntryStatus status = Iterables.getFirst(statuses, null);
return handleStatus(user, status, sub, entry); return handleStatus(user, status, sub, entry);
} }
@@ -93,7 +93,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
} }
public List<FeedEntryStatus> findStarred(User user, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent) { public List<FeedEntryStatus> findStarred(User user, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent) {
HibernateQuery query = newQuery().from(status).where(status.user.eq(user), status.starred.isTrue()); HibernateQuery<FeedEntryStatus> query = query().selectFrom(status).where(status.user.eq(user), status.starred.isTrue());
if (newerThan != null) { if (newerThan != null) {
query.where(status.entryInserted.gt(newerThan)); query.where(status.entryInserted.gt(newerThan));
} }
@@ -110,7 +110,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
query.setTimeout(timeout / 1000); query.setTimeout(timeout / 1000);
} }
List<FeedEntryStatus> statuses = query.list(status); List<FeedEntryStatus> statuses = query.fetch();
for (FeedEntryStatus status : statuses) { for (FeedEntryStatus status : statuses) {
status = handleStatus(user, status, status.getSubscription(), status.getEntry()); status = handleStatus(user, status, status.getSubscription(), status.getEntry());
fetchTags(user, status); fetchTags(user, status);
@@ -118,10 +118,10 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return lazyLoadContent(includeContent, statuses); return lazyLoadContent(includeContent, statuses);
} }
private HibernateQuery buildQuery(User user, FeedSubscription sub, boolean unreadOnly, List<FeedEntryKeyword> keywords, Date newerThan, private HibernateQuery<FeedEntry> buildQuery(User user, FeedSubscription sub, boolean unreadOnly, List<FeedEntryKeyword> keywords,
int offset, int limit, ReadingOrder order, Date last, String tag) { Date newerThan, int offset, int limit, ReadingOrder order, Date last, String tag) {
HibernateQuery query = newQuery().from(entry).where(entry.feed.eq(sub.getFeed())); HibernateQuery<FeedEntry> query = query().selectFrom(entry).where(entry.feed.eq(sub.getFeed()));
if (CollectionUtils.isNotEmpty(keywords)) { if (CollectionUtils.isNotEmpty(keywords)) {
query.join(entry.content, content); query.join(entry.content, content);
@@ -197,8 +197,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
FixedSizeSortedSet<FeedEntryStatus> set = new FixedSizeSortedSet<FeedEntryStatus>(capacity, comparator); FixedSizeSortedSet<FeedEntryStatus> set = new FixedSizeSortedSet<FeedEntryStatus>(capacity, comparator);
for (FeedSubscription sub : subs) { for (FeedSubscription sub : subs) {
Date last = (order != null && set.isFull()) ? set.last().getEntryUpdated() : null; Date last = (order != null && set.isFull()) ? set.last().getEntryUpdated() : null;
HibernateQuery query = buildQuery(user, sub, unreadOnly, keywords, newerThan, -1, capacity, order, last, tag); HibernateQuery<FeedEntry> query = buildQuery(user, sub, unreadOnly, keywords, newerThan, -1, capacity, order, last, tag);
List<Tuple> tuples = query.list(entry.id, entry.updated, status.id); List<Tuple> tuples = query.select(entry.id, entry.updated, status.id).fetch();
for (Tuple tuple : tuples) { for (Tuple tuple : tuples) {
Long id = tuple.get(entry.id); Long id = tuple.get(entry.id);
Date updated = tuple.get(entry.updated); Date updated = tuple.get(entry.updated);
@@ -245,8 +245,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
public UnreadCount getUnreadCount(User user, FeedSubscription subscription) { public UnreadCount getUnreadCount(User user, FeedSubscription subscription) {
UnreadCount uc = null; UnreadCount uc = null;
HibernateQuery query = buildQuery(user, subscription, true, null, null, -1, -1, null, null, null); HibernateQuery<FeedEntry> query = buildQuery(user, subscription, true, null, null, -1, -1, null, null, null);
List<Tuple> tuples = query.list(entry.count(), entry.updated.max()); List<Tuple> tuples = query.select(entry.count(), entry.updated.max()).fetch();
for (Tuple tuple : tuples) { for (Tuple tuple : tuples) {
Long count = tuple.get(entry.count()); Long count = tuple.get(entry.count());
Date updated = tuple.get(entry.updated.max()); Date updated = tuple.get(entry.updated.max());
@@ -266,7 +266,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
} }
public List<FeedEntryStatus> getOldStatuses(Date olderThan, int limit) { public List<FeedEntryStatus> getOldStatuses(Date olderThan, int limit) {
return newQuery().from(status).where(status.entryInserted.lt(olderThan), status.starred.isFalse()).limit(limit).list(status); return query().selectFrom(status).where(status.entryInserted.lt(olderThan), status.starred.isFalse()).limit(limit).fetch();
} }
} }

View File

@@ -23,10 +23,10 @@ public class FeedEntryTagDAO extends GenericDAO<FeedEntryTag> {
} }
public List<String> findByUser(User user) { public List<String> findByUser(User user) {
return newQuery().from(tag).where(tag.user.eq(user)).distinct().list(tag.name); return query().selectDistinct(tag.name).from(tag).where(tag.user.eq(user)).fetch();
} }
public List<FeedEntryTag> findByEntry(User user, FeedEntry entry) { public List<FeedEntryTag> findByEntry(User user, FeedEntry entry) {
return newQuery().from(tag).where(tag.user.eq(user), tag.entry.eq(entry)).list(tag); return query().selectFrom(tag).where(tag.user.eq(user), tag.entry.eq(entry)).fetch();
} }
} }

View File

@@ -16,7 +16,7 @@ import com.commafeed.backend.model.Models;
import com.commafeed.backend.model.QFeedSubscription; import com.commafeed.backend.model.QFeedSubscription;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.mysema.query.jpa.hibernate.HibernateQuery; import com.querydsl.jpa.hibernate.HibernateQuery;
@Singleton @Singleton
public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> { public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
@@ -29,34 +29,34 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
} }
public FeedSubscription findById(User user, Long id) { public FeedSubscription findById(User user, Long id) {
List<FeedSubscription> subs = newQuery().from(sub).where(sub.user.eq(user), sub.id.eq(id)).leftJoin(sub.feed).fetch() List<FeedSubscription> subs = query().selectFrom(sub).where(sub.user.eq(user), sub.id.eq(id)).leftJoin(sub.feed).fetchJoin()
.leftJoin(sub.category).fetch().list(sub); .leftJoin(sub.category).fetchJoin().fetch();
return initRelations(Iterables.getFirst(subs, null)); return initRelations(Iterables.getFirst(subs, null));
} }
public List<FeedSubscription> findByFeed(Feed feed) { public List<FeedSubscription> findByFeed(Feed feed) {
return newQuery().from(sub).where(sub.feed.eq(feed)).list(sub); return query().selectFrom(sub).where(sub.feed.eq(feed)).fetch();
} }
public FeedSubscription findByFeed(User user, Feed feed) { public FeedSubscription findByFeed(User user, Feed feed) {
List<FeedSubscription> subs = newQuery().from(sub).where(sub.user.eq(user), sub.feed.eq(feed)).list(sub); List<FeedSubscription> subs = query().selectFrom(sub).where(sub.user.eq(user), sub.feed.eq(feed)).fetch();
return initRelations(Iterables.getFirst(subs, null)); return initRelations(Iterables.getFirst(subs, null));
} }
public List<FeedSubscription> findAll(User user) { public List<FeedSubscription> findAll(User user) {
List<FeedSubscription> subs = newQuery().from(sub).where(sub.user.eq(user)).leftJoin(sub.feed).fetch().leftJoin(sub.category) List<FeedSubscription> subs = query().selectFrom(sub).where(sub.user.eq(user)).leftJoin(sub.feed).fetchJoin()
.fetch().list(sub); .leftJoin(sub.category).fetchJoin().fetch();
return initRelations(subs); return initRelations(subs);
} }
public List<FeedSubscription> findByCategory(User user, FeedCategory category) { public List<FeedSubscription> findByCategory(User user, FeedCategory category) {
HibernateQuery query = newQuery().from(sub).where(sub.user.eq(user)); HibernateQuery<FeedSubscription> query = query().selectFrom(sub).where(sub.user.eq(user));
if (category == null) { if (category == null) {
query.where(sub.category.isNull()); query.where(sub.category.isNull());
} else { } else {
query.where(sub.category.eq(category)); query.where(sub.category.eq(category));
} }
return initRelations(query.list(sub)); return initRelations(query.fetch());
} }
public List<FeedSubscription> findByCategories(User user, List<FeedCategory> categories) { public List<FeedSubscription> findByCategories(User user, List<FeedCategory> categories) {

View File

@@ -1,22 +1,25 @@
package com.commafeed.backend.dao; package com.commafeed.backend.dao;
import io.dropwizard.hibernate.AbstractDAO;
import java.util.Collection; import java.util.Collection;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import com.commafeed.backend.model.AbstractModel; import com.commafeed.backend.model.AbstractModel;
import com.mysema.query.jpa.hibernate.HibernateQuery; import com.querydsl.jpa.hibernate.HibernateQueryFactory;
import io.dropwizard.hibernate.AbstractDAO;
public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T> { public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T> {
private HibernateQueryFactory factory;
protected GenericDAO(SessionFactory sessionFactory) { protected GenericDAO(SessionFactory sessionFactory) {
super(sessionFactory); super(sessionFactory);
this.factory = new HibernateQueryFactory(() -> currentSession());
} }
protected HibernateQuery newQuery() { protected HibernateQueryFactory query() {
return new HibernateQuery(currentSession()); return factory;
} }
public void saveOrUpdate(T model) { public void saveOrUpdate(T model) {

View File

@@ -18,13 +18,13 @@ public class UnitOfWork {
} }
public static void run(SessionFactory sessionFactory, SessionRunner sessionRunner) { public static void run(SessionFactory sessionFactory, SessionRunner sessionRunner) {
run(sessionFactory, () -> { call(sessionFactory, () -> {
sessionRunner.runInSession(); sessionRunner.runInSession();
return null; return null;
}); });
} }
public static <T> T run(SessionFactory sessionFactory, SessionRunnerReturningValue<T> sessionRunner) { public static <T> T call(SessionFactory sessionFactory, SessionRunnerReturningValue<T> sessionRunner) {
final Session session = sessionFactory.openSession(); final Session session = sessionFactory.openSession();
if (ManagedSessionContext.hasBind(sessionFactory)) { if (ManagedSessionContext.hasBind(sessionFactory)) {
throw new IllegalStateException("Already in a unit of work!"); throw new IllegalStateException("Already in a unit of work!");

View File

@@ -19,18 +19,18 @@ public class UserDAO extends GenericDAO<User> {
} }
public User findByName(String name) { public User findByName(String name) {
return newQuery().from(user).where(user.name.equalsIgnoreCase(name)).uniqueResult(user); return query().selectFrom(user).where(user.name.equalsIgnoreCase(name)).fetchOne();
} }
public User findByApiKey(String key) { public User findByApiKey(String key) {
return newQuery().from(user).where(user.apiKey.equalsIgnoreCase(key)).uniqueResult(user); return query().selectFrom(user).where(user.apiKey.equalsIgnoreCase(key)).fetchOne();
} }
public User findByEmail(String email) { public User findByEmail(String email) {
return newQuery().from(user).where(user.email.equalsIgnoreCase(email)).uniqueResult(user); return query().selectFrom(user).where(user.email.equalsIgnoreCase(email)).fetchOne();
} }
public long count() { public long count() {
return newQuery().from(user).count(); return query().selectFrom(user).fetchCount();
} }
} }

View File

@@ -25,11 +25,11 @@ public class UserRoleDAO extends GenericDAO<UserRole> {
} }
public List<UserRole> findAll() { public List<UserRole> findAll() {
return newQuery().from(role).leftJoin(role.user).fetch().distinct().list(role); return query().selectFrom(role).leftJoin(role.user).fetchJoin().distinct().fetch();
} }
public List<UserRole> findAll(User user) { public List<UserRole> findAll(User user) {
return newQuery().from(role).where(role.user.eq(user)).distinct().list(role); return query().selectFrom(role).where(role.user.eq(user)).distinct().fetch();
} }
public Set<Role> findRoles(User user) { public Set<Role> findRoles(User user) {

View File

@@ -20,6 +20,6 @@ public class UserSettingsDAO extends GenericDAO<UserSettings> {
} }
public UserSettings findByUser(User user) { public UserSettings findByUser(User user) {
return newQuery().from(settings).where(settings.user.eq(user)).uniqueResult(settings); return query().selectFrom(settings).where(settings.user.eq(user)).fetchFirst();
} }
} }

View File

@@ -8,9 +8,6 @@ import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.client.utils.URLEncodedUtils;
@@ -27,8 +24,11 @@ import com.google.api.services.youtube.model.Channel;
import com.google.api.services.youtube.model.ChannelListResponse; import com.google.api.services.youtube.model.ChannelListResponse;
import com.google.api.services.youtube.model.Thumbnail; import com.google.api.services.youtube.model.Thumbnail;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class YoutubeFaviconFetcher extends AbstractFaviconFetcher { public class YoutubeFaviconFetcher extends AbstractFaviconFetcher {
@@ -54,8 +54,7 @@ public class YoutubeFaviconFetcher extends AbstractFaviconFetcher {
try { try {
List<NameValuePair> params = URLEncodedUtils.parse(url.substring(url.indexOf("?") + 1), StandardCharsets.UTF_8); List<NameValuePair> params = URLEncodedUtils.parse(url.substring(url.indexOf("?") + 1), StandardCharsets.UTF_8);
Optional<NameValuePair> userId = params.stream().filter(nvp -> nvp.getName().equalsIgnoreCase("user")).findFirst(); Optional<NameValuePair> userId = params.stream().filter(nvp -> nvp.getName().equalsIgnoreCase("user")).findFirst();
Optional<NameValuePair> channelId = params.stream().filter(nvp -> nvp.getName().equalsIgnoreCase("channel")).findFirst(); Optional<NameValuePair> channelId = params.stream().filter(nvp -> nvp.getName().equalsIgnoreCase("channel_id")).findFirst();
System.out.println(userId.isPresent());
if (!userId.isPresent() && !channelId.isPresent()) { if (!userId.isPresent() && !channelId.isPresent()) {
return null; return null;
} }

View File

@@ -108,7 +108,7 @@ public class FeedQueues {
// add feeds that are up to refresh from the database // add feeds that are up to refresh from the database
int count = batchSize - contexts.size(); int count = batchSize - contexts.size();
if (count > 0) { if (count > 0) {
List<Feed> feeds = UnitOfWork.run(sessionFactory, () -> feedDAO.findNextUpdatable(count, getLastLoginThreshold())); List<Feed> feeds = UnitOfWork.call(sessionFactory, () -> feedDAO.findNextUpdatable(count, getLastLoginThreshold()));
for (Feed feed : feeds) { for (Feed feed : feeds) {
contexts.add(new FeedRefreshContext(feed, false)); contexts.add(new FeedRefreshContext(feed, false));
} }

View File

@@ -5,11 +5,11 @@ import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import com.codahale.metrics.Gauge; import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import lombok.extern.slf4j.Slf4j;
/** /**
* Wraps a {@link ThreadPoolExecutor} instance. Blocks when queue is full instead of rejecting the task. Allow priority queueing by using * Wraps a {@link ThreadPoolExecutor} instance. Blocks when queue is full instead of rejecting the task. Allow priority queueing by using
* {@link Task} instead of {@link Runnable} * {@link Task} instead of {@link Runnable}
@@ -43,7 +43,7 @@ public class FeedRefreshExecutor {
if (t != null) { if (t != null) {
log.error("thread from pool {} threw a runtime exception", poolName, t); log.error("thread from pool {} threw a runtime exception", poolName, t);
} }
}; }
}; };
pool.setRejectedExecutionHandler(new RejectedExecutionHandler() { pool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override @Override

View File

@@ -1,7 +1,5 @@
package com.commafeed.backend.feed; package com.commafeed.backend.feed;
import io.dropwizard.lifecycle.Managed;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
@@ -14,8 +12,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -39,6 +35,9 @@ import com.commafeed.backend.service.FeedUpdateService;
import com.commafeed.backend.service.PubSubService; import com.commafeed.backend.service.PubSubService;
import com.google.common.util.concurrent.Striped; import com.google.common.util.concurrent.Striped;
import io.dropwizard.lifecycle.Managed;
import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@Singleton @Singleton
public class FeedRefreshUpdater implements Managed { public class FeedRefreshUpdater implements Managed {
@@ -121,7 +120,7 @@ public class FeedRefreshUpdater implements Managed {
if (!lastEntries.contains(cacheKey)) { if (!lastEntries.contains(cacheKey)) {
log.debug("cache miss for {}", entry.getUrl()); log.debug("cache miss for {}", entry.getUrl());
if (subscriptions == null) { if (subscriptions == null) {
subscriptions = UnitOfWork.run(sessionFactory, () -> feedSubscriptionDAO.findByFeed(feed)); subscriptions = UnitOfWork.call(sessionFactory, () -> feedSubscriptionDAO.findByFeed(feed));
} }
ok &= addEntry(feed, entry, subscriptions); ok &= addEntry(feed, entry, subscriptions);
entryCacheMiss.mark(); entryCacheMiss.mark();
@@ -183,7 +182,7 @@ public class FeedRefreshUpdater implements Managed {
locked1 = lock1.tryLock(1, TimeUnit.MINUTES); locked1 = lock1.tryLock(1, TimeUnit.MINUTES);
locked2 = lock2.tryLock(1, TimeUnit.MINUTES); locked2 = lock2.tryLock(1, TimeUnit.MINUTES);
if (locked1 && locked2) { if (locked1 && locked2) {
boolean inserted = UnitOfWork.run(sessionFactory, () -> feedUpdateService.addEntry(feed, entry, subscriptions)); boolean inserted = UnitOfWork.call(sessionFactory, () -> feedUpdateService.addEntry(feed, entry, subscriptions));
if (inserted) { if (inserted) {
entryInserted.mark(); entryInserted.mark();
} }

View File

@@ -13,8 +13,6 @@ import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -40,6 +38,7 @@ import com.ibm.icu.text.CharsetMatch;
import com.steadystate.css.parser.CSSOMParser; import com.steadystate.css.parser.CSSOMParser;
import edu.uci.ics.crawler4j.url.URLCanonicalizer; import edu.uci.ics.crawler4j.url.URLCanonicalizer;
import lombok.extern.slf4j.Slf4j;
/** /**
* Utility methods related to feed handling * Utility methods related to feed handling
@@ -87,7 +86,7 @@ public class FeedUtils {
whitelist.addAttributes("th", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "scope", "width"); whitelist.addAttributes("th", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "scope", "width");
whitelist.addAttributes("ul", "type"); whitelist.addAttributes("ul", "type");
whitelist.addProtocols("a", "href", "ftp", "http", "https", "mailto"); whitelist.addProtocols("a", "href", "ftp", "http", "https", "magnet", "mailto");
whitelist.addProtocols("blockquote", "cite", "http", "https"); whitelist.addProtocols("blockquote", "cite", "http", "https");
whitelist.addProtocols("img", "src", "http", "https"); whitelist.addProtocols("img", "src", "http", "https");
whitelist.addProtocols("q", "cite", "http", "https"); whitelist.addProtocols("q", "cite", "http", "https");
@@ -438,10 +437,7 @@ public class FeedUtils {
return removeTrailingSlash(publicUrl) + "/rest/feed/favicon/" + subscription.getId(); return removeTrailingSlash(publicUrl) + "/rest/feed/favicon/" + subscription.getId();
} }
public static String proxyImages(String content, String publicUrl, boolean proxyImages) { public static String proxyImages(String content, String publicUrl) {
if (!proxyImages) {
return content;
}
if (StringUtils.isBlank(content)) { if (StringUtils.isBlank(content)) {
return content; return content;
} }
@@ -451,7 +447,7 @@ public class FeedUtils {
for (Element element : elements) { for (Element element : elements) {
String href = element.attr("src"); String href = element.attr("src");
if (href != null) { if (href != null) {
String proxy = removeTrailingSlash(publicUrl) + "/rest/server/proxy?u=" + imageProxyEncoder(href); String proxy = proxyImage(href, publicUrl);
element.attr("src", proxy); element.attr("src", proxy);
} }
} }
@@ -459,6 +455,13 @@ public class FeedUtils {
return doc.body().html(); return doc.body().html();
} }
public static String proxyImage(String url, String publicUrl) {
if (StringUtils.isBlank(url)) {
return url;
}
return removeTrailingSlash(publicUrl) + "/rest/server/proxy?u=" + imageProxyEncoder(url);
}
public static String rot13(String msg) { public static String rot13(String msg) {
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
@@ -491,8 +494,8 @@ public class FeedUtils {
Entry entry = it.next(); Entry entry = it.next();
boolean keep = true; boolean keep = true;
for (FeedEntryKeyword keyword : keywords) { for (FeedEntryKeyword keyword : keywords) {
String title = Jsoup.parse(entry.getTitle()).text(); String title = entry.getTitle() == null ? null : Jsoup.parse(entry.getTitle()).text();
String content = Jsoup.parse(entry.getContent()).text(); String content = entry.getContent() == null ? null : Jsoup.parse(entry.getContent()).text();
boolean condition = !StringUtils.containsIgnoreCase(content, keyword.getKeyword()) boolean condition = !StringUtils.containsIgnoreCase(content, keyword.getKeyword())
&& !StringUtils.containsIgnoreCase(title, keyword.getKeyword()); && !StringUtils.containsIgnoreCase(title, keyword.getKeyword());
if (keyword.getMode() == Mode.EXCLUDE) { if (keyword.getMode() == Mode.EXCLUDE) {

View File

@@ -29,7 +29,7 @@ public class OPML11Parser extends OPML10Parser {
return false; return false;
}; }
@Override @Override
public WireFeed parse(Document document, boolean validate, Locale locale) throws IllegalArgumentException, FeedException { public WireFeed parse(Document document, boolean validate, Locale locale) throws IllegalArgumentException, FeedException {

View File

@@ -6,9 +6,6 @@ import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import com.commafeed.backend.dao.FeedDAO; import com.commafeed.backend.dao.FeedDAO;
@@ -19,12 +16,15 @@ import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.UnitOfWork; import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/** /**
* Contains utility methods for cleaning the database * Contains utility methods for cleaning the database
* *
*/ */
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class DatabaseCleaningService { public class DatabaseCleaningService {
@@ -42,16 +42,16 @@ public class DatabaseCleaningService {
int deleted = 0; int deleted = 0;
long entriesTotal = 0; long entriesTotal = 0;
do { do {
List<Feed> feeds = UnitOfWork.run(sessionFactory, () -> feedDAO.findWithoutSubscriptions(1)); List<Feed> feeds = UnitOfWork.call(sessionFactory, () -> feedDAO.findWithoutSubscriptions(1));
for (Feed feed : feeds) { for (Feed feed : feeds) {
int entriesDeleted = 0; int entriesDeleted = 0;
do { do {
entriesDeleted = UnitOfWork.run(sessionFactory, () -> feedEntryDAO.delete(feed.getId(), BATCH_SIZE)); entriesDeleted = UnitOfWork.call(sessionFactory, () -> feedEntryDAO.delete(feed.getId(), BATCH_SIZE));
entriesTotal += entriesDeleted; entriesTotal += entriesDeleted;
log.info("removed {} entries for feeds without subscriptions", entriesTotal); log.info("removed {} entries for feeds without subscriptions", entriesTotal);
} while (entriesDeleted > 0); } while (entriesDeleted > 0);
} }
deleted = UnitOfWork.run(sessionFactory, () -> feedDAO.delete(feeds)); deleted = UnitOfWork.call(sessionFactory, () -> feedDAO.delete(feeds));
total += deleted; total += deleted;
log.info("removed {} feeds without subscriptions", total); log.info("removed {} feeds without subscriptions", total);
} while (deleted != 0); } while (deleted != 0);
@@ -64,7 +64,7 @@ public class DatabaseCleaningService {
long total = 0; long total = 0;
int deleted = 0; int deleted = 0;
do { do {
deleted = UnitOfWork.run(sessionFactory, () -> feedEntryContentDAO.deleteWithoutEntries(BATCH_SIZE)); deleted = UnitOfWork.call(sessionFactory, () -> feedEntryContentDAO.deleteWithoutEntries(BATCH_SIZE));
total += deleted; total += deleted;
log.info("removed {} contents without entries", total); log.info("removed {} contents without entries", total);
} while (deleted != 0); } while (deleted != 0);
@@ -75,7 +75,7 @@ public class DatabaseCleaningService {
public long cleanEntriesForFeedsExceedingCapacity(final int maxFeedCapacity) { public long cleanEntriesForFeedsExceedingCapacity(final int maxFeedCapacity) {
long total = 0; long total = 0;
while (true) { while (true) {
List<FeedCapacity> feeds = UnitOfWork.run(sessionFactory, List<FeedCapacity> feeds = UnitOfWork.call(sessionFactory,
() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, BATCH_SIZE)); () -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, BATCH_SIZE));
if (feeds.isEmpty()) { if (feeds.isEmpty()) {
break; break;
@@ -85,7 +85,7 @@ public class DatabaseCleaningService {
long remaining = feed.getCapacity() - maxFeedCapacity; long remaining = feed.getCapacity() - maxFeedCapacity;
do { do {
final long rem = remaining; final long rem = remaining;
int deleted = UnitOfWork.run(sessionFactory, int deleted = UnitOfWork.call(sessionFactory,
() -> feedEntryDAO.deleteOldEntries(feed.getId(), Math.min(BATCH_SIZE, rem))); () -> feedEntryDAO.deleteOldEntries(feed.getId(), Math.min(BATCH_SIZE, rem)));
total += deleted; total += deleted;
remaining -= deleted; remaining -= deleted;
@@ -102,7 +102,7 @@ public class DatabaseCleaningService {
long total = 0; long total = 0;
int deleted = 0; int deleted = 0;
do { do {
deleted = UnitOfWork.run(sessionFactory, deleted = UnitOfWork.call(sessionFactory,
() -> feedEntryStatusDAO.delete(feedEntryStatusDAO.getOldStatuses(olderThan, BATCH_SIZE))); () -> feedEntryStatusDAO.delete(feedEntryStatusDAO.getOldStatuses(olderThan, BATCH_SIZE)));
total += deleted; total += deleted;
log.info("removed {} old read statuses", total); log.info("removed {} old read statuses", total);

View File

@@ -1,7 +1,5 @@
package com.commafeed.backend.service; package com.commafeed.backend.service;
import io.dropwizard.lifecycle.Managed;
import java.sql.Connection; import java.sql.Connection;
import java.util.Arrays; import java.util.Arrays;
@@ -9,17 +7,6 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.sql.DataSource; import javax.sql.DataSource;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.core.PostgresDatabase;
import liquibase.database.jvm.JdbcConnection;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.structure.DatabaseObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
@@ -31,8 +18,20 @@ import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.dao.UserDAO; import com.commafeed.backend.dao.UserDAO;
import com.commafeed.backend.model.UserRole.Role; import com.commafeed.backend.model.UserRole.Role;
import io.dropwizard.lifecycle.Managed;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.core.PostgresDatabase;
import liquibase.database.jvm.JdbcConnection;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.structure.DatabaseObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class StartupService implements Managed { public class StartupService implements Managed {
@@ -44,7 +43,7 @@ public class StartupService implements Managed {
@Override @Override
public void start() throws Exception { public void start() throws Exception {
updateSchema(); updateSchema();
long count = UnitOfWork.run(sessionFactory, () -> userDAO.count()); long count = UnitOfWork.call(sessionFactory, () -> userDAO.count());
if (count == 0) { if (count == 0) {
UnitOfWork.run(sessionFactory, () -> initialData()); UnitOfWork.run(sessionFactory, () -> initialData());
} }

View File

@@ -4,11 +4,10 @@ import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Entry details") @ApiModel("Entry details")
@Data @Data

View File

@@ -4,11 +4,10 @@ import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("List of entries with some metadata") @ApiModel("List of entries with some metadata")
@Data @Data

View File

@@ -6,7 +6,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.Data; import org.apache.commons.lang3.StringUtils;
import com.commafeed.backend.feed.FeedUtils; import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
@@ -19,8 +19,10 @@ import com.rometools.rome.feed.synd.SyndEnclosure;
import com.rometools.rome.feed.synd.SyndEnclosureImpl; import com.rometools.rome.feed.synd.SyndEnclosureImpl;
import com.rometools.rome.feed.synd.SyndEntry; import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndEntryImpl; import com.rometools.rome.feed.synd.SyndEntryImpl;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Entry details") @ApiModel("Entry details")
@@ -52,10 +54,11 @@ public class Entry implements Serializable {
if (content != null) { if (content != null) {
entry.setRtl(FeedUtils.isRTL(feedEntry)); entry.setRtl(FeedUtils.isRTL(feedEntry));
entry.setTitle(content.getTitle()); entry.setTitle(content.getTitle());
entry.setContent(FeedUtils.proxyImages(content.getContent(), publicUrl, proxyImages)); entry.setContent(proxyImages ? FeedUtils.proxyImages(content.getContent(), publicUrl) : content.getContent());
entry.setAuthor(content.getAuthor()); entry.setAuthor(content.getAuthor());
entry.setEnclosureUrl(content.getEnclosureUrl());
entry.setEnclosureType(content.getEnclosureType()); entry.setEnclosureType(content.getEnclosureType());
entry.setEnclosureUrl(proxyImages && StringUtils.contains(content.getEnclosureType(), "image")
? FeedUtils.proxyImage(content.getEnclosureUrl(), publicUrl) : content.getEnclosureUrl());
entry.setCategories(content.getCategories()); entry.setCategories(content.getCategories());
} }

View File

@@ -2,10 +2,9 @@ package com.commafeed.frontend.model;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Feed details") @ApiModel("Feed details")
@Data @Data

View File

@@ -2,10 +2,9 @@ package com.commafeed.frontend.model;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Server infos") @ApiModel("Server infos")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("User settings") @ApiModel("User settings")
@Data @Data

View File

@@ -3,14 +3,14 @@ package com.commafeed.frontend.model;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
import lombok.Data;
import com.commafeed.backend.feed.FeedUtils; import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.FeedSubscription;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("User information") @ApiModel("User information")

View File

@@ -3,10 +3,9 @@ package com.commafeed.frontend.model;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
import io.swagger.annotations.ApiModel;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Unread count") @ApiModel("Unread count")
@Data @Data

View File

@@ -3,11 +3,10 @@ package com.commafeed.frontend.model;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("User information") @ApiModel("User information")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Add Category Request") @ApiModel("Add Category Request")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Category modification request") @ApiModel("Category modification request")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Mark Request") @ApiModel("Mark Request")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Feed information request") @ApiModel("Feed information request")
@Data @Data

View File

@@ -3,11 +3,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Feed merge Request") @ApiModel("Feed merge Request")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Feed modification request") @ApiModel("Feed modification request")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel @ApiModel
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@Data @Data
@ApiModel @ApiModel

View File

@@ -3,11 +3,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Mark Request") @ApiModel("Mark Request")
@Data @Data

View File

@@ -3,11 +3,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Multiple Mark Request") @ApiModel("Multiple Mark Request")
@Data @Data

View File

@@ -2,13 +2,12 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import lombok.Data;
import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import com.wordnik.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Profile modification request") @ApiModel("Profile modification request")
@Data @Data

View File

@@ -2,14 +2,13 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import lombok.Data;
import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import com.wordnik.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Star Request") @ApiModel("Star Request")
@Data @Data

View File

@@ -2,11 +2,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Subscription request") @ApiModel("Subscription request")
@Data @Data

View File

@@ -3,11 +3,10 @@ package com.commafeed.frontend.model.request;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Tag Request") @ApiModel("Tag Request")
@Data @Data

View File

@@ -1,7 +1,5 @@
package com.commafeed.frontend.resource; package com.commafeed.frontend.resource;
import io.dropwizard.hibernate.UnitOfWork;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -18,11 +16,10 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.annotation.Timed;
import com.commafeed.CommaFeedApplication; import com.commafeed.CommaFeedApplication;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedConfiguration.ApplicationSettings; import com.commafeed.CommaFeedConfiguration.ApplicationSettings;
@@ -38,15 +35,18 @@ import com.commafeed.frontend.model.UserModel;
import com.commafeed.frontend.model.request.IDRequest; import com.commafeed.frontend.model.request.IDRequest;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation; import io.dropwizard.hibernate.UnitOfWork;
import com.wordnik.swagger.annotations.ApiParam; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
@Path("/admin") @Path("/admin")
@Api(value = "/admin", description = "Operations about application administration") @Api(value = "/admin")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class AdminREST { public class AdminREST {
@@ -61,6 +61,7 @@ public class AdminREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Save or update a user", notes = "Save or update a user. If the id is not specified, a new user will be created") @ApiOperation(value = "Save or update a user", notes = "Save or update a user. If the id is not specified, a new user will be created")
@Timed
public Response save(@SecurityCheck(Role.ADMIN) User user, @ApiParam(required = true) UserModel userModel) { public Response save(@SecurityCheck(Role.ADMIN) User user, @ApiParam(required = true) UserModel userModel) {
Preconditions.checkNotNull(userModel); Preconditions.checkNotNull(userModel);
Preconditions.checkNotNull(userModel.getName()); Preconditions.checkNotNull(userModel.getName());
@@ -115,6 +116,7 @@ public class AdminREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get user information", notes = "Get user information", response = UserModel.class) @ApiOperation(value = "Get user information", notes = "Get user information", response = UserModel.class)
@Timed
public Response getUser(@SecurityCheck(Role.ADMIN) User user, @ApiParam(value = "user id", required = true) @PathParam("id") Long id) { public Response getUser(@SecurityCheck(Role.ADMIN) User user, @ApiParam(value = "user id", required = true) @PathParam("id") Long id) {
Preconditions.checkNotNull(id); Preconditions.checkNotNull(id);
User u = userDAO.findById(id); User u = userDAO.findById(id);
@@ -131,6 +133,7 @@ public class AdminREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get all users", notes = "Get all users", response = UserModel.class, responseContainer = "List") @ApiOperation(value = "Get all users", notes = "Get all users", response = UserModel.class, responseContainer = "List")
@Timed
public Response getUsers(@SecurityCheck(Role.ADMIN) User user) { public Response getUsers(@SecurityCheck(Role.ADMIN) User user) {
Map<Long, UserModel> users = new HashMap<>(); Map<Long, UserModel> users = new HashMap<>();
for (UserRole role : userRoleDAO.findAll()) { for (UserRole role : userRoleDAO.findAll()) {
@@ -158,6 +161,7 @@ public class AdminREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Delete a user", notes = "Delete a user, and all his subscriptions") @ApiOperation(value = "Delete a user", notes = "Delete a user, and all his subscriptions")
@Timed
public Response delete(@SecurityCheck(Role.ADMIN) User user, @ApiParam(required = true) IDRequest req) { public Response delete(@SecurityCheck(Role.ADMIN) User user, @ApiParam(required = true) IDRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -177,6 +181,7 @@ public class AdminREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Retrieve application settings", notes = "Retrieve application settings", response = ApplicationSettings.class) @ApiOperation(value = "Retrieve application settings", notes = "Retrieve application settings", response = ApplicationSettings.class)
@Timed
public Response getSettings(@SecurityCheck(Role.ADMIN) User user) { public Response getSettings(@SecurityCheck(Role.ADMIN) User user) {
return Response.ok(config.getApplicationSettings()).build(); return Response.ok(config.getApplicationSettings()).build();
} }
@@ -185,6 +190,7 @@ public class AdminREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Retrieve server metrics") @ApiOperation(value = "Retrieve server metrics")
@Timed
public Response getMetrics(@SecurityCheck(Role.ADMIN) User user) { public Response getMetrics(@SecurityCheck(Role.ADMIN) User user) {
return Response.ok(metrics).build(); return Response.ok(metrics).build();
} }

View File

@@ -1,7 +1,5 @@
package com.commafeed.frontend.resource; package com.commafeed.frontend.resource;
import io.dropwizard.hibernate.UnitOfWork;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -27,13 +25,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.codahale.metrics.annotation.Timed;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.cache.CacheService; import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedCategoryDAO; import com.commafeed.backend.dao.FeedCategoryDAO;
@@ -65,16 +61,20 @@ import com.google.common.collect.Lists;
import com.rometools.rome.feed.synd.SyndFeed; import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl; import com.rometools.rome.feed.synd.SyndFeedImpl;
import com.rometools.rome.io.SyndFeedOutput; import com.rometools.rome.io.SyndFeedOutput;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation; import io.dropwizard.hibernate.UnitOfWork;
import com.wordnik.swagger.annotations.ApiParam; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Path("/category") @Path("/category")
@Api(value = "/category", description = "Operations about user categories") @Api(value = "/category")
@Slf4j @Slf4j
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class CategoryREST { public class CategoryREST {
@@ -93,10 +93,13 @@ public class CategoryREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get category entries", notes = "Get a list of category entries", response = Entries.class) @ApiOperation(value = "Get category entries", notes = "Get a list of category entries", response = Entries.class)
public Response getCategoryEntries( @Timed
@SecurityCheck User user, public Response getCategoryEntries(@SecurityCheck User user,
@ApiParam(value = "id of the category, 'all' or 'starred'", required = true) @QueryParam("id") String id, @ApiParam(value = "id of the category, 'all' or 'starred'", required = true) @QueryParam("id") String id,
@ApiParam(value = "all entries or only unread ones", allowableValues = "all,unread", required = true) @DefaultValue("unread") @QueryParam("readType") ReadingMode readType, @ApiParam(
value = "all entries or only unread ones",
allowableValues = "all,unread",
required = true) @DefaultValue("unread") @QueryParam("readType") ReadingMode readType,
@ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan, @ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan,
@ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset, @ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset,
@ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit, @ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit,
@@ -104,7 +107,8 @@ public class CategoryREST {
@ApiParam( @ApiParam(
value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords, value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords,
@ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds, @ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds,
@ApiParam(value = "comma-separated list of excluded subscription ids") @QueryParam("excludedSubscriptionIds") String excludedSubscriptionIds, @ApiParam(
value = "comma-separated list of excluded subscription ids") @QueryParam("excludedSubscriptionIds") String excludedSubscriptionIds,
@ApiParam(value = "keep only entries tagged with this tag") @QueryParam("tag") String tag) { @ApiParam(value = "keep only entries tagged with this tag") @QueryParam("tag") String tag) {
Preconditions.checkNotNull(readType); Preconditions.checkNotNull(readType);
@@ -139,18 +143,16 @@ public class CategoryREST {
offset, limit + 1, order, true, onlyIds, tag); offset, limit + 1, order, true, onlyIds, tag);
for (FeedEntryStatus status : list) { for (FeedEntryStatus status : list) {
entries.getEntries().add( entries.getEntries().add(Entry.build(status, config.getApplicationSettings().getPublicUrl(),
Entry.build(status, config.getApplicationSettings().getPublicUrl(), config.getApplicationSettings() config.getApplicationSettings().getImageProxyEnabled()));
.getImageProxyEnabled()));
} }
} else if (STARRED.equals(id)) { } else if (STARRED.equals(id)) {
entries.setName("Starred"); entries.setName("Starred");
List<FeedEntryStatus> starred = feedEntryStatusDAO.findStarred(user, newerThanDate, offset, limit + 1, order, !onlyIds); List<FeedEntryStatus> starred = feedEntryStatusDAO.findStarred(user, newerThanDate, offset, limit + 1, order, !onlyIds);
for (FeedEntryStatus status : starred) { for (FeedEntryStatus status : starred) {
entries.getEntries().add( entries.getEntries().add(Entry.build(status, config.getApplicationSettings().getPublicUrl(),
Entry.build(status, config.getApplicationSettings().getPublicUrl(), config.getApplicationSettings() config.getApplicationSettings().getImageProxyEnabled()));
.getImageProxyEnabled()));
} }
} else { } else {
FeedCategory parent = feedCategoryDAO.findById(user, Long.valueOf(id)); FeedCategory parent = feedCategoryDAO.findById(user, Long.valueOf(id));
@@ -162,9 +164,8 @@ public class CategoryREST {
offset, limit + 1, order, true, onlyIds, tag); offset, limit + 1, order, true, onlyIds, tag);
for (FeedEntryStatus status : list) { for (FeedEntryStatus status : list) {
entries.getEntries().add( entries.getEntries().add(Entry.build(status, config.getApplicationSettings().getPublicUrl(),
Entry.build(status, config.getApplicationSettings().getPublicUrl(), config.getApplicationSettings() config.getApplicationSettings().getImageProxyEnabled()));
.getImageProxyEnabled()));
} }
entries.setName(parent.getName()); entries.setName(parent.getName());
} else { } else {
@@ -189,10 +190,13 @@ public class CategoryREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get category entries as feed", notes = "Get a feed of category entries") @ApiOperation(value = "Get category entries as feed", notes = "Get a feed of category entries")
@Produces(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML)
public Response getCategoryEntriesAsFeed( @Timed
@SecurityCheck(apiKeyAllowed = true) User user, public Response getCategoryEntriesAsFeed(@SecurityCheck(apiKeyAllowed = true) User user,
@ApiParam(value = "id of the category, 'all' or 'starred'", required = true) @QueryParam("id") String id, @ApiParam(value = "id of the category, 'all' or 'starred'", required = true) @QueryParam("id") String id,
@ApiParam(value = "all entries or only unread ones", allowableValues = "all,unread", required = true) @DefaultValue("all") @QueryParam("readType") ReadingMode readType, @ApiParam(
value = "all entries or only unread ones",
allowableValues = "all,unread",
required = true) @DefaultValue("all") @QueryParam("readType") ReadingMode readType,
@ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan, @ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan,
@ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset, @ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset,
@ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit, @ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit,
@@ -200,7 +204,8 @@ public class CategoryREST {
@ApiParam( @ApiParam(
value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords, value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords,
@ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds, @ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds,
@ApiParam(value = "comma-separated list of excluded subscription ids") @QueryParam("excludedSubscriptionIds") String excludedSubscriptionIds, @ApiParam(
value = "comma-separated list of excluded subscription ids") @QueryParam("excludedSubscriptionIds") String excludedSubscriptionIds,
@ApiParam(value = "keep only entries tagged with this tag") @QueryParam("tag") String tag) { @ApiParam(value = "keep only entries tagged with this tag") @QueryParam("tag") String tag) {
Response response = getCategoryEntries(user, id, readType, newerThan, offset, limit, order, keywords, onlyIds, Response response = getCategoryEntries(user, id, readType, newerThan, offset, limit, order, keywords, onlyIds,
@@ -232,6 +237,7 @@ public class CategoryREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Mark category entries", notes = "Mark feed entries of this category as read") @ApiOperation(value = "Mark category entries", notes = "Mark feed entries of this category as read")
@Timed
public Response markCategoryEntries(@SecurityCheck User user, public Response markCategoryEntries(@SecurityCheck User user,
@ApiParam(value = "category id, or 'all'", required = true) MarkRequest req) { @ApiParam(value = "category id, or 'all'", required = true) MarkRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
@@ -273,6 +279,7 @@ public class CategoryREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Add a category", notes = "Add a new feed category", response = Long.class) @ApiOperation(value = "Add a category", notes = "Add a new feed category", response = Long.class)
@Timed
public Response addCategory(@SecurityCheck User user, @ApiParam(required = true) AddCategoryRequest req) { public Response addCategory(@SecurityCheck User user, @ApiParam(required = true) AddCategoryRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getName()); Preconditions.checkNotNull(req.getName());
@@ -296,6 +303,7 @@ public class CategoryREST {
@Path("/delete") @Path("/delete")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Delete a category", notes = "Delete an existing feed category") @ApiOperation(value = "Delete a category", notes = "Delete an existing feed category")
@Timed
public Response deleteCategory(@SecurityCheck User user, @ApiParam(required = true) IDRequest req) { public Response deleteCategory(@SecurityCheck User user, @ApiParam(required = true) IDRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
@@ -328,6 +336,7 @@ public class CategoryREST {
@Path("/modify") @Path("/modify")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Rename a category", notes = "Rename an existing feed category") @ApiOperation(value = "Rename a category", notes = "Rename an existing feed category")
@Timed
public Response modifyCategory(@SecurityCheck User user, @ApiParam(required = true) CategoryModificationRequest req) { public Response modifyCategory(@SecurityCheck User user, @ApiParam(required = true) CategoryModificationRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -382,6 +391,7 @@ public class CategoryREST {
@Path("/collapse") @Path("/collapse")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Collapse a category", notes = "Save collapsed or expanded status for a category") @ApiOperation(value = "Collapse a category", notes = "Save collapsed or expanded status for a category")
@Timed
public Response collapse(@SecurityCheck User user, @ApiParam(required = true) CollapseRequest req) { public Response collapse(@SecurityCheck User user, @ApiParam(required = true) CollapseRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -400,6 +410,7 @@ public class CategoryREST {
@Path("/unreadCount") @Path("/unreadCount")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get unread count for feed subscriptions", response = UnreadCount.class, responseContainer = "List") @ApiOperation(value = "Get unread count for feed subscriptions", response = UnreadCount.class, responseContainer = "List")
@Timed
public Response getUnreadCount(@SecurityCheck User user) { public Response getUnreadCount(@SecurityCheck User user) {
Map<Long, UnreadCount> unreadCount = feedSubscriptionService.getUnreadCount(user); Map<Long, UnreadCount> unreadCount = feedSubscriptionService.getUnreadCount(user);
return Response.ok(Lists.newArrayList(unreadCount.values())).build(); return Response.ok(Lists.newArrayList(unreadCount.values())).build();
@@ -409,6 +420,7 @@ public class CategoryREST {
@Path("/get") @Path("/get")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get feed categories", notes = "Get all categories and subscriptions of the user", response = Category.class) @ApiOperation(value = "Get feed categories", notes = "Get all categories and subscriptions of the user", response = Category.class)
@Timed
public Response getSubscriptions(@SecurityCheck User user) { public Response getSubscriptions(@SecurityCheck User user) {
Category root = cache.getUserRootCategory(user); Category root = cache.getUserRootCategory(user);
if (root == null) { if (root == null) {

View File

@@ -1,7 +1,5 @@
package com.commafeed.frontend.resource; package com.commafeed.frontend.resource;
import io.dropwizard.hibernate.UnitOfWork;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
@@ -14,8 +12,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import lombok.RequiredArgsConstructor; import com.codahale.metrics.annotation.Timed;
import com.commafeed.backend.dao.FeedEntryTagDAO; import com.commafeed.backend.dao.FeedEntryTagDAO;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.commafeed.backend.service.FeedEntryService; import com.commafeed.backend.service.FeedEntryService;
@@ -26,15 +23,18 @@ import com.commafeed.frontend.model.request.MultipleMarkRequest;
import com.commafeed.frontend.model.request.StarRequest; import com.commafeed.frontend.model.request.StarRequest;
import com.commafeed.frontend.model.request.TagRequest; import com.commafeed.frontend.model.request.TagRequest;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation; import io.dropwizard.hibernate.UnitOfWork;
import com.wordnik.swagger.annotations.ApiParam; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
@Path("/entry") @Path("/entry")
@Api(value = "/entry", description = "Operations about feed entries") @Api(value = "/entry")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class EntryREST { public class EntryREST {
@@ -46,6 +46,7 @@ public class EntryREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Mark a feed entry", notes = "Mark a feed entry as read/unread") @ApiOperation(value = "Mark a feed entry", notes = "Mark a feed entry as read/unread")
@Timed
public Response markFeedEntry(@SecurityCheck User user, @ApiParam(value = "Mark Request", required = true) MarkRequest req) { public Response markFeedEntry(@SecurityCheck User user, @ApiParam(value = "Mark Request", required = true) MarkRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -58,6 +59,7 @@ public class EntryREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Mark multiple feed entries", notes = "Mark feed entries as read/unread") @ApiOperation(value = "Mark multiple feed entries", notes = "Mark feed entries as read/unread")
@Timed
public Response markFeedEntries(@SecurityCheck User user, public Response markFeedEntries(@SecurityCheck User user,
@ApiParam(value = "Multiple Mark Request", required = true) MultipleMarkRequest req) { @ApiParam(value = "Multiple Mark Request", required = true) MultipleMarkRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
@@ -74,6 +76,7 @@ public class EntryREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Mark a feed entry", notes = "Mark a feed entry as read/unread") @ApiOperation(value = "Mark a feed entry", notes = "Mark a feed entry as read/unread")
@Timed
public Response starFeedEntry(@SecurityCheck User user, @ApiParam(value = "Star Request", required = true) StarRequest req) { public Response starFeedEntry(@SecurityCheck User user, @ApiParam(value = "Star Request", required = true) StarRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -88,6 +91,7 @@ public class EntryREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get list of tags for the user", notes = "Get list of tags for the user") @ApiOperation(value = "Get list of tags for the user", notes = "Get list of tags for the user")
@Timed
public Response getTags(@SecurityCheck User user) { public Response getTags(@SecurityCheck User user) {
List<String> tags = feedEntryTagDAO.findByUser(user); List<String> tags = feedEntryTagDAO.findByUser(user);
return Response.ok(tags).build(); return Response.ok(tags).build();
@@ -97,6 +101,7 @@ public class EntryREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Mark a feed entry", notes = "Mark a feed entry as read/unread") @ApiOperation(value = "Mark a feed entry", notes = "Mark a feed entry as read/unread")
@Timed
public Response tagFeedEntry(@SecurityCheck User user, @ApiParam(value = "Tag Request", required = true) TagRequest req) { public Response tagFeedEntry(@SecurityCheck User user, @ApiParam(value = "Tag Request", required = true) TagRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getEntryId()); Preconditions.checkNotNull(req.getEntryId());

View File

@@ -1,7 +1,5 @@
package com.commafeed.frontend.resource; package com.commafeed.frontend.resource;
import io.dropwizard.hibernate.UnitOfWork;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringWriter; import java.io.StringWriter;
import java.net.URI; import java.net.URI;
@@ -32,14 +30,12 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import com.codahale.metrics.annotation.Timed;
import com.commafeed.CommaFeedApplication; import com.commafeed.CommaFeedApplication;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.cache.CacheService; import com.commafeed.backend.cache.CacheService;
@@ -86,16 +82,20 @@ import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl; import com.rometools.rome.feed.synd.SyndFeedImpl;
import com.rometools.rome.io.SyndFeedOutput; import com.rometools.rome.io.SyndFeedOutput;
import com.rometools.rome.io.WireFeedOutput; import com.rometools.rome.io.WireFeedOutput;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation; import io.dropwizard.hibernate.UnitOfWork;
import com.wordnik.swagger.annotations.ApiParam; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Path("/feed") @Path("/feed")
@Api(value = "/feed", description = "Operations about feeds") @Api(value = "/feed")
@Slf4j @Slf4j
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class FeedREST { public class FeedREST {
@@ -131,10 +131,13 @@ public class FeedREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get feed entries", notes = "Get a list of feed entries", response = Entries.class) @ApiOperation(value = "Get feed entries", notes = "Get a list of feed entries", response = Entries.class)
public Response getFeedEntries( @Timed
@SecurityCheck User user, public Response getFeedEntries(@SecurityCheck User user,
@ApiParam(value = "id of the feed", required = true) @QueryParam("id") String id, @ApiParam(value = "id of the feed", required = true) @QueryParam("id") String id,
@ApiParam(value = "all entries or only unread ones", allowableValues = "all,unread", required = true) @DefaultValue("unread") @QueryParam("readType") ReadingMode readType, @ApiParam(
value = "all entries or only unread ones",
allowableValues = "all,unread",
required = true) @DefaultValue("unread") @QueryParam("readType") ReadingMode readType,
@ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan, @ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan,
@ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset, @ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset,
@ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit, @ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit,
@@ -172,9 +175,8 @@ public class FeedREST {
entryKeywords, newerThanDate, offset, limit + 1, order, true, onlyIds, null); entryKeywords, newerThanDate, offset, limit + 1, order, true, onlyIds, null);
for (FeedEntryStatus status : list) { for (FeedEntryStatus status : list) {
entries.getEntries().add( entries.getEntries().add(Entry.build(status, config.getApplicationSettings().getPublicUrl(),
Entry.build(status, config.getApplicationSettings().getPublicUrl(), config.getApplicationSettings() config.getApplicationSettings().getImageProxyEnabled()));
.getImageProxyEnabled()));
} }
boolean hasMore = entries.getEntries().size() > limit; boolean hasMore = entries.getEntries().size() > limit;
@@ -197,10 +199,13 @@ public class FeedREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get feed entries as a feed", notes = "Get a feed of feed entries") @ApiOperation(value = "Get feed entries as a feed", notes = "Get a feed of feed entries")
@Produces(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML)
public Response getFeedEntriesAsFeed( @Timed
@SecurityCheck(apiKeyAllowed = true) User user, public Response getFeedEntriesAsFeed(@SecurityCheck(apiKeyAllowed = true) User user,
@ApiParam(value = "id of the feed", required = true) @QueryParam("id") String id, @ApiParam(value = "id of the feed", required = true) @QueryParam("id") String id,
@ApiParam(value = "all entries or only unread ones", allowableValues = "all,unread", required = true) @DefaultValue("all") @QueryParam("readType") ReadingMode readType, @ApiParam(
value = "all entries or only unread ones",
allowableValues = "all,unread",
required = true) @DefaultValue("all") @QueryParam("readType") ReadingMode readType,
@ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan, @ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan,
@ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset, @ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset,
@ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit, @ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit,
@@ -254,6 +259,7 @@ public class FeedREST {
@Path("/fetch") @Path("/fetch")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Fetch a feed", notes = "Fetch a feed by its url", response = FeedInfo.class) @ApiOperation(value = "Fetch a feed", notes = "Fetch a feed by its url", response = FeedInfo.class)
@Timed
public Response fetchFeed(@SecurityCheck User user, @ApiParam(value = "feed url", required = true) FeedInfoRequest req) { public Response fetchFeed(@SecurityCheck User user, @ApiParam(value = "feed url", required = true) FeedInfoRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getUrl()); Preconditions.checkNotNull(req.getUrl());
@@ -272,6 +278,7 @@ public class FeedREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Queue all feeds of the user for refresh", notes = "Manually add all feeds of the user to the refresh queue") @ApiOperation(value = "Queue all feeds of the user for refresh", notes = "Manually add all feeds of the user to the refresh queue")
@Timed
public Response queueAllForRefresh(@SecurityCheck User user) { public Response queueAllForRefresh(@SecurityCheck User user) {
feedSubscriptionService.refreshAll(user); feedSubscriptionService.refreshAll(user);
return Response.ok().build(); return Response.ok().build();
@@ -281,6 +288,7 @@ public class FeedREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Queue a feed for refresh", notes = "Manually add a feed to the refresh queue") @ApiOperation(value = "Queue a feed for refresh", notes = "Manually add a feed to the refresh queue")
@Timed
public Response queueForRefresh(@SecurityCheck User user, @ApiParam(value = "Feed id") IDRequest req) { public Response queueForRefresh(@SecurityCheck User user, @ApiParam(value = "Feed id") IDRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
@@ -299,6 +307,7 @@ public class FeedREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Mark feed entries", notes = "Mark feed entries as read (unread is not supported)") @ApiOperation(value = "Mark feed entries", notes = "Mark feed entries as read (unread is not supported)")
@Timed
public Response markFeedEntries(@SecurityCheck User user, @ApiParam(value = "Mark request") MarkRequest req) { public Response markFeedEntries(@SecurityCheck User user, @ApiParam(value = "Mark request") MarkRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -318,6 +327,7 @@ public class FeedREST {
@Path("/get/{id}") @Path("/get/{id}")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "", notes = "") @ApiOperation(value = "", notes = "")
@Timed
public Response get(@SecurityCheck User user, @ApiParam(value = "user id", required = true) @PathParam("id") Long id) { public Response get(@SecurityCheck User user, @ApiParam(value = "user id", required = true) @PathParam("id") Long id) {
Preconditions.checkNotNull(id); Preconditions.checkNotNull(id);
@@ -333,6 +343,7 @@ public class FeedREST {
@Path("/favicon/{id}") @Path("/favicon/{id}")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Fetch a feed's icon", notes = "Fetch a feed's icon") @ApiOperation(value = "Fetch a feed's icon", notes = "Fetch a feed's icon")
@Timed
public Response getFavicon(@SecurityCheck User user, @ApiParam(value = "subscription id") @PathParam("id") Long id) { public Response getFavicon(@SecurityCheck User user, @ApiParam(value = "subscription id") @PathParam("id") Long id) {
Preconditions.checkNotNull(id); Preconditions.checkNotNull(id);
@@ -362,6 +373,7 @@ public class FeedREST {
@Path("/subscribe") @Path("/subscribe")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Subscribe to a feed", notes = "Subscribe to a feed") @ApiOperation(value = "Subscribe to a feed", notes = "Subscribe to a feed")
@Timed
public Response subscribe(@SecurityCheck User user, @ApiParam(value = "subscription request", required = true) SubscribeRequest req) { public Response subscribe(@SecurityCheck User user, @ApiParam(value = "subscription request", required = true) SubscribeRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getTitle()); Preconditions.checkNotNull(req.getTitle());
@@ -388,6 +400,7 @@ public class FeedREST {
@Path("/subscribe") @Path("/subscribe")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Subscribe to a feed", notes = "Subscribe to a feed") @ApiOperation(value = "Subscribe to a feed", notes = "Subscribe to a feed")
@Timed
public Response subscribe(@SecurityCheck User user, @ApiParam(value = "feed url", required = true) @QueryParam("url") String url) { public Response subscribe(@SecurityCheck User user, @ApiParam(value = "feed url", required = true) @QueryParam("url") String url) {
try { try {
@@ -415,6 +428,7 @@ public class FeedREST {
@Path("/unsubscribe") @Path("/unsubscribe")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Unsubscribe from a feed", notes = "Unsubscribe from a feed") @ApiOperation(value = "Unsubscribe from a feed", notes = "Unsubscribe from a feed")
@Timed
public Response unsubscribe(@SecurityCheck User user, @ApiParam(required = true) IDRequest req) { public Response unsubscribe(@SecurityCheck User user, @ApiParam(required = true) IDRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -431,6 +445,7 @@ public class FeedREST {
@Path("/modify") @Path("/modify")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Modify a subscription", notes = "Modify a feed subscription") @ApiOperation(value = "Modify a subscription", notes = "Modify a feed subscription")
@Timed
public Response modify(@SecurityCheck User user, @ApiParam(value = "subscription id", required = true) FeedModificationRequest req) { public Response modify(@SecurityCheck User user, @ApiParam(value = "subscription id", required = true) FeedModificationRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -442,7 +457,7 @@ public class FeedREST {
} }
FeedSubscription subscription = feedSubscriptionDAO.findById(user, req.getId()); FeedSubscription subscription = feedSubscriptionDAO.findById(user, req.getId());
subscription.setFilter(req.getFilter()); subscription.setFilter(StringUtils.lowerCase(req.getFilter()));
if (StringUtils.isNotBlank(req.getName())) { if (StringUtils.isNotBlank(req.getName())) {
subscription.setTitle(req.getName()); subscription.setTitle(req.getName());
@@ -490,12 +505,13 @@ public class FeedREST {
@UnitOfWork @UnitOfWork
@Consumes(MediaType.MULTIPART_FORM_DATA) @Consumes(MediaType.MULTIPART_FORM_DATA)
@ApiOperation(value = "OPML import", notes = "Import an OPML file, posted as a FORM with the 'file' name") @ApiOperation(value = "OPML import", notes = "Import an OPML file, posted as a FORM with the 'file' name")
@Timed
public Response importOpml(@SecurityCheck User user, @FormDataParam("file") InputStream input) { public Response importOpml(@SecurityCheck User user, @FormDataParam("file") InputStream input) {
String publicUrl = config.getApplicationSettings().getPublicUrl(); String publicUrl = config.getApplicationSettings().getPublicUrl();
if (StringUtils.isBlank(publicUrl)) { if (StringUtils.isBlank(publicUrl)) {
throw new WebApplicationException(Response.status(Status.INTERNAL_SERVER_ERROR) throw new WebApplicationException(
.entity("Set the public URL in the admin section.").build()); Response.status(Status.INTERNAL_SERVER_ERROR).entity("Set the public URL in the admin section.").build());
} }
if (CommaFeedApplication.USERNAME_DEMO.equals(user.getName())) { if (CommaFeedApplication.USERNAME_DEMO.equals(user.getName())) {
@@ -516,6 +532,7 @@ public class FeedREST {
@UnitOfWork @UnitOfWork
@Produces(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML)
@ApiOperation(value = "OPML export", notes = "Export an OPML file of the user's subscriptions") @ApiOperation(value = "OPML export", notes = "Export an OPML file of the user's subscriptions")
@Timed
public Response exportOpml(@SecurityCheck User user) { public Response exportOpml(@SecurityCheck User user) {
Opml opml = opmlExporter.export(user); Opml opml = opmlExporter.export(user);
WireFeedOutput output = new WireFeedOutput(); WireFeedOutput output = new WireFeedOutput();

View File

@@ -1,7 +1,5 @@
package com.commafeed.frontend.resource; package com.commafeed.frontend.resource;
import io.dropwizard.hibernate.UnitOfWork;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@@ -19,14 +17,12 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.annotation.Timed;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.dao.FeedDAO; import com.commafeed.backend.dao.FeedDAO;
import com.commafeed.backend.feed.FeedParser; import com.commafeed.backend.feed.FeedParser;
@@ -35,9 +31,13 @@ import com.commafeed.backend.feed.FetchedFeed;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import io.dropwizard.hibernate.UnitOfWork;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Path("/push") @Path("/push")
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class PubSubHubbubCallbackREST { public class PubSubHubbubCallbackREST {
@@ -54,6 +54,7 @@ public class PubSubHubbubCallbackREST {
@GET @GET
@UnitOfWork @UnitOfWork
@Produces(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN)
@Timed
public Response verify(@QueryParam("hub.mode") String mode, @QueryParam("hub.topic") String topic, public Response verify(@QueryParam("hub.mode") String mode, @QueryParam("hub.topic") String topic,
@QueryParam("hub.challenge") String challenge, @QueryParam("hub.lease_seconds") String leaseSeconds, @QueryParam("hub.challenge") String challenge, @QueryParam("hub.lease_seconds") String leaseSeconds,
@QueryParam("hub.verify_token") String verifyToken) { @QueryParam("hub.verify_token") String verifyToken) {
@@ -85,6 +86,7 @@ public class PubSubHubbubCallbackREST {
@POST @POST
@UnitOfWork @UnitOfWork
@Consumes({ MediaType.APPLICATION_ATOM_XML, "application/rss+xml" }) @Consumes({ MediaType.APPLICATION_ATOM_XML, "application/rss+xml" })
@Timed
public Response callback() { public Response callback() {
if (!config.getApplicationSettings().getPubsubhubbub()) { if (!config.getApplicationSettings().getPubsubhubbub()) {

View File

@@ -1,7 +1,5 @@
package com.commafeed.frontend.resource; package com.commafeed.frontend.resource;
import io.dropwizard.hibernate.UnitOfWork;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@@ -13,10 +11,9 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.codahale.metrics.annotation.Timed;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.HttpGetter; import com.commafeed.backend.HttpGetter;
import com.commafeed.backend.HttpGetter.HttpResult; import com.commafeed.backend.HttpGetter.HttpResult;
@@ -24,14 +21,17 @@ import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.commafeed.frontend.auth.SecurityCheck; import com.commafeed.frontend.auth.SecurityCheck;
import com.commafeed.frontend.model.ServerInfo; import com.commafeed.frontend.model.ServerInfo;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation; import io.dropwizard.hibernate.UnitOfWork;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@Path("/server") @Path("/server")
@Api(value = "/server", description = "Operations about server infos") @Api(value = "/server")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class ServerREST { public class ServerREST {
@@ -42,6 +42,7 @@ public class ServerREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get server infos", notes = "Get server infos", response = ServerInfo.class) @ApiOperation(value = "Get server infos", notes = "Get server infos", response = ServerInfo.class)
@Timed
public Response get() { public Response get() {
ServerInfo infos = new ServerInfo(); ServerInfo infos = new ServerInfo();
infos.setAnnouncement(config.getApplicationSettings().getAnnouncement()); infos.setAnnouncement(config.getApplicationSettings().getAnnouncement());
@@ -58,6 +59,7 @@ public class ServerREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "proxy image") @ApiOperation(value = "proxy image")
@Produces("image/png") @Produces("image/png")
@Timed
public Response get(@SecurityCheck User user, @QueryParam("u") String url) { public Response get(@SecurityCheck User user, @QueryParam("u") String url) {
if (!config.getApplicationSettings().getImageProxyEnabled()) { if (!config.getApplicationSettings().getImageProxyEnabled()) {
return Response.status(Status.FORBIDDEN).build(); return Response.status(Status.FORBIDDEN).build();

View File

@@ -1,17 +1,12 @@
package com.commafeed.frontend.resource; package com.commafeed.frontend.resource;
import io.dropwizard.hibernate.UnitOfWork;
import io.dropwizard.jersey.validation.ValidationErrorMessage;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.validation.ConstraintViolation;
import javax.validation.Valid; import javax.validation.Valid;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@@ -24,15 +19,13 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import com.codahale.metrics.annotation.Timed;
import com.commafeed.CommaFeedApplication; import com.commafeed.CommaFeedApplication;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.dao.UserDAO; import com.commafeed.backend.dao.UserDAO;
@@ -59,16 +52,21 @@ import com.commafeed.frontend.model.request.RegistrationRequest;
import com.commafeed.frontend.session.SessionHelper; import com.commafeed.frontend.session.SessionHelper;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation; import io.dropwizard.hibernate.UnitOfWork;
import com.wordnik.swagger.annotations.ApiParam; import io.dropwizard.jersey.validation.ValidationErrorMessage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Path("/user") @Path("/user")
@Api(value = "/user", description = "Operations about the user") @Api(value = "/user")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class UserREST { public class UserREST {
@@ -84,6 +82,7 @@ public class UserREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Retrieve user settings", notes = "Retrieve user settings", response = Settings.class) @ApiOperation(value = "Retrieve user settings", notes = "Retrieve user settings", response = Settings.class)
@Timed
public Response getSettings(@SecurityCheck User user) { public Response getSettings(@SecurityCheck User user) {
Settings s = new Settings(); Settings s = new Settings();
UserSettings settings = userSettingsDAO.findByUser(user); UserSettings settings = userSettingsDAO.findByUser(user);
@@ -138,6 +137,7 @@ public class UserREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Save user settings", notes = "Save user settings") @ApiOperation(value = "Save user settings", notes = "Save user settings")
@Timed
public Response saveSettings(@SecurityCheck User user, @ApiParam(required = true) Settings settings) { public Response saveSettings(@SecurityCheck User user, @ApiParam(required = true) Settings settings) {
Preconditions.checkNotNull(settings); Preconditions.checkNotNull(settings);
@@ -176,6 +176,7 @@ public class UserREST {
@GET @GET
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Retrieve user's profile", response = UserModel.class) @ApiOperation(value = "Retrieve user's profile", response = UserModel.class)
@Timed
public Response get(@SecurityCheck User user) { public Response get(@SecurityCheck User user) {
UserModel userModel = new UserModel(); UserModel userModel = new UserModel();
userModel.setId(user.getId()); userModel.setId(user.getId());
@@ -195,6 +196,7 @@ public class UserREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Save user's profile") @ApiOperation(value = "Save user's profile")
@Timed
public Response save(@SecurityCheck User user, @ApiParam(required = true) ProfileModificationRequest request) { public Response save(@SecurityCheck User user, @ApiParam(required = true) ProfileModificationRequest request) {
Preconditions.checkArgument(StringUtils.isBlank(request.getPassword()) || request.getPassword().length() >= 6); Preconditions.checkArgument(StringUtils.isBlank(request.getPassword()) || request.getPassword().length() >= 6);
if (StringUtils.isNotBlank(request.getEmail())) { if (StringUtils.isNotBlank(request.getEmail())) {
@@ -223,6 +225,7 @@ public class UserREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Register a new account") @ApiOperation(value = "Register a new account")
@Timed
public Response register(@Valid @ApiParam(required = true) RegistrationRequest req, @Context SessionHelper sessionHelper) { public Response register(@Valid @ApiParam(required = true) RegistrationRequest req, @Context SessionHelper sessionHelper) {
try { try {
User registeredUser = userService.register(req.getName(), req.getPassword(), req.getEmail(), Arrays.asList(Role.USER)); User registeredUser = userService.register(req.getName(), req.getPassword(), req.getEmail(), Arrays.asList(Role.USER));
@@ -230,12 +233,8 @@ public class UserREST {
sessionHelper.setLoggedInUser(registeredUser); sessionHelper.setLoggedInUser(registeredUser);
return Response.ok().build(); return Response.ok().build();
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
return Response.status(422).entity(new ValidationErrorMessage(Collections.<ConstraintViolation<?>> emptySet()) { return Response.status(422).entity(new ValidationErrorMessage(ImmutableList.of(e.getMessage()))).type(MediaType.TEXT_PLAIN)
@Override .build();
public ImmutableList<String> getErrors() {
return ImmutableList.of(e.getMessage());
}
}).type(MediaType.TEXT_PLAIN).build();
} }
} }
@@ -243,6 +242,7 @@ public class UserREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Login and create a session") @ApiOperation(value = "Login and create a session")
@Timed
public Response login(@ApiParam(required = true) LoginRequest req, @Context SessionHelper sessionHelper) { public Response login(@ApiParam(required = true) LoginRequest req, @Context SessionHelper sessionHelper) {
Optional<User> user = userService.login(req.getName(), req.getPassword()); Optional<User> user = userService.login(req.getName(), req.getPassword());
if (user.isPresent()) { if (user.isPresent()) {
@@ -257,6 +257,7 @@ public class UserREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "send a password reset email") @ApiOperation(value = "send a password reset email")
@Timed
public Response sendPasswordReset(@Valid PasswordResetRequest req) { public Response sendPasswordReset(@Valid PasswordResetRequest req) {
User user = userDAO.findByEmail(req.getEmail()); User user = userDAO.findByEmail(req.getEmail());
if (user == null) { if (user == null) {
@@ -278,9 +279,9 @@ public class UserREST {
private String buildEmailContent(User user) throws Exception { private String buildEmailContent(User user) throws Exception {
String publicUrl = FeedUtils.removeTrailingSlash(config.getApplicationSettings().getPublicUrl()); String publicUrl = FeedUtils.removeTrailingSlash(config.getApplicationSettings().getPublicUrl());
publicUrl += "/rest/user/passwordResetCallback"; publicUrl += "/rest/user/passwordResetCallback";
return String return String.format(
.format("You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.", "You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.",
user.getName(), callbackUrl(user, publicUrl)); user.getName(), callbackUrl(user, publicUrl));
} }
private String callbackUrl(User user, String publicUrl) throws Exception { private String callbackUrl(User user, String publicUrl) throws Exception {
@@ -292,6 +293,7 @@ public class UserREST {
@GET @GET
@UnitOfWork @UnitOfWork
@Produces(MediaType.TEXT_HTML) @Produces(MediaType.TEXT_HTML)
@Timed
public Response passwordRecoveryCallback(@QueryParam("email") String email, @QueryParam("token") String token) { public Response passwordRecoveryCallback(@QueryParam("email") String email, @QueryParam("token") String token) {
Preconditions.checkNotNull(email); Preconditions.checkNotNull(email);
Preconditions.checkNotNull(token); Preconditions.checkNotNull(token);
@@ -327,6 +329,7 @@ public class UserREST {
@POST @POST
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Delete the user account") @ApiOperation(value = "Delete the user account")
@Timed
public Response delete(@SecurityCheck User user) { public Response delete(@SecurityCheck User user) {
if (CommaFeedApplication.USERNAME_ADMIN.equals(user.getName()) || CommaFeedApplication.USERNAME_DEMO.equals(user.getName())) { if (CommaFeedApplication.USERNAME_ADMIN.equals(user.getName()) || CommaFeedApplication.USERNAME_DEMO.equals(user.getName())) {
return Response.status(Status.FORBIDDEN).build(); return Response.status(Status.FORBIDDEN).build();

View File

@@ -10,8 +10,6 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import com.commafeed.backend.dao.UnitOfWork; import com.commafeed.backend.dao.UnitOfWork;
@@ -20,8 +18,10 @@ import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserSettings; import com.commafeed.backend.model.UserSettings;
import com.commafeed.frontend.session.SessionHelper; import com.commafeed.frontend.session.SessionHelper;
import lombok.RequiredArgsConstructor;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class CustomCssServlet extends HttpServlet { public class CustomCssServlet extends HttpServlet {
@@ -37,7 +37,7 @@ public class CustomCssServlet extends HttpServlet {
return; return;
} }
UserSettings settings = UnitOfWork.run(sessionFactory, () -> userSettingsDAO.findByUser(user.get())); UserSettings settings = UnitOfWork.call(sessionFactory, () -> userSettingsDAO.findByUser(user.get()));
if (settings == null || settings.getCustomCss() == null) { if (settings == null || settings.getCustomCss() == null) {
return; return;
} }

View File

@@ -11,8 +11,6 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
@@ -31,8 +29,10 @@ import com.commafeed.frontend.resource.CategoryREST;
import com.commafeed.frontend.session.SessionHelper; import com.commafeed.frontend.session.SessionHelper;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import lombok.RequiredArgsConstructor;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }) )
@Singleton @Singleton
public class NextUnreadServlet extends HttpServlet { public class NextUnreadServlet extends HttpServlet {
@@ -63,31 +63,29 @@ public class NextUnreadServlet extends HttpServlet {
final ReadingOrder order = StringUtils.equals(orderParam, "asc") ? ReadingOrder.asc : ReadingOrder.desc; final ReadingOrder order = StringUtils.equals(orderParam, "asc") ? ReadingOrder.asc : ReadingOrder.desc;
FeedEntryStatus status = UnitOfWork.run( FeedEntryStatus status = UnitOfWork.call(sessionFactory, () -> {
sessionFactory, FeedEntryStatus s = null;
() -> { if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) {
FeedEntryStatus s = null; List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user.get());
if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) { List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subs, true, null, null, 0, 1, order,
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user.get()); true, false, null);
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subs, true, null, null, 0, 1, s = Iterables.getFirst(statuses, null);
order, true, false, null); } else {
s = Iterables.getFirst(statuses, null); FeedCategory category = feedCategoryDAO.findById(user.get(), Long.valueOf(categoryId));
} else { if (category != null) {
FeedCategory category = feedCategoryDAO.findById(user.get(), Long.valueOf(categoryId)); List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user.get(), category);
if (category != null) { List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user.get(), children);
List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user.get(), category); List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subscriptions, true, null, null, 0,
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user.get(), children); 1, order, true, false, null);
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subscriptions, true, null, s = Iterables.getFirst(statuses, null);
null, 0, 1, order, true, false, null); }
s = Iterables.getFirst(statuses, null); }
} if (s != null) {
} s.setRead(true);
if (s != null) { feedEntryStatusDAO.saveOrUpdate(s);
s.setRead(true); }
feedEntryStatusDAO.saveOrUpdate(s); return s;
} });
return s;
});
if (status == null) { if (status == null) {
resp.sendRedirect(resp.encodeRedirectURL(config.getApplicationSettings().getPublicUrl())); resp.sendRedirect(resp.encodeRedirectURL(config.getApplicationSettings().getPublicUrl()));