Compare commits

...

50 Commits
2.4.0 ... 2.5.0

Author SHA1 Message Date
Athou
c1dac2e064 2.5.0 release 2020-09-02 21:20:20 +02:00
Jérémie Panzer
f707993188 fix travis build 2020-08-15 08:33:12 +02:00
Athou
ea612d9d53 add missing validCheckSum 2020-05-18 09:42:08 +02:00
Jeremie Panzer
b44e737448 fix liquibase script when running on an empty postgresql database 2020-03-12 13:45:06 +01:00
Jeremie Panzer
bb429afd95 ignore swagger in eclipse 2020-03-12 12:54:20 +01:00
dependabot[bot]
475a8f8a28 Bump bower from 1.4.1 to 1.8.8 (#920)
Bumps [bower](https://github.com/bower/bower) from 1.4.1 to 1.8.8.
- [Release notes](https://github.com/bower/bower/releases)
- [Changelog](https://github.com/bower/bower/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bower/bower/compare/v1.4.1...v1.8.8)

Signed-off-by: dependabot[bot] <support@github.com>
2019-09-30 09:01:55 +02:00
Athou
c7ba5ca894 make swagger aware that dates are serialized as longs 2019-05-03 22:01:18 +02:00
Athou
3023f0a7cc fix build 2019-05-03 18:57:02 +02:00
Athou
ddaefbc952 deduplicate method names across all the api (swagger requires unique api operations) 2019-05-03 18:40:50 +02:00
Jeremie Panzer
0b3a0fb3ed add missing required 2019-05-02 13:42:49 +02:00
Athou
7f40a430fd hide securitycheck user from swagger documentation 2019-05-01 23:33:55 +02:00
Athou
05f5d3b25c add missing "required" flags 2019-05-01 20:31:48 +02:00
Athou
c3ca0b18b3 value field of annotation is actually the name of the class 2019-05-01 19:57:52 +02:00
Athou
696e0b1fa7 maven config for swagger plugin changed 2019-05-01 19:56:48 +02:00
Athou
201f7dbd3e restore cookieMaxAge behavior 2019-04-23 01:14:26 +02:00
Athou
0bfd3e906c stop hibernate HHH90000015 spam 2019-04-22 20:55:39 +02:00
Jérémie Panzer
71ac2bfc45 support for Java9+ (#906)
* initial java9+ support

* restore session management, updated for jetty 9.4

* Session actually implements EntityManager

* reusable method for setting the timeout
2019-04-22 20:30:06 +02:00
Athou
5370db7c5e rename for clarity 2019-03-17 07:05:29 +01:00
Athou
bcc30e40ba Merge branch 'ildar-shaimordanov-master' 2019-03-17 06:46:51 +01:00
Athou
2f70f654f7 extensible mechanism for feed url building 2019-03-17 06:44:09 +01:00
ildar-shaimordanov
b64115dcbd improve youtube feed URL getter 2019-03-12 05:52:00 +04:00
ildar-shaimordanov
c9c71d8582 workaround for youtube channels 2019-03-12 02:13:41 +04:00
Jérémie Panzer
689bc19296 Merge pull request #896 from nelsonblaha/patch-1
English correction for configuration comment
2019-02-10 20:33:12 +01:00
Ben Nelson
27498ab649 English correction for configuration comment 2019-02-10 11:35:46 -06:00
Athou
678a11f998 blur event seems to trigger twice for some reason, make sure we don't fetch the feed twice (#825) 2018-07-31 15:51:48 +02:00
Athou
e9ef98716f configurable user agent string (#825) 2018-07-31 15:21:45 +02:00
Athou
b3ce43eaf7 faster replace for large feeds (#881) 2018-07-11 17:13:38 +02:00
Jérémie Panzer
72083b7e87 Merge pull request #880 from Athou/snyk-fix-0oenl9
[Snyk] Fix for 6 vulnerable dependencies
2018-06-23 13:49:14 +02:00
snyk-bot
0cc94c2033 fix: pom.xml to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JAVA-COMH2DATABASE-31685
- https://snyk.io/vuln/SNYK-JAVA-MYSQL-31399
- https://snyk.io/vuln/SNYK-JAVA-MYSQL-31449
- https://snyk.io/vuln/SNYK-JAVA-MYSQL-31580
- https://snyk.io/vuln/SNYK-JAVA-ORGAPACHEHTTPCOMPONENTS-31517
- https://snyk.io/vuln/SNYK-JAVA-ORGJSOUP-31218
2018-06-14 05:55:43 +00:00
Jérémie Panzer
1d6296b400 Merge pull request #870 from asny23/update-japanese-translation
improve japanese translation
2018-04-12 12:18:50 +02:00
Unknown
7ad5da2a9e translate and improve japanese in ja.js 2018-04-11 23:27:56 +09:00
Athou
a7665a9994 add current year to filtering context 2018-02-25 13:38:37 +01:00
Jérémie Panzer
a4cd3f26e8 Update README.md 2018-02-22 18:21:48 +01:00
Athou
fcdb33b64b utility for testing feeds 2018-02-06 15:17:37 +01:00
Athou
7fd6119bcf add author to rss generated feeds (#858) 2017-12-22 18:50:32 +01:00
Jérémie Panzer
b4d4b2473c Merge pull request #854 from Busimus/patch-7
Updated Russian translation
2017-11-08 15:34:32 +01:00
Alexander Bus
91f715c3c3 Updated Russian translation 2017-10-28 01:16:57 +07:00
Athou
ea5fccfe5f fix build 2017-10-12 12:10:04 +02:00
Athou
86835eec73 request may not be a HttpUriRequest when using a proxy (#850) 2017-10-12 10:21:11 +02:00
Jérémie Panzer
2bccee2333 Merge pull request #849 from ema-pe/update-italian-translation
Update italian translation
2017-09-27 15:00:56 +02:00
Emanuele Petriglia
2d01b0d714 Update italian translation 2017-09-27 14:09:37 +02:00
Jérémie Panzer
44bf37b05a Update bower.json
fix alignment
2017-08-18 16:10:51 +02:00
Jérémie Panzer
cf617f0a64 Merge pull request #847 from sometoby/missing-shortcut-help
Add missing shortcut help for 'r'
2017-08-18 14:27:13 +02:00
Jérémie Panzer
eeeaffd883 Merge pull request #846 from sometoby/tinycon-unread-badge
Use tinycon to display unread article count
2017-08-18 14:25:07 +02:00
Tobias Umbach
d178302d34 Add comment so shortcut code is easier to find 2017-08-18 08:40:05 +02:00
Tobias Umbach
83a5364903 Add missing shortcut help for 'r', refresh 2017-08-18 08:39:49 +02:00
Tobias Umbach
aef76db664 Update CHANGELOG 2017-08-18 08:21:36 +02:00
Tobias Umbach
c3b3240191 Use tinycon to display unread article count 2017-08-14 11:15:43 +02:00
Athou
f381974955 prepare for next version 2017-08-01 13:55:11 +02:00
Athou
bd16dd98c4 update readme 2017-08-01 13:55:04 +02:00
70 changed files with 690 additions and 453 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@ config.yml
# build directory # build directory
target target
target-ide
# log files # log files
log log

View File

@@ -1,3 +1,3 @@
language: java language: java
jdk: jdk:
- oraclejdk8 - openjdk8

View File

@@ -1,3 +1,9 @@
v 2.5.0
- unread count is now displayed in a favicon badge when supported
- the user agent string for the bot fetching feeds is now configurable
- feed parsing performance improvements
- support for java9+ runtime
- can now properly start from an empty postgresql database
v 2.4.0 v 2.4.0
- users were not able to change password or delete account - users were not able to change password or delete account
- fix api key generation - fix api key generation
@@ -51,4 +57,4 @@ v2.0.0
- The backend has been completely rewritten using Dropwizard instead of TomEE, resulting in a lot less memory consumption and better overall performances. - The backend has been completely rewritten using Dropwizard instead of TomEE, resulting in a lot less memory consumption and better overall performances.
See the README on how to build CommaFeed from now on. See the README on how to build CommaFeed from now on.
- CommaFeed should no longer fetch the same feed multiple times in a row - CommaFeed should no longer fetch the same feed multiple times in a row
- Users can use their username or email to log in - Users can use their username or email to log in

View File

@@ -17,8 +17,8 @@ Browser extensions: [Chrome](https://github.com/Athou/commafeed-chrome) - [Firef
### The very short version (download precompiled package) ### The very short version (download precompiled package)
mkdir commafeed && cd commafeed mkdir commafeed && cd commafeed
wget https://github.com/Athou/commafeed/releases/download/2.3.0/commafeed.jar wget https://github.com/Athou/commafeed/releases/download/2.4.0/commafeed.jar
wget https://raw.githubusercontent.com/Athou/commafeed/2.3.0/config.yml.example -O config.yml wget https://raw.githubusercontent.com/Athou/commafeed/2.4.0/config.yml.example -O config.yml
vi config.yml vi config.yml
java -Djava.net.preferIPv4Stack=true -jar commafeed.jar server config.yml java -Djava.net.preferIPv4Stack=true -jar commafeed.jar server config.yml
@@ -67,20 +67,6 @@ Issue the following command to run the app, the server will listen by default on
You can use a proxy http server such as nginx or apache. You can use a proxy http server such as nginx or apache.
## Deployment on OpenShift
[OpenShift](https://openshift.redhat.com) is Red Hat's Platform-as-a-Service (PaaS) that allows developers to quickly develop, host, and scale applications in a cloud environment. CommaFeed runs perfectly on OpenShift and can even be used in the free tier. Follow the [Getting Started](https://developers.openshift.com/en/getting-started-overview.html) guide and after you sign up and install the Command Line Tools (RHC), do:
rhc create-app commafeed diy-0.1 mysql-5.5
cd commafeed
git remote add upstream -m master https://github.com/Athou/commafeed.git
git pull -s recursive -X theirs upstream master
git push
# To upgrade an existing openshift installation
git pull upstream master
git push
## Translate CommaFeed into your language ## Translate CommaFeed into your language
Files for internationalization are located [here](https://github.com/Athou/commafeed/tree/master/src/main/app/i18n). Files for internationalization are located [here](https://github.com/Athou/commafeed/tree/master/src/main/app/i18n).

View File

@@ -28,7 +28,8 @@
"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.0" "swagger-ui": "2.1.0",
"tinycon": "0.6.5"
}, },
"resolutions": { "resolutions": {
"angular": "1.3.14", "angular": "1.3.14",

View File

@@ -67,6 +67,9 @@ app:
# announcement string displayed on the main page # announcement string displayed on the main page
announcement: announcement:
# user-agent string that will be used by the http client, leave empty for the default one
userAgent:
# Database connection # Database connection
# ------------------- # -------------------
# for MySQL # for MySQL

View File

@@ -4,7 +4,7 @@ app:
# url used to access commafeed # url used to access commafeed
publicUrl: http://localhost:8082/ publicUrl: http://localhost:8082/
# wether to allow user registrations # whether to allow user registrations
allowRegistrations: false allowRegistrations: false
# create a demo account the first time the app starts # create a demo account the first time the app starts
@@ -68,6 +68,9 @@ app:
# announcement string displayed on the main page # announcement string displayed on the main page
announcement: announcement:
# user-agent string that will be used by the http client, leave empty for the default one
userAgent:
# Database connection # Database connection
# ------------------- # -------------------
# for MySQL # for MySQL
@@ -108,6 +111,7 @@ logging:
com.commafeed: INFO com.commafeed: INFO
liquibase: INFO liquibase: INFO
io.dropwizard.server.ServerFactory: INFO io.dropwizard.server.ServerFactory: INFO
org.hibernate.orm.deprecation: "OFF"
appenders: appenders:
- type: console - type: console
- type: file - type: file
@@ -128,4 +132,4 @@ redis:
timeout: 2000 timeout: 2000
database: 0 database: 0
maxTotal: 500 maxTotal: 500

View File

@@ -4,7 +4,7 @@
"main": "main.js", "main": "main.js",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"bower": "1.4.1", "bower": "1.8.8",
"gulp": "3.8.11", "gulp": "3.8.11",
"gulp-rev": "4.0.0", "gulp-rev": "4.0.0",
"gulp-rev-replace": "0.4.1", "gulp-rev-replace": "0.4.1",

153
pom.xml
View File

@@ -4,16 +4,16 @@
<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.4.0</version> <version>2.5.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>CommaFeed</name> <name>CommaFeed</name>
<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.9.1</dropwizard.version> <dropwizard.version>1.3.20</dropwizard.version>
<guice.version>4.0</guice.version> <guice.version>4.2.2</guice.version>
<querydsl.version>4.0.2</querydsl.version> <querydsl.version>4.2.1</querydsl.version>
<rome.version>1.5.0</rome.version> <rome.version>1.5.0</rome.version>
</properties> </properties>
@@ -32,43 +32,11 @@
</resource> </resource>
</resources> </resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<versionRange>[0.0.22,)</versionRange>
<goals>
<goal>npm</goal>
<goal>gulp</goal>
<goal>bower</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute>
<runOnIncremental>false</runOnIncremental>
</execute>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version> <version>3.8.0</version>
<configuration> <configuration>
<source>${java.version}</source> <source>${java.version}</source>
<target>${java.version}</target> <target>${java.version}</target>
@@ -128,11 +96,15 @@
<plugin> <plugin>
<groupId>com.github.kongchen</groupId> <groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId> <artifactId>swagger-maven-plugin</artifactId>
<version>3.1.1</version> <version>3.1.7</version>
<configuration> <configuration>
<apiSources> <apiSources>
<apiSource> <apiSource>
<locations>com.commafeed.frontend.resource;com.commafeed.frontend.model;com.commafeed.frontend.model.request</locations> <locations>
<location>com.commafeed.frontend.resource</location>
<location>com.commafeed.frontend.model</location>
<location>com.commafeed.frontend.model.request</location>
</locations>
<swaggerDirectory>target/swagger</swaggerDirectory> <swaggerDirectory>target/swagger</swaggerDirectory>
<basePath>/rest</basePath> <basePath>/rest</basePath>
<info> <info>
@@ -157,7 +129,7 @@
<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.25</version> <version>1.6</version>
<executions> <executions>
<execution> <execution>
<id>install node and npm</id> <id>install node and npm</id>
@@ -166,7 +138,7 @@
</goals> </goals>
<phase>compile</phase> <phase>compile</phase>
<configuration> <configuration>
<nodeVersion>v0.10.39</nodeVersion> <nodeVersion>v6.11.4</nodeVersion>
<npmVersion>3.10.6</npmVersion> <npmVersion>3.10.6</npmVersion>
</configuration> </configuration>
</execution> </execution>
@@ -202,7 +174,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>2.6</version> <version>3.1.1</version>
<configuration> <configuration>
<archive> <archive>
<manifest> <manifest>
@@ -214,11 +186,70 @@
</plugins> </plugins>
</build> </build>
<profiles>
<profile>
<id>only-eclipse</id>
<activation>
<property>
<name>m2e.version</name>
</property>
</activation>
<build>
<directory>target-ide</directory>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<versionRange>[0.0.22,)</versionRange>
<goals>
<goal>npm</goal>
<goal>gulp</goal>
<goal>bower</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute>
<runOnIncremental>false</runOnIncremental>
</execute>
</action>
</pluginExecution>
<pluginExecution>
<pluginExecutionFilter>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<versionRange>[3.1.7,)</versionRange>
<goals>
<goal>generate</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.16.4</version> <version>1.18.6</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -279,16 +310,21 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.dropwizard.metrics</groupId> <groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-graphite</artifactId> <artifactId>metrics-graphite</artifactId>
<version>3.1.2</version> <version>3.1.2</version>
</dependency> </dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
<version>4.5</version> <version>4.5.2</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>commons-logging</artifactId> <artifactId>commons-logging</artifactId>
@@ -300,7 +336,7 @@
<dependency> <dependency>
<groupId>io.swagger</groupId> <groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId> <artifactId>swagger-annotations</artifactId>
<version>1.5.0</version> <version>1.5.22</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -316,11 +352,6 @@
<version>${querydsl.version}</version> <version>${querydsl.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
@@ -386,11 +417,15 @@
<artifactId>jdom2</artifactId> <artifactId>jdom2</artifactId>
<version>2.0.6</version> <version>2.0.6</version>
</dependency> </dependency>
<dependency>
<groupId>org.ahocorasick</groupId>
<artifactId>ahocorasick</artifactId>
<version>0.4.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
<version>1.8.2</version> <version>1.8.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.ibm.icu</groupId> <groupId>com.ibm.icu</groupId>
@@ -418,12 +453,12 @@
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
<version>1.3.176</version> <version>1.4.197</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version> <version>5.1.42</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>

View File

@@ -175,7 +175,8 @@
"font_size" : "increase/decrease font size of the current entry ", "font_size" : "increase/decrease font size of the current entry ",
"go_to_all" : "go to the All view ", "go_to_all" : "go to the All view ",
"go_to_starred" : "go to the Starred view ", "go_to_starred" : "go to the Starred view ",
"feed_search" : "navigate to a subscription by entering the subscription name" "feed_search" : "navigate to a subscription by entering the subscription name",
"refresh": "refresh"
} }
} }
} }

View File

@@ -177,7 +177,8 @@
"font_size" : "increase/decrease font size of the current entry", "font_size" : "increase/decrease font size of the current entry",
"go_to_all" : "go to the All view", "go_to_all" : "go to the All view",
"go_to_starred" : "go to the Starred view", "go_to_starred" : "go to the Starred view",
"feed_search" : "navigate to a subscription by entering the subscription name" "feed_search" : "navigate to a subscription by entering the subscription name",
"refresh": "refresh"
} }
} }
} }

View File

@@ -41,7 +41,7 @@
"refresh" : "Aggiorna", "refresh" : "Aggiorna",
"refresh_all" : "Forza l'aggiornamento di tutti i feed", "refresh_all" : "Forza l'aggiornamento di tutti i feed",
"sort_by_asc_desc" : "Ordina per data crescente/decrescente", "sort_by_asc_desc" : "Ordina per data crescente/decrescente",
"sort_by_abc_zyx" : "Sort Alphabetically", "sort_by_abc_zyx" : "Ordina alfabeticamente",
"titles_only" : "Solo i titoli", "titles_only" : "Solo i titoli",
"expanded_view" : "Espandi", "expanded_view" : "Espandi",
"mark_all_as_read" : "Segna tutti come già letti", "mark_all_as_read" : "Segna tutti come già letti",
@@ -57,8 +57,8 @@
"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 del feed", "error_while_loading_feed" : "Si è verificato un errore durante il caricamento del feed",
"keep_unread" : "Mantieni come da leggere", "keep_unread" : "Mantieni come da leggere",
"no_unread_items" : "non contiene elementi da leggere", "no_unread_items" : "non contiene elementi da leggere",
@@ -82,9 +82,9 @@
}, },
"appearance" : "Aspetto", "appearance" : "Aspetto",
"scroll_speed" : "Velocità di scorrimento quando navighi tra i feed (in millisecondi)", "scroll_speed" : "Velocità di scorrimento quando navighi tra i feed (in millisecondi)",
"scroll_speed_help" : "Imposta su 0 per disabilitare", "scroll_speed_help" : "imposta su 0 per disabilitare",
"theme" : "Tema", "theme" : "Tema",
"submit_your_theme" : "Sottoponi il tuo tema", "submit_your_theme" : "Inserisci il tuo tema",
"custom_css" : "CSS personalizzato" "custom_css" : "CSS personalizzato"
}, },
"details" : { "details" : {
@@ -130,7 +130,7 @@
"line1" : "CommaFeed è basato su JAX-RS e AngularJS. Pertanto è disponibile una REST API.", "line1" : "CommaFeed è basato su JAX-RS e AngularJS. Pertanto è disponibile una REST API.",
"link_to_documentation" : "Link 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. Trovi i sorgenti su ", "line1_prefix" : "CommaFeed è un progetto open source. Trovi i sorgenti su ",
"line1_suffix" : ".", "line1_suffix" : ".",
@@ -148,12 +148,12 @@
"subscribe_bookmarklet" : "Aggiungi la sottoscrizione ai segnalibri (clicca)", "subscribe_bookmarklet" : "Aggiungi la sottoscrizione ai segnalibri (clicca)",
"subscribe_bookmarklet_asc" : "Prima i vecchi", "subscribe_bookmarklet_asc" : "Prima i vecchi",
"subscribe_bookmarklet_desc" : "Prima i recenti", "subscribe_bookmarklet_desc" : "Prima i recenti",
"next_unread_bookmarklet" : "Bookmarklet al prossimo elemento da leggere (trascinalo nella barra dei segnalibri)" "next_unread_bookmarklet" : "Segnalibro 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" : "Scopri come aiutarci nella traduzioni." "link" : "Scopri come aiutarci nelle traduzioni."
}, },
"announcements" : "Annunci", "announcements" : "Annunci",
"shortcuts" : { "shortcuts" : {
@@ -177,7 +177,8 @@
"font_size" : "aumenta/decrementa la dimensione del font per la voce corrente", "font_size" : "aumenta/decrementa la dimensione del font per la voce corrente",
"go_to_all" : "vai alla vista Tutti", "go_to_all" : "vai alla vista Tutti",
"go_to_starred" : "vai alla vista Preferiti", "go_to_starred" : "vai alla vista Preferiti",
"feed_search" : "raggiungi una sottoscrizione scrivendo il suo nome" "feed_search" : "raggiungi una sottoscrizione scrivendo il suo nome",
"refresh" : "aggiorna"
} }
} }
} }

View File

@@ -3,7 +3,7 @@
"save" : "保存", "save" : "保存",
"cancel" : "取り消し", "cancel" : "取り消し",
"delete" : "削除", "delete" : "削除",
"required" : "Required", "required" : "必須",
"download" : "ダウンロード", "download" : "ダウンロード",
"link" : "リンク", "link" : "リンク",
"bookmark" : "ブックマーク", "bookmark" : "ブックマーク",
@@ -15,7 +15,7 @@
"import" : "インポート", "import" : "インポート",
"new_category" : "新しいカテゴリー", "new_category" : "新しいカテゴリー",
"all" : "全て", "all" : "全て",
"starred" : "スター付" "starred" : "スター付"
}, },
"subscribe" : { "subscribe" : {
"feed_url" : "フィードURL", "feed_url" : "フィードURL",
@@ -40,8 +40,8 @@
"next_entry" : "次のエントリー", "next_entry" : "次のエントリー",
"refresh" : "更新", "refresh" : "更新",
"refresh_all" : "全てのフィードを更新", "refresh_all" : "全てのフィードを更新",
"sort_by_asc_desc" : "昇順/降順にソート", "sort_by_asc_desc" : "日時でソート",
"sort_by_abc_zyx" : "Sort Alphabetically", "sort_by_abc_zyx" : "名前でソート",
"titles_only" : "タイトルのみ", "titles_only" : "タイトルのみ",
"expanded_view" : "拡張ビュー", "expanded_view" : "拡張ビュー",
"mark_all_as_read" : "全て既読にする", "mark_all_as_read" : "全て既読にする",
@@ -101,8 +101,8 @@
"feed_url" : "フィードURL", "feed_url" : "フィードURL",
"generate_api_key_first" : "最初にあなたのAPIキーを生成して下さい。", "generate_api_key_first" : "最初にあなたのAPIキーを生成して下さい。",
"unsubscribe" : "購読解除", "unsubscribe" : "購読解除",
"unsubscribe_confirmation" : "Are you sure you want to unsubscribe from this feed? ", "unsubscribe_confirmation" : "フィードの購読を解除してよろしいですか?",
"delete_category_confirmation" : "Are you sure you want to delete this category? ", "delete_category_confirmation" : "カテゴリーを削除してよろしいですか?",
"category_details" : "カテゴリー詳細", "category_details" : "カテゴリー詳細",
"tag_details" : "タグ詳細", "tag_details" : "タグ詳細",
"parent_category" : "親カテゴリー" "parent_category" : "親カテゴリー"
@@ -117,10 +117,10 @@
"api_key" : "APIキー", "api_key" : "APIキー",
"api_key_not_generated" : "APIキーが生成されていません", "api_key_not_generated" : "APIキーが生成されていません",
"generate_new_api_key" : "新しいAPIキーを生成", "generate_new_api_key" : "新しいAPIキーを生成",
"generate_new_api_key_info" : "パスワード変更新しいAPIキーが生成されます", "generate_new_api_key_info" : "パスワード変更すると新しいAPIキーが生成されます",
"opml_export" : "OPMLエクスポート", "opml_export" : "OPMLエクスポート",
"delete_account" : "アカウント削除", "delete_account" : "アカウント削除",
"delete_account_confirmation" : "Delete your acount? There's no turning back! " "delete_account_confirmation" : "アカウントを削除してよろしいですか? 削除すると戻すことはできません!"
}, },
"about" : { "about" : {
"rest_api" : { "rest_api" : {
@@ -150,10 +150,10 @@
}, },
"translation" : { "translation" : {
"value" : "翻訳", "value" : "翻訳",
"message" : "CommaFeedの翻訳に助けが必要です", "message" : "CommaFeedの翻訳にご協力ください",
"link" : "どうやって翻訳に貢献できるか見て下さい。" "link" : "翻訳にあたっての案内はこちら"
}, },
"announcements" : "Announcements", "announcements" : "お知らせ",
"shortcuts" : { "shortcuts" : {
"mouse_middleclick" : "中クリック", "mouse_middleclick" : "中クリック",
"open_next_entry" : "次のエントリーを開く", "open_next_entry" : "次のエントリーを開く",
@@ -171,7 +171,7 @@
"mark_current_entry" : "現在のエントリーを既読/未読にする", "mark_current_entry" : "現在のエントリーを既読/未読にする",
"mark_all_as_read" : "全エントリーを既読にする", "mark_all_as_read" : "全エントリーを既読にする",
"open_in_new_tab_mark_as_read" : "エントリーを既読にして新しいタブで開く", "open_in_new_tab_mark_as_read" : "エントリーを既読にして新しいタブで開く",
"fullscreen" : "フルスクリーントグル", "fullscreen" : "フルスクリーン切り替え",
"font_size" : "現在のエントリーのフォントサイズを大きく/小さくする", "font_size" : "現在のエントリーのフォントサイズを大きく/小さくする",
"go_to_all" : "All viewに変更する", "go_to_all" : "All viewに変更する",
"go_to_starred" : "スター付きviewに変更する", "go_to_starred" : "スター付きviewに変更する",

View File

@@ -41,7 +41,7 @@
"refresh" : "Обновить", "refresh" : "Обновить",
"refresh_all" : "Обновить все подписки вручную", "refresh_all" : "Обновить все подписки вручную",
"sort_by_asc_desc" : "Сначала новые/старые", "sort_by_asc_desc" : "Сначала новые/старые",
"sort_by_abc_zyx" : "Sort Alphabetically", "sort_by_abc_zyx" : "По алфавиту",
"titles_only" : "Только заголовки", "titles_only" : "Только заголовки",
"expanded_view" : "Развёрнутый вид", "expanded_view" : "Развёрнутый вид",
"mark_all_as_read" : "Отметить всё как прочитанное", "mark_all_as_read" : "Отметить всё как прочитанное",
@@ -178,4 +178,4 @@
"feed_search" : "перейти к подписке по названию" "feed_search" : "перейти к подписке по названию"
} }
} }
} }

View File

@@ -70,6 +70,7 @@
<script type="text/javascript" src="lib/mousetrap/mousetrap.js"></script> <script type="text/javascript" src="lib/mousetrap/mousetrap.js"></script>
<script type="text/javascript" src="lib/momentjs/min/moment-with-locales.js"></script> <script type="text/javascript" src="lib/momentjs/min/moment-with-locales.js"></script>
<script type="text/javascript" src="lib/devicejs/lib/device.js"></script> <script type="text/javascript" src="lib/devicejs/lib/device.js"></script>
<script type="text/javascript" src="lib/tinycon/tinycon.js"></script>
<script type="text/javascript" src="js/controllers.js"></script> <script type="text/javascript" src="js/controllers.js"></script>
<script type="text/javascript" src="js/directives.js"></script> <script type="text/javascript" src="js/directives.js"></script>

View File

@@ -43,7 +43,7 @@ module.controller('SubscribeCtrl', ['$scope', '$location', 'FeedService', 'Categ
// 'ok', 'loading' or 'failed' // 'ok', 'loading' or 'failed'
$scope.state = 'ok'; $scope.state = 'ok';
$scope.urlChanged = function() { $scope.urlChanged = function() {
if ($scope.sub.url) { if ($scope.sub.url && $scope.state != 'loading') {
$scope.state = 'loading'; $scope.state = 'loading';
$scope.sub.title = 'Loading...'; $scope.sub.title = 'Loading...';
FeedService.fetch({ FeedService.fetch({
@@ -194,11 +194,7 @@ module.controller('CategoryTreeCtrl', [
}; };
$scope.$watch(rootUnreadCount, function(value) { $scope.$watch(rootUnreadCount, function(value) {
var label = 'CommaFeed'; Tinycon.setBubble(value);
if (value > 0) {
label = '(' + value + ') ' + label;
}
$window.document.title = label;
}); });
var mark = function(node, entry) { var mark = function(node, entry) {
@@ -1061,6 +1057,7 @@ module.controller('FeedListCtrl', [
} }
}; };
// keyboard shortcuts
Mousetrap.bind('j', function(e) { Mousetrap.bind('j', function(e) {
$scope.$apply(function() { $scope.$apply(function() {
$scope.navigationMode = 'keyboard'; $scope.navigationMode = 'keyboard';

View File

@@ -1,4 +1,7 @@
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt>r</dt>
<dd>{{ 'about.shortcuts.refresh' | translate }}</dd>
<dt>j</dt> <dt>j</dt>
<dd>{{ 'about.shortcuts.open_next_entry' | translate }}</dd> <dd>{{ 'about.shortcuts.open_next_entry' | translate }}</dd>
@@ -68,4 +71,4 @@
</dt> </dt>
<dd>{{ 'about.shortcuts.feed_search' | translate }}</dd> <dd>{{ 'about.shortcuts.feed_search' | translate }}</dd>
</dl> </dl>

View File

@@ -13,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.eclipse.jetty.server.session.SessionHandler;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import com.commafeed.backend.feed.FeedRefreshTaskGiver; import com.commafeed.backend.feed.FeedRefreshTaskGiver;
@@ -111,7 +110,7 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
Injector injector = Guice.createInjector(new CommaFeedModule(hibernateBundle.getSessionFactory(), config, environment.metrics())); Injector injector = Guice.createInjector(new CommaFeedModule(hibernateBundle.getSessionFactory(), config, environment.metrics()));
// session management // session management
environment.servlets().setSessionHandler(new SessionHandler(config.getSessionManagerFactory().build())); environment.servlets().setSessionHandler(config.getSessionHandlerFactory().build());
// support for "@SecurityCheck User user" injection // support for "@SecurityCheck User user" injection
environment.jersey().register(new SecurityCheckFactoryProvider.Binder(injector.getInstance(UserService.class))); environment.jersey().register(new SecurityCheckFactoryProvider.Binder(injector.getInstance(UserService.class)));

View File

@@ -1,8 +1,5 @@
package com.commafeed; package com.commafeed;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
import java.util.Date; import java.util.Date;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@@ -10,15 +7,17 @@ import javax.validation.Valid;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import lombok.Getter;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.NotBlank;
import com.commafeed.backend.cache.RedisPoolFactory; import com.commafeed.backend.cache.RedisPoolFactory;
import com.commafeed.frontend.session.SessionManagerFactory; import com.commafeed.frontend.session.SessionHandlerFactory;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
import lombok.Getter;
@Getter @Getter
public class CommaFeedConfiguration extends Configuration { public class CommaFeedConfiguration extends Configuration {
@@ -45,7 +44,7 @@ public class CommaFeedConfiguration extends Configuration {
@Valid @Valid
@NotNull @NotNull
@JsonProperty("session") @JsonProperty("session")
private SessionManagerFactory sessionManagerFactory = new SessionManagerFactory(); private SessionHandlerFactory SessionHandlerFactory = new SessionHandlerFactory();
@Valid @Valid
@NotNull @NotNull
@@ -138,10 +137,11 @@ public class CommaFeedConfiguration extends Configuration {
@Valid @Valid
private CacheType cache; private CacheType cache;
@NotNull
@Valid @Valid
private String announcement; private String announcement;
private String userAgent;
public Date getUnreadThreshold() { public Date getUnreadThreshold() {
int keepStatusDays = getKeepStatusDays(); int keepStatusDays = getKeepStatusDays();
return keepStatusDays > 0 ? DateUtils.addDays(new Date(), -1 * keepStatusDays) : null; return keepStatusDays > 0 ? DateUtils.addDays(new Date(), -1 * keepStatusDays) : null;

View File

@@ -26,6 +26,9 @@ import com.commafeed.backend.task.OldStatusesCleanupTask;
import com.commafeed.backend.task.OrphanedContentsCleanupTask; import com.commafeed.backend.task.OrphanedContentsCleanupTask;
import com.commafeed.backend.task.OrphanedFeedsCleanupTask; import com.commafeed.backend.task.OrphanedFeedsCleanupTask;
import com.commafeed.backend.task.ScheduledTask; import com.commafeed.backend.task.ScheduledTask;
import com.commafeed.backend.urlprovider.FeedURLProvider;
import com.commafeed.backend.urlprovider.InPageReferenceFeedURLProvider;
import com.commafeed.backend.urlprovider.YoutubeFeedURLProvider;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.Multibinder;
@@ -55,6 +58,10 @@ public class CommaFeedModule extends AbstractModule {
faviconMultibinder.addBinding().to(FacebookFaviconFetcher.class); faviconMultibinder.addBinding().to(FacebookFaviconFetcher.class);
faviconMultibinder.addBinding().to(DefaultFaviconFetcher.class); faviconMultibinder.addBinding().to(DefaultFaviconFetcher.class);
Multibinder<FeedURLProvider> urlProviderMultibinder = Multibinder.newSetBinder(binder(), FeedURLProvider.class);
urlProviderMultibinder.addBinding().to(InPageReferenceFeedURLProvider.class);
urlProviderMultibinder.addBinding().to(YoutubeFeedURLProvider.class);
Multibinder<ScheduledTask> taskMultibinder = Multibinder.newSetBinder(binder(), ScheduledTask.class); Multibinder<ScheduledTask> taskMultibinder = Multibinder.newSetBinder(binder(), ScheduledTask.class);
taskMultibinder.addBinding().to(OldStatusesCleanupTask.class); taskMultibinder.addBinding().to(OldStatusesCleanupTask.class);
taskMultibinder.addBinding().to(OldEntriesCleanupTask.class); taskMultibinder.addBinding().to(OldEntriesCleanupTask.class);

View File

@@ -70,7 +70,10 @@ public class HttpGetter {
@Inject @Inject
public HttpGetter(CommaFeedConfiguration config) { public HttpGetter(CommaFeedConfiguration config) {
this.userAgent = String.format("CommaFeed/%s (https://github.com/Athou/commafeed)", config.getVersion()); this.userAgent = config.getApplicationSettings().getUserAgent();
if (this.userAgent == null) {
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 {
@@ -151,9 +154,13 @@ public class HttpGetter {
contentType = entity.getContentType().getValue(); contentType = entity.getContentType().getValue();
} }
} }
HttpUriRequest req = (HttpUriRequest) context.getRequest();
HttpHost host = context.getTargetHost(); String urlAfterRedirect = url;
String urlAfterRedirect = req.getURI().isAbsolute() ? req.getURI().toString() : host.toURI() + req.getURI(); if (context.getRequest() instanceof HttpUriRequest) {
HttpUriRequest req = (HttpUriRequest) context.getRequest();
HttpHost host = context.getTargetHost();
urlAfterRedirect = req.getURI().isAbsolute() ? req.getURI().toString() : host.toURI() + req.getURI();
}
long duration = System.currentTimeMillis() - start; long duration = System.currentTimeMillis() - start;
result = new HttpResult(content, contentType, lastModifiedHeaderValue, eTagHeaderValue, duration, urlAfterRedirect); result = new HttpResult(content, contentType, lastModifiedHeaderValue, eTagHeaderValue, duration, urlAfterRedirect);
@@ -219,4 +226,11 @@ public class HttpGetter {
return null; return null;
} }
} }
public static void main(String[] args) throws Exception {
CommaFeedConfiguration config = new CommaFeedConfiguration();
HttpGetter getter = new HttpGetter(config);
HttpResult result = getter.getBinary("https://sourceforge.net/projects/mpv-player-windows/rss", 30000);
System.out.println(new String(result.content));
}
} }

View File

@@ -17,7 +17,6 @@ import com.commafeed.backend.model.QUser;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.JPQLQuery; import com.querydsl.jpa.JPQLQuery;
import com.querydsl.jpa.hibernate.HibernateQuery;
@Singleton @Singleton
public class FeedDAO extends GenericDAO<Feed> { public class FeedDAO extends GenericDAO<Feed> {
@@ -30,7 +29,7 @@ public class FeedDAO extends GenericDAO<Feed> {
} }
public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) { public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) {
HibernateQuery<Feed> query = query().selectFrom(feed); JPQLQuery<Feed> query = query().selectFrom(feed);
query.where(feed.disabledUntil.isNull().or(feed.disabledUntil.lt(new Date()))); query.where(feed.disabledUntil.isNull().or(feed.disabledUntil.lt(new Date())));
if (lastLoginThreshold != null) { if (lastLoginThreshold != null) {
@@ -60,7 +59,6 @@ public class FeedDAO extends GenericDAO<Feed> {
public List<Feed> findWithoutSubscriptions(int max) { public List<Feed> findWithoutSubscriptions(int max) {
QFeedSubscription sub = QFeedSubscription.feedSubscription; QFeedSubscription sub = QFeedSubscription.feedSubscription;
return query().selectFrom(feed).where(JPAExpressions.selectOne().from(sub).where(sub.feed.eq(feed)).notExists()).limit(max) return query().selectFrom(feed).where(JPAExpressions.selectOne().from(sub).where(sub.feed.eq(feed)).notExists()).limit(max).fetch();
.fetch();
} }
} }

View File

@@ -33,7 +33,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.querydsl.core.BooleanBuilder; import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Tuple; import com.querydsl.core.Tuple;
import com.querydsl.jpa.hibernate.HibernateQuery; import com.querydsl.jpa.impl.JPAQuery;
@Singleton @Singleton
public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> { public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
@@ -67,7 +67,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
}; };
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();
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ABC = new Comparator<FeedEntryStatus>() { private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ABC = new Comparator<FeedEntryStatus>() {
@Override @Override
public int compare(FeedEntryStatus o1, FeedEntryStatus o2) { public int compare(FeedEntryStatus o1, FeedEntryStatus o2) {
@@ -77,9 +77,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return builder.toComparison(); return builder.toComparison();
} }
}; };
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ZYX = Ordering.from(STATUS_COMPARATOR_ABC).reverse();
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ZYX = Ordering.from(STATUS_COMPARATOR_ABC).reverse();
public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) { public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) {
List<FeedEntryStatus> statuses = query().selectFrom(status).where(status.entry.eq(entry), status.subscription.eq(sub)).fetch(); List<FeedEntryStatus> statuses = query().selectFrom(status).where(status.entry.eq(entry), status.subscription.eq(sub)).fetch();
@@ -107,26 +106,23 @@ 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<FeedEntryStatus> query = query().selectFrom(status).where(status.user.eq(user), status.starred.isTrue()); JPAQuery<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));
} }
if (order == ReadingOrder.asc) { if (order == ReadingOrder.asc) {
query.orderBy(status.entryUpdated.asc(), status.id.asc()); query.orderBy(status.entryUpdated.asc(), status.id.asc());
} else if (order == ReadingOrder.desc){ } else if (order == ReadingOrder.desc) {
query.orderBy(status.entryUpdated.desc(), status.id.desc()); query.orderBy(status.entryUpdated.desc(), status.id.desc());
} else if (order == ReadingOrder.abc) { } else if (order == ReadingOrder.abc) {
query.orderBy(status.entry.content.title.asc(), status.id.desc()); query.orderBy(status.entry.content.title.asc(), status.id.desc());
} else { //order == ReadingOrder.xyz } else { // order == ReadingOrder.xyz
query.orderBy(status.entry.content.title.desc(), status.id.desc()); query.orderBy(status.entry.content.title.desc(), status.id.desc());
} }
query.offset(offset).limit(limit); query.offset(offset).limit(limit);
int timeout = config.getApplicationSettings().getQueryTimeout(); setTimeout(query, config.getApplicationSettings().getQueryTimeout());
if (timeout > 0) {
query.setTimeout(timeout / 1000);
}
List<FeedEntryStatus> statuses = query.fetch(); List<FeedEntryStatus> statuses = query.fetch();
for (FeedEntryStatus status : statuses) { for (FeedEntryStatus status : statuses) {
@@ -136,10 +132,10 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return lazyLoadContent(includeContent, statuses); return lazyLoadContent(includeContent, statuses);
} }
private HibernateQuery<FeedEntry> buildQuery(User user, FeedSubscription sub, boolean unreadOnly, List<FeedEntryKeyword> keywords, private JPAQuery<FeedEntry> buildQuery(User user, FeedSubscription sub, boolean unreadOnly, List<FeedEntryKeyword> keywords,
Date newerThan, int offset, int limit, ReadingOrder order, FeedEntryStatus last, String tag) { Date newerThan, int offset, int limit, ReadingOrder order, FeedEntryStatus last, String tag) {
HibernateQuery<FeedEntry> query = query().selectFrom(entry).where(entry.feed.eq(sub.getFeed())); JPAQuery<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);
@@ -187,14 +183,14 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
} else if (order == ReadingOrder.abc) { } else if (order == ReadingOrder.abc) {
query.join(entry.content, content); query.join(entry.content, content);
query.where(content.title.lt(last.getEntry().getContent().getTitle())); query.where(content.title.lt(last.getEntry().getContent().getTitle()));
} else { //order == ReadingOrder.zyx } else { // order == ReadingOrder.zyx
query.join(entry.content, content); query.join(entry.content, content);
query.where(content.title.gt(last.getEntry().getContent().getTitle())); query.where(content.title.gt(last.getEntry().getContent().getTitle()));
} }
} else if (order != null && (order == ReadingOrder.abc || order == ReadingOrder.zyx)) { } else if (order != null && (order == ReadingOrder.abc || order == ReadingOrder.zyx)) {
query.join(entry.content, content); query.join(entry.content, content);
} }
if (order != null) { if (order != null) {
if (order == ReadingOrder.asc) { if (order == ReadingOrder.asc) {
query.orderBy(entry.updated.asc(), entry.id.asc()); query.orderBy(entry.updated.asc(), entry.id.asc());
@@ -202,7 +198,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
query.orderBy(entry.updated.desc(), entry.id.desc()); query.orderBy(entry.updated.desc(), entry.id.desc());
} else if (order == ReadingOrder.abc) { } else if (order == ReadingOrder.abc) {
query.orderBy(content.title.asc(), entry.id.asc()); query.orderBy(content.title.asc(), entry.id.asc());
} else { //order == ReadingOrder.zyx } else { // order == ReadingOrder.zyx
query.orderBy(content.title.desc(), entry.id.desc()); query.orderBy(content.title.desc(), entry.id.desc());
} }
} }
@@ -212,10 +208,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
if (limit > -1) { if (limit > -1) {
query.limit(limit); query.limit(limit);
} }
int timeout = config.getApplicationSettings().getQueryTimeout(); setTimeout(query, config.getApplicationSettings().getQueryTimeout());
if (timeout > 0) {
query.setTimeout(timeout / 1000);
}
return query; return query;
} }
@@ -223,7 +216,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
List<FeedEntryKeyword> keywords, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent, List<FeedEntryKeyword> keywords, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent,
boolean onlyIds, String tag) { boolean onlyIds, String tag) {
int capacity = offset + limit; int capacity = offset + limit;
Comparator<FeedEntryStatus> comparator; Comparator<FeedEntryStatus> comparator;
if (order == ReadingOrder.desc) { if (order == ReadingOrder.desc) {
comparator = STATUS_COMPARATOR_DESC; comparator = STATUS_COMPARATOR_DESC;
@@ -238,7 +231,7 @@ 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) {
FeedEntryStatus last = (order != null && set.isFull()) ? set.last() : null; FeedEntryStatus last = (order != null && set.isFull()) ? set.last() : null;
HibernateQuery<FeedEntry> query = buildQuery(user, sub, unreadOnly, keywords, newerThan, -1, capacity, order, last, tag); JPAQuery<FeedEntry> query = buildQuery(user, sub, unreadOnly, keywords, newerThan, -1, capacity, order, last, tag);
List<Tuple> tuples = query.select(entry.id, entry.updated, status.id, entry.content.title).fetch(); List<Tuple> tuples = query.select(entry.id, entry.updated, status.id, entry.content.title).fetch();
for (Tuple tuple : tuples) { for (Tuple tuple : tuples) {
@@ -291,7 +284,7 @@ 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<FeedEntry> query = buildQuery(user, subscription, true, null, null, -1, -1, null, null, null); JPAQuery<FeedEntry> query = buildQuery(user, subscription, true, null, null, -1, -1, null, null, null);
List<Tuple> tuples = query.select(entry.count(), entry.updated.max()).fetch(); 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());

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.querydsl.jpa.hibernate.HibernateQuery; import com.querydsl.jpa.JPQLQuery;
@Singleton @Singleton
public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> { public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
@@ -44,13 +44,13 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
} }
public List<FeedSubscription> findAll(User user) { public List<FeedSubscription> findAll(User user) {
List<FeedSubscription> subs = query().selectFrom(sub).where(sub.user.eq(user)).leftJoin(sub.feed).fetchJoin() List<FeedSubscription> subs = query().selectFrom(sub).where(sub.user.eq(user)).leftJoin(sub.feed).fetchJoin().leftJoin(sub.category)
.leftJoin(sub.category).fetchJoin().fetch(); .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<FeedSubscription> query = query().selectFrom(sub).where(sub.user.eq(user)); JPQLQuery<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 {

View File

@@ -3,22 +3,24 @@ package com.commafeed.backend.dao;
import java.util.Collection; import java.util.Collection;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.annotations.QueryHints;
import com.commafeed.backend.model.AbstractModel; import com.commafeed.backend.model.AbstractModel;
import com.querydsl.jpa.hibernate.HibernateQueryFactory; import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import io.dropwizard.hibernate.AbstractDAO; 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; private JPAQueryFactory factory;
protected GenericDAO(SessionFactory sessionFactory) { protected GenericDAO(SessionFactory sessionFactory) {
super(sessionFactory); super(sessionFactory);
this.factory = new HibernateQueryFactory(() -> currentSession()); this.factory = new JPAQueryFactory(() -> currentSession());
} }
protected HibernateQueryFactory query() { protected JPAQueryFactory query() {
return factory; return factory;
} }
@@ -49,4 +51,10 @@ public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T>
return objects.size(); return objects.size();
} }
protected void setTimeout(JPAQuery<?> query, int timeoutMs) {
if (timeoutMs > 0) {
query.setHint(QueryHints.TIMEOUT_JPA, timeoutMs);
}
}
} }

View File

@@ -2,26 +2,24 @@ package com.commafeed.backend.feed;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
import java.util.Set;
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.commons.codec.binary.StringUtils; import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.client.ClientProtocolException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import com.commafeed.backend.HttpGetter; import com.commafeed.backend.HttpGetter;
import com.commafeed.backend.HttpGetter.HttpResult; import com.commafeed.backend.HttpGetter.HttpResult;
import com.commafeed.backend.HttpGetter.NotModifiedException; import com.commafeed.backend.HttpGetter.NotModifiedException;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import com.commafeed.backend.urlprovider.FeedURLProvider;
import com.rometools.rome.io.FeedException; import com.rometools.rome.io.FeedException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }))
@Singleton @Singleton
@@ -29,9 +27,10 @@ public class FeedFetcher {
private final FeedParser parser; private final FeedParser parser;
private final HttpGetter getter; private final HttpGetter getter;
private final Set<FeedURLProvider> urlProviders;
public FetchedFeed fetch(String feedUrl, boolean extractFeedUrlFromHtml, String lastModified, String eTag, Date lastPublishedDate, public FetchedFeed fetch(String feedUrl, boolean extractFeedUrlFromHtml, String lastModified, String eTag, Date lastPublishedDate,
String lastContentHash) throws FeedException, ClientProtocolException, IOException, NotModifiedException { String lastContentHash) throws FeedException, IOException, NotModifiedException {
log.debug("Fetching feed {}", feedUrl); log.debug("Fetching feed {}", feedUrl);
FetchedFeed fetchedFeed = null; FetchedFeed fetchedFeed = null;
@@ -44,7 +43,7 @@ public class FeedFetcher {
fetchedFeed = parser.parse(result.getUrlAfterRedirect(), content); fetchedFeed = parser.parse(result.getUrlAfterRedirect(), content);
} catch (FeedException e) { } catch (FeedException e) {
if (extractFeedUrlFromHtml) { if (extractFeedUrlFromHtml) {
String extractedUrl = extractFeedUrl(StringUtils.newStringUtf8(result.getContent()), feedUrl); String extractedUrl = extractFeedUrl(urlProviders, feedUrl, StringUtils.newStringUtf8(result.getContent()));
if (org.apache.commons.lang3.StringUtils.isNotBlank(extractedUrl)) { if (org.apache.commons.lang3.StringUtils.isNotBlank(extractedUrl)) {
feedUrl = extractedUrl; feedUrl = extractedUrl;
@@ -84,20 +83,13 @@ public class FeedFetcher {
return fetchedFeed; return fetchedFeed;
} }
private String extractFeedUrl(String html, String baseUri) { private static String extractFeedUrl(Set<FeedURLProvider> urlProviders, String url, String urlContent) {
String foundUrl = null; for (FeedURLProvider urlProvider : urlProviders) {
String feedUrl = urlProvider.get(url, urlContent);
Document doc = Jsoup.parse(html, baseUri); if (feedUrl != null)
String root = doc.children().get(0).tagName(); return feedUrl;
if ("html".equals(root)) {
Elements atom = doc.select("link[type=application/atom+xml]");
Elements rss = doc.select("link[type=application/rss+xml]");
if (!atom.isEmpty()) {
foundUrl = atom.get(0).attr("abs:href");
} else if (!rss.isEmpty()) {
foundUrl = rss.get(0).attr("abs:href");
}
} }
return foundUrl;
return null;
} }
} }

View File

@@ -6,6 +6,7 @@ import java.net.URL;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
@@ -13,6 +14,9 @@ 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 org.ahocorasick.trie.Emit;
import org.ahocorasick.trie.Trie;
import org.ahocorasick.trie.Trie.TrieBuilder;
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;
@@ -132,7 +136,32 @@ public class FeedUtils {
} }
public static String replaceHtmlEntitiesWithNumericEntities(String source) { public static String replaceHtmlEntitiesWithNumericEntities(String source) {
return StringUtils.replaceEach(source, HtmlEntities.HTML_ENTITIES, HtmlEntities.NUMERIC_ENTITIES); // Create a buffer sufficiently large that re-allocations are minimized.
StringBuilder sb = new StringBuilder(source.length() << 1);
TrieBuilder builder = Trie.builder();
builder.ignoreOverlaps();
for (String key : HtmlEntities.HTML_ENTITIES) {
builder.addKeyword(key);
}
Trie trie = builder.build();
Collection<Emit> emits = trie.parseText(source);
int prevIndex = 0;
for (Emit emit : emits) {
int matchIndex = emit.getStart();
sb.append(source.substring(prevIndex, matchIndex));
sb.append(HtmlEntities.HTML_TO_NUMERIC_MAP.get(emit.getKeyword()));
prevIndex = emit.getEnd() + 1;
}
// Add the remainder of the string (contains no more matches).
sb.append(source.substring(prevIndex));
return sb.toString();
} }
/** /**

View File

@@ -1,9 +1,11 @@
package com.commafeed.backend.feed; package com.commafeed.backend.feed;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
public class HtmlEntities { public class HtmlEntities {
public static final Map<String, String> HTML_TO_NUMERIC_MAP;
public static final String[] HTML_ENTITIES; public static final String[] HTML_ENTITIES;
public static final String[] NUMERIC_ENTITIES; public static final String[] NUMERIC_ENTITIES;
@@ -260,6 +262,7 @@ public class HtmlEntities {
map.put("&zwj;", "&#8205;"); map.put("&zwj;", "&#8205;");
map.put("&zwnj;", "&#8204;"); map.put("&zwnj;", "&#8204;");
HTML_TO_NUMERIC_MAP = Collections.unmodifiableMap(map);
HTML_ENTITIES = map.keySet().toArray(new String[map.size()]); HTML_ENTITIES = map.keySet().toArray(new String[map.size()]);
NUMERIC_ENTITIES = map.values().toArray(new String[map.size()]); NUMERIC_ENTITIES = map.values().toArray(new String[map.size()]);
} }

View File

@@ -8,11 +8,11 @@ import javax.persistence.Lob;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
import org.hibernate.annotations.Type;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.hibernate.annotations.Type;
@Entity @Entity
@Table(name = "FEEDENTRYCONTENTS") @Table(name = "FEEDENTRYCONTENTS")
@SuppressWarnings("serial") @SuppressWarnings("serial")
@@ -28,7 +28,7 @@ public class FeedEntryContent extends AbstractModel {
@Lob @Lob
@Column(length = Integer.MAX_VALUE) @Column(length = Integer.MAX_VALUE)
@Type(type = "org.hibernate.type.StringClobType") @Type(type = "org.hibernate.type.TextType")
private String content; private String content;
@Column(length = 40) @Column(length = 40)

View File

@@ -10,11 +10,11 @@ import javax.persistence.Lob;
import javax.persistence.OneToOne; import javax.persistence.OneToOne;
import javax.persistence.Table; import javax.persistence.Table;
import org.hibernate.annotations.Type;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.hibernate.annotations.Type;
@Entity @Entity
@Table(name = "USERSETTINGS") @Table(name = "USERSETTINGS")
@SuppressWarnings("serial") @SuppressWarnings("serial")
@@ -61,7 +61,7 @@ public class UserSettings extends AbstractModel {
@Lob @Lob
@Column(length = Integer.MAX_VALUE) @Column(length = Integer.MAX_VALUE)
@Type(type = "org.hibernate.type.StringClobType") @Type(type = "org.hibernate.type.TextType")
private String customCss; private String customCss;
@Column(name = "scroll_speed") @Column(name = "scroll_speed")

View File

@@ -8,18 +8,19 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.ObjectUtils;
import com.commafeed.backend.dao.FeedCategoryDAO; import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO; import com.commafeed.backend.dao.FeedSubscriptionDAO;
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.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.google.common.base.MoreObjects;
import com.rometools.opml.feed.opml.Attribute; import com.rometools.opml.feed.opml.Attribute;
import com.rometools.opml.feed.opml.Opml; import com.rometools.opml.feed.opml.Opml;
import com.rometools.opml.feed.opml.Outline; import com.rometools.opml.feed.opml.Outline;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }))
@Singleton @Singleton
public class OPMLExporter { public class OPMLExporter {
@@ -35,11 +36,11 @@ public class OPMLExporter {
List<FeedCategory> categories = feedCategoryDAO.findAll(user); List<FeedCategory> categories = feedCategoryDAO.findAll(user);
Collections.sort(categories, Collections.sort(categories,
(e1, e2) -> MoreObjects.firstNonNull(e1.getPosition(), 0) - MoreObjects.firstNonNull(e2.getPosition(), 0)); (e1, e2) -> ObjectUtils.firstNonNull(e1.getPosition(), 0) - ObjectUtils.firstNonNull(e2.getPosition(), 0));
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findAll(user); List<FeedSubscription> subscriptions = feedSubscriptionDAO.findAll(user);
Collections.sort(subscriptions, Collections.sort(subscriptions,
(e1, e2) -> MoreObjects.firstNonNull(e1.getPosition(), 0) - MoreObjects.firstNonNull(e2.getPosition(), 0)); (e1, e2) -> ObjectUtils.firstNonNull(e1.getPosition(), 0) - ObjectUtils.firstNonNull(e2.getPosition(), 0));
// export root categories // export root categories
for (FeedCategory cat : categories.stream().filter(c -> c.getParent() == null).collect(Collectors.toList())) { for (FeedCategory cat : categories.stream().filter(c -> c.getParent() == null).collect(Collectors.toList())) {

View File

@@ -1,5 +1,6 @@
package com.commafeed.backend.service; package com.commafeed.backend.service;
import java.time.Year;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -91,6 +92,8 @@ public class FeedEntryFilteringService {
context.set("url", entry.getUrl() == null ? "" : entry.getUrl().toLowerCase()); context.set("url", entry.getUrl() == null ? "" : entry.getUrl().toLowerCase());
context.set("categories", entry.getContent().getCategories() == null ? "" : entry.getContent().getCategories().toLowerCase()); context.set("categories", entry.getContent().getCategories() == null ? "" : entry.getContent().getCategories().toLowerCase());
context.set("year", Year.now().getValue());
Callable<Object> callable = script.callable(context); Callable<Object> callable = script.callable(context);
Future<Object> future = executor.submit(callable); Future<Object> future = executor.submit(callable);
Object result = null; Object result = null;

View File

@@ -0,0 +1,10 @@
package com.commafeed.backend.urlprovider;
/**
* Tries to find a feed url given the url and page content
*/
public interface FeedURLProvider {
String get(String url, String urlContent);
}

View File

@@ -0,0 +1,28 @@
package com.commafeed.backend.urlprovider;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class InPageReferenceFeedURLProvider implements FeedURLProvider {
@Override
public String get(String url, String urlContent) {
String foundUrl = null;
Document doc = Jsoup.parse(urlContent, url);
String root = doc.children().get(0).tagName();
if ("html".equals(root)) {
Elements atom = doc.select("link[type=application/atom+xml]");
Elements rss = doc.select("link[type=application/rss+xml]");
if (!atom.isEmpty()) {
foundUrl = atom.get(0).attr("abs:href");
} else if (!rss.isEmpty()) {
foundUrl = rss.get(0).attr("abs:href");
}
}
return foundUrl;
}
}

View File

@@ -0,0 +1,22 @@
package com.commafeed.backend.urlprovider;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Workaround for Youtube channels
*
* converts the channel URL https://www.youtube.com/channel/CHANNEL_ID to the valid feed URL
* https://www.youtube.com/feeds/videos.xml?channel_id=CHANNEL_ID
*/
public class YoutubeFeedURLProvider implements FeedURLProvider {
private static final Pattern REGEXP = Pattern.compile("(.*\\byoutube\\.com)\\/channel\\/([^\\/]+)", Pattern.CASE_INSENSITIVE);
@Override
public String get(String url, String urlContent) {
Matcher matcher = REGEXP.matcher(url);
return matcher.find() ? matcher.group(1) + "/feeds/videos.xml?channel_id=" + matcher.group(2) : null;
}
}

View File

@@ -9,28 +9,28 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Entry details") @ApiModel(description = "Entry details")
@Data @Data
public class Category implements Serializable { public class Category implements Serializable {
@ApiModelProperty("category id") @ApiModelProperty(value = "category id", required = true)
private String id; private String id;
@ApiModelProperty("parent category id") @ApiModelProperty(value = "parent category id")
private String parentId; private String parentId;
@ApiModelProperty("category id") @ApiModelProperty(value = "category id", required = true)
private String name; private String name;
@ApiModelProperty("category children categories") @ApiModelProperty(value = "category children categories", required = true)
private List<Category> children = new ArrayList<>(); private List<Category> children = new ArrayList<>();
@ApiModelProperty("category feeds") @ApiModelProperty(value = "category feeds", required = true)
private List<Subscription> feeds = new ArrayList<>(); private List<Subscription> feeds = new ArrayList<>();
@ApiModelProperty("wether the category is expanded or collapsed") @ApiModelProperty(value = "wether the category is expanded or collapsed", required = true)
private boolean expanded; private boolean expanded;
@ApiModelProperty("position of the category in the list") @ApiModelProperty(value = "position of the category in the list", required = true)
private Integer position; private Integer position;
} }

View File

@@ -9,38 +9,40 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("List of entries with some metadata") @ApiModel(description = "List of entries with some metadata")
@Data @Data
public class Entries implements Serializable { public class Entries implements Serializable {
@ApiModelProperty("name of the feed or the category requested") @ApiModelProperty(value = "name of the feed or the category requested", required = true)
private String name; private String name;
@ApiModelProperty("error or warning message") @ApiModelProperty(value = "error or warning message")
private String message; private String message;
@ApiModelProperty("times the server tried to refresh the feed and failed") @ApiModelProperty(value = "times the server tried to refresh the feed and failed", required = true)
private int errorCount; private int errorCount;
@ApiModelProperty("URL of the website, extracted from the feed") @ApiModelProperty(value = "URL of the website, extracted from the feed", required = true)
private String feedLink; private String feedLink;
@ApiModelProperty("list generation timestamp") @ApiModelProperty(value = "list generation timestamp", required = true)
private long timestamp; private long timestamp;
@ApiModelProperty("if the query has more elements") @ApiModelProperty(value = "if the query has more elements", required = true)
private boolean hasMore; private boolean hasMore;
@ApiModelProperty("the requested offset") @ApiModelProperty(value = "the requested offset")
private int offset; private int offset;
@ApiModelProperty("the requested limit") @ApiModelProperty(value = "the requested limit")
private int limit; private int limit;
@ApiModelProperty("list of entries") @ApiModelProperty(value = "list of entries", required = true)
private List<Entry> entries = new ArrayList<>(); private List<Entry> entries = new ArrayList<>();
@ApiModelProperty("if true, the unread flag was ignored in the request, all entries are returned regardless of their read status") @ApiModelProperty(
value = "if true, the unread flag was ignored in the request, all entries are returned regardless of their read status",
required = true)
private boolean ignoredReadStatus; private boolean ignoredReadStatus;
} }

View File

@@ -25,7 +25,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Entry details") @ApiModel(description = "Entry details")
@Data @Data
public class Entry implements Serializable { public class Entry implements Serializable {
@@ -58,7 +58,8 @@ public class Entry implements Serializable {
entry.setAuthor(content.getAuthor()); entry.setAuthor(content.getAuthor());
entry.setEnclosureType(content.getEnclosureType()); entry.setEnclosureType(content.getEnclosureType());
entry.setEnclosureUrl(proxyImages && StringUtils.contains(content.getEnclosureType(), "image") entry.setEnclosureUrl(proxyImages && StringUtils.contains(content.getEnclosureType(), "image")
? FeedUtils.proxyImage(content.getEnclosureUrl(), publicUrl) : content.getEnclosureUrl()); ? FeedUtils.proxyImage(content.getEnclosureUrl(), publicUrl)
: content.getEnclosureUrl());
entry.setCategories(content.getCategories()); entry.setCategories(content.getCategories());
} }
@@ -70,6 +71,7 @@ public class Entry implements Serializable {
entry.setUri(getGuid()); entry.setUri(getGuid());
entry.setTitle(getTitle()); entry.setTitle(getTitle());
entry.setAuthor(getAuthor());
SyndContentImpl content = new SyndContentImpl(); SyndContentImpl content = new SyndContentImpl();
content.setValue(getContent()); content.setValue(getContent());
@@ -87,66 +89,66 @@ public class Entry implements Serializable {
return entry; return entry;
} }
@ApiModelProperty("entry id") @ApiModelProperty(value = "entry id", required = true)
private String id; private String id;
@ApiModelProperty("entry guid") @ApiModelProperty(value = "entry guid", required = true)
private String guid; private String guid;
@ApiModelProperty("entry title") @ApiModelProperty(value = "entry title", required = true)
private String title; private String title;
@ApiModelProperty("entry content") @ApiModelProperty(value = "entry content", required = true)
private String content; private String content;
@ApiModelProperty("comma-separated list of categories") @ApiModelProperty(value = "comma-separated list of categories")
private String categories; private String categories;
@ApiModelProperty("wether entry content and title are rtl") @ApiModelProperty(value = "wether entry content and title are rtl", required = true)
private boolean rtl; private boolean rtl;
@ApiModelProperty("entry author") @ApiModelProperty(value = "entry author")
private String author; private String author;
@ApiModelProperty("entry enclosure url, if any") @ApiModelProperty(value = "entry enclosure url, if any")
private String enclosureUrl; private String enclosureUrl;
@ApiModelProperty("entry enclosure mime type, if any") @ApiModelProperty(value = "entry enclosure mime type, if any")
private String enclosureType; private String enclosureType;
@ApiModelProperty("entry publication date") @ApiModelProperty(value = "entry publication date", dataType = "number", required = true)
private Date date; private Date date;
@ApiModelProperty("entry insertion date in the database") @ApiModelProperty(value = "entry insertion date in the database", dataType = "number", required = true)
private Date insertedDate; private Date insertedDate;
@ApiModelProperty("feed id") @ApiModelProperty(value = "feed id", required = true)
private String feedId; private String feedId;
@ApiModelProperty("feed name") @ApiModelProperty(value = "feed name", required = true)
private String feedName; private String feedName;
@ApiModelProperty("this entry's feed url") @ApiModelProperty(value = "this entry's feed url", required = true)
private String feedUrl; private String feedUrl;
@ApiModelProperty("this entry's website url") @ApiModelProperty(value = "this entry's website url", required = true)
private String feedLink; private String feedLink;
@ApiModelProperty(value = "The favicon url to use for this feed") @ApiModelProperty(value = "The favicon url to use for this feed", required = true)
private String iconUrl; private String iconUrl;
@ApiModelProperty("entry url") @ApiModelProperty(value = "entry url", required = true)
private String url; private String url;
@ApiModelProperty("read sttaus") @ApiModelProperty(value = "read status", required = true)
private boolean read; private boolean read;
@ApiModelProperty("starred status") @ApiModelProperty(value = "starred status", required = true)
private boolean starred; private boolean starred;
@ApiModelProperty("wether the entry is still markable (old entry statuses are discarded)") @ApiModelProperty(value = "wether the entry is still markable (old entry statuses are discarded)", required = true)
private boolean markable; private boolean markable;
@ApiModelProperty("tags") @ApiModelProperty(value = "tags", required = true)
private List<String> tags; private List<String> tags;
} }

View File

@@ -3,14 +3,18 @@ package com.commafeed.frontend.model;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Feed details") @ApiModel(description = "Feed details")
@Data @Data
public class FeedInfo implements Serializable { public class FeedInfo implements Serializable {
@ApiModelProperty(value = "url", required = true)
private String url; private String url;
@ApiModelProperty(value = "title", required = true)
private String title; private String title;
} }

View File

@@ -3,18 +3,30 @@ package com.commafeed.frontend.model;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Server infos") @ApiModel(description = "Server infos")
@Data @Data
public class ServerInfo implements Serializable { public class ServerInfo implements Serializable {
@ApiModelProperty
private String announcement; private String announcement;
@ApiModelProperty(required = true)
private String version; private String version;
@ApiModelProperty(required = true)
private String gitCommit; private String gitCommit;
@ApiModelProperty(required = true)
private boolean allowRegistrations; private boolean allowRegistrations;
@ApiModelProperty
private String googleAnalyticsCode; private String googleAnalyticsCode;
@ApiModelProperty(required = true)
private boolean smtpEnabled; private boolean smtpEnabled;
} }

View File

@@ -7,11 +7,11 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("User settings") @ApiModel(description = "User settings")
@Data @Data
public class Settings implements Serializable { public class Settings implements Serializable {
@ApiModelProperty(value = "user's preferred language, english if none") @ApiModelProperty(value = "user's preferred language, english if none", required = true)
private String language; private String language;
@ApiModelProperty(value = "user reads all entries or unread entries only", allowableValues = "all,unread", required = true) @ApiModelProperty(value = "user reads all entries or unread entries only", allowableValues = "all,unread", required = true)
@@ -35,18 +35,37 @@ public class Settings implements Serializable {
@ApiModelProperty(value = "user's custom css for the website") @ApiModelProperty(value = "user's custom css for the website")
private String customCss; private String customCss;
@ApiModelProperty(value = "user's preferred scroll speed when navigating between entries") @ApiModelProperty(value = "user's preferred scroll speed when navigating between entries", required = true)
private int scrollSpeed; private int scrollSpeed;
@ApiModelProperty(required = true)
private boolean email; private boolean email;
@ApiModelProperty(required = true)
private boolean gmail; private boolean gmail;
@ApiModelProperty(required = true)
private boolean facebook; private boolean facebook;
@ApiModelProperty(required = true)
private boolean twitter; private boolean twitter;
@ApiModelProperty(required = true)
private boolean googleplus; private boolean googleplus;
@ApiModelProperty(required = true)
private boolean tumblr; private boolean tumblr;
@ApiModelProperty(required = true)
private boolean pocket; private boolean pocket;
@ApiModelProperty(required = true)
private boolean instapaper; private boolean instapaper;
@ApiModelProperty(required = true)
private boolean buffer; private boolean buffer;
@ApiModelProperty(required = true)
private boolean readability; private boolean readability;
} }

View File

@@ -13,7 +13,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("User information") @ApiModel(description = "User information")
@Data @Data
public class Subscription implements Serializable { public class Subscription implements Serializable {
@@ -51,10 +51,13 @@ public class Subscription implements Serializable {
@ApiModelProperty(value = "error count", required = true) @ApiModelProperty(value = "error count", required = true)
private int errorCount; private int errorCount;
@ApiModelProperty(value = "last time the feed was refreshed", required = true) @ApiModelProperty(value = "last time the feed was refreshed", dataType = "number", required = true)
private Date lastRefresh; private Date lastRefresh;
@ApiModelProperty(value = "next time the feed refresh is planned, null if refresh is already queued", required = true) @ApiModelProperty(
value = "next time the feed refresh is planned, null if refresh is already queued",
dataType = "number",
required = true)
private Date nextRefresh; private Date nextRefresh;
@ApiModelProperty(value = "this subscription's feed url", required = true) @ApiModelProperty(value = "this subscription's feed url", required = true)
@@ -63,7 +66,7 @@ public class Subscription implements Serializable {
@ApiModelProperty(value = "this subscription's website url", required = true) @ApiModelProperty(value = "this subscription's website url", required = true)
private String feedLink; private String feedLink;
@ApiModelProperty(value = "The favicon url to use for this feed") @ApiModelProperty(value = "The favicon url to use for this feed", required = true)
private String iconUrl; private String iconUrl;
@ApiModelProperty(value = "unread count", required = true) @ApiModelProperty(value = "unread count", required = true)
@@ -75,7 +78,7 @@ public class Subscription implements Serializable {
@ApiModelProperty("position of the subscription's in the list") @ApiModelProperty("position of the subscription's in the list")
private Integer position; private Integer position;
@ApiModelProperty("date of the newest item") @ApiModelProperty(value = "date of the newest item", dataType = "number")
private Date newestItemTime; private Date newestItemTime;
@ApiModelProperty(value = "JEXL string evaluated on new entries to mark them as read if they do not match") @ApiModelProperty(value = "JEXL string evaluated on new entries to mark them as read if they do not match")

View File

@@ -4,15 +4,21 @@ import java.io.Serializable;
import java.util.Date; import java.util.Date;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Unread count") @ApiModel(description = "Unread count")
@Data @Data
public class UnreadCount implements Serializable { public class UnreadCount implements Serializable {
@ApiModelProperty
private long feedId; private long feedId;
@ApiModelProperty
private long unreadCount; private long unreadCount;
@ApiModelProperty(dataType = "number")
private Date newestItemTime; private Date newestItemTime;
public UnreadCount() { public UnreadCount() {

View File

@@ -8,7 +8,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("User information") @ApiModel(description = "User information")
@Data @Data
public class UserModel implements Serializable { public class UserModel implements Serializable {
@@ -27,16 +27,16 @@ public class UserModel implements Serializable {
@ApiModelProperty(value = "user password, never returned by the api") @ApiModelProperty(value = "user password, never returned by the api")
private String password; private String password;
@ApiModelProperty(value = "account status") @ApiModelProperty(value = "account status", required = true)
private boolean enabled; private boolean enabled;
@ApiModelProperty(value = "account creation date") @ApiModelProperty(value = "account creation date", dataType = "number", required = true)
private Date created; private Date created;
@ApiModelProperty(value = "last login date") @ApiModelProperty(value = "last login date", dataType = "number")
private Date lastLogin; private Date lastLogin;
@ApiModelProperty(value = "user is admin") @ApiModelProperty(value = "user is admin", required = true)
private boolean admin; private boolean admin;
} }

View File

@@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Add Category Request") @ApiModel(description = "Add Category Request")
@Data @Data
public class AddCategoryRequest implements Serializable { public class AddCategoryRequest implements Serializable {

View File

@@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Category modification request") @ApiModel(description = "Category modification request")
@Data @Data
public class CategoryModificationRequest implements Serializable { public class CategoryModificationRequest implements Serializable {

View File

@@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Mark Request") @ApiModel(description = "Mark Request")
@Data @Data
public class CollapseRequest implements Serializable { public class CollapseRequest implements Serializable {

View File

@@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Feed information request") @ApiModel(description = "Feed information request")
@Data @Data
public class FeedInfoRequest implements Serializable { public class FeedInfoRequest implements Serializable {

View File

@@ -8,7 +8,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Feed merge Request") @ApiModel(description = "Feed merge Request")
@Data @Data
public class FeedMergeRequest implements Serializable { public class FeedMergeRequest implements Serializable {

View File

@@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Feed modification request") @ApiModel(description = "Feed modification request")
@Data @Data
public class FeedModificationRequest implements Serializable { public class FeedModificationRequest implements Serializable {

View File

@@ -11,7 +11,7 @@ import lombok.Data;
@Data @Data
public class IDRequest implements Serializable { public class IDRequest implements Serializable {
@ApiModelProperty @ApiModelProperty(required = true)
private Long id; private Long id;
} }

View File

@@ -8,14 +8,14 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Mark Request") @ApiModel(description = "Mark Request")
@Data @Data
public class MarkRequest implements Serializable { public class MarkRequest implements Serializable {
@ApiModelProperty(value = "entry id, category id, 'all' or 'starred'", required = true) @ApiModelProperty(value = "entry id, category id, 'all' or 'starred'", required = true)
private String id; private String id;
@ApiModelProperty(value = "mark as read or unread") @ApiModelProperty(value = "mark as read or unread", required = true)
private boolean read; private boolean read;
@ApiModelProperty( @ApiModelProperty(
@@ -25,7 +25,7 @@ public class MarkRequest implements Serializable {
@ApiModelProperty(value = "only mark read if a feed has these keywords in the title or rss content", required = false) @ApiModelProperty(value = "only mark read if a feed has these keywords in the title or rss content", required = false)
private String keywords; private String keywords;
@ApiModelProperty(value = "if marking a category or 'all', exclude those subscriptions from the marking", required = false) @ApiModelProperty(value = "if marking a category or 'all', exclude those subscriptions from the marking", required = false)
private List<Long> excludedSubscriptions; private List<Long> excludedSubscriptions;

View File

@@ -8,7 +8,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Multiple Mark Request") @ApiModel(description = "Multiple Mark Request")
@Data @Data
public class MultipleMarkRequest implements Serializable { public class MultipleMarkRequest implements Serializable {

View File

@@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Profile modification request") @ApiModel(description = "Profile modification request")
@Data @Data
public class ProfileModificationRequest implements Serializable { public class ProfileModificationRequest implements Serializable {

View File

@@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Star Request") @ApiModel(description = "Star Request")
@Data @Data
public class StarRequest implements Serializable { public class StarRequest implements Serializable {
@@ -17,7 +17,7 @@ public class StarRequest implements Serializable {
@ApiModelProperty(value = "feed id", required = true) @ApiModelProperty(value = "feed id", required = true)
private Long feedId; private Long feedId;
@ApiModelProperty(value = "starred or not") @ApiModelProperty(value = "starred or not", required = true)
private boolean starred; private boolean starred;
} }

View File

@@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Subscription request") @ApiModel(description = "Subscription request")
@Data @Data
public class SubscribeRequest implements Serializable { public class SubscribeRequest implements Serializable {

View File

@@ -8,14 +8,14 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ApiModel("Tag Request") @ApiModel(description = "Tag Request")
@Data @Data
public class TagRequest implements Serializable { public class TagRequest implements Serializable {
@ApiModelProperty(value = "entry id", required = true) @ApiModelProperty(value = "entry id", required = true)
private Long entryId; private Long entryId;
@ApiModelProperty(value = "tags") @ApiModelProperty(value = "tags", required = true)
private List<String> tags; private List<String> tags;
} }

View File

@@ -46,7 +46,7 @@ import lombok.RequiredArgsConstructor;
@Api(value = "/admin") @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 {
@@ -62,7 +62,8 @@ public class AdminREST {
@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 @Timed
public Response save(@SecurityCheck(Role.ADMIN) User user, @ApiParam(required = true) UserModel userModel) { public Response adminSaveUser(@ApiParam(hidden = true) @SecurityCheck(Role.ADMIN) User user,
@ApiParam(required = true) UserModel userModel) {
Preconditions.checkNotNull(userModel); Preconditions.checkNotNull(userModel);
Preconditions.checkNotNull(userModel.getName()); Preconditions.checkNotNull(userModel.getName());
@@ -117,7 +118,8 @@ public class AdminREST {
@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 @Timed
public Response getUser(@SecurityCheck(Role.ADMIN) User user, @ApiParam(value = "user id", required = true) @PathParam("id") Long id) { public Response adminGetUser(@ApiParam(hidden = true) @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);
UserModel userModel = new UserModel(); UserModel userModel = new UserModel();
@@ -134,7 +136,7 @@ public class AdminREST {
@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 @Timed
public Response getUsers(@SecurityCheck(Role.ADMIN) User user) { public Response adminGetUsers(@ApiParam(hidden = true) @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()) {
User u = role.getUser(); User u = role.getUser();
@@ -162,7 +164,8 @@ public class AdminREST {
@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 @Timed
public Response delete(@SecurityCheck(Role.ADMIN) User user, @ApiParam(required = true) IDRequest req) { public Response adminDeleteUser(@ApiParam(hidden = true) @SecurityCheck(Role.ADMIN) User user,
@ApiParam(required = true) IDRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -182,7 +185,7 @@ public class AdminREST {
@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 @Timed
public Response getSettings(@SecurityCheck(Role.ADMIN) User user) { public Response getApplicationSettings(@ApiParam(hidden = true) @SecurityCheck(Role.ADMIN) User user) {
return Response.ok(config.getApplicationSettings()).build(); return Response.ok(config.getApplicationSettings()).build();
} }
@@ -191,7 +194,7 @@ public class AdminREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Retrieve server metrics") @ApiOperation(value = "Retrieve server metrics")
@Timed @Timed
public Response getMetrics(@SecurityCheck(Role.ADMIN) User user) { public Response getMetrics(@ApiParam(hidden = true) @SecurityCheck(Role.ADMIN) User user) {
return Response.ok(metrics).build(); return Response.ok(metrics).build();
} }

View File

@@ -74,7 +74,7 @@ import lombok.extern.slf4j.Slf4j;
@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 {
@@ -94,7 +94,7 @@ public class CategoryREST {
@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)
@Timed @Timed
public Response getCategoryEntries(@SecurityCheck User user, public Response getCategoryEntries(@ApiParam(hidden = true) @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( @ApiParam(
value = "all entries or only unread ones", value = "all entries or only unread ones",
@@ -103,7 +103,9 @@ public class CategoryREST {
@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,
@ApiParam(value = "ordering", allowableValues = "asc,desc,abc,zyx") @QueryParam("order") @DefaultValue("desc") ReadingOrder order, @ApiParam(
value = "ordering",
allowableValues = "asc,desc,abc,zyx") @QueryParam("order") @DefaultValue("desc") ReadingOrder order,
@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,
@@ -191,7 +193,7 @@ public class CategoryREST {
@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)
@Timed @Timed
public Response getCategoryEntriesAsFeed(@SecurityCheck(apiKeyAllowed = true) User user, public Response getCategoryEntriesAsFeed(@ApiParam(hidden = true) @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( @ApiParam(
value = "all entries or only unread ones", value = "all entries or only unread ones",
@@ -238,7 +240,7 @@ public class CategoryREST {
@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 @Timed
public Response markCategoryEntries(@SecurityCheck User user, public Response markCategoryEntries(@ApiParam(hidden = true) @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);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -280,7 +282,7 @@ public class CategoryREST {
@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 @Timed
public Response addCategory(@SecurityCheck User user, @ApiParam(required = true) AddCategoryRequest req) { public Response addCategory(@ApiParam(hidden = true) @SecurityCheck User user, @ApiParam(required = true) AddCategoryRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getName()); Preconditions.checkNotNull(req.getName());
@@ -304,7 +306,7 @@ public class CategoryREST {
@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 @Timed
public Response deleteCategory(@SecurityCheck User user, @ApiParam(required = true) IDRequest req) { public Response deleteCategory(@ApiParam(hidden = true) @SecurityCheck User user, @ApiParam(required = true) IDRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -337,7 +339,8 @@ public class CategoryREST {
@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 @Timed
public Response modifyCategory(@SecurityCheck User user, @ApiParam(required = true) CategoryModificationRequest req) { public Response modifyCategory(@ApiParam(hidden = true) @SecurityCheck User user,
@ApiParam(required = true) CategoryModificationRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -392,7 +395,7 @@ public class CategoryREST {
@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 @Timed
public Response collapse(@SecurityCheck User user, @ApiParam(required = true) CollapseRequest req) { public Response collapseCategory(@ApiParam(hidden = true) @SecurityCheck User user, @ApiParam(required = true) CollapseRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -411,7 +414,7 @@ public class CategoryREST {
@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 @Timed
public Response getUnreadCount(@SecurityCheck User user) { public Response getUnreadCount(@ApiParam(hidden = true) @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();
} }
@@ -419,9 +422,9 @@ public class CategoryREST {
@GET @GET
@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 root category", notes = "Get all categories and subscriptions of the user", response = Category.class)
@Timed @Timed
public Response getSubscriptions(@SecurityCheck User user) { public Response getRootCategory(@ApiParam(hidden = true) @SecurityCheck User user) {
Category root = cache.getUserRootCategory(user); Category root = cache.getUserRootCategory(user);
if (root == null) { if (root == null) {
log.debug("tree cache miss for {}", user.getId()); log.debug("tree cache miss for {}", user.getId());

View File

@@ -34,7 +34,7 @@ import lombok.RequiredArgsConstructor;
@Api(value = "/entry") @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 {
@@ -47,7 +47,8 @@ public class EntryREST {
@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 @Timed
public Response markFeedEntry(@SecurityCheck User user, @ApiParam(value = "Mark Request", required = true) MarkRequest req) { public Response markEntry(@ApiParam(hidden = true) @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());
@@ -60,13 +61,13 @@ public class EntryREST {
@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 @Timed
public Response markFeedEntries(@SecurityCheck User user, public Response markEntries(@ApiParam(hidden = true) @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);
Preconditions.checkNotNull(req.getRequests()); Preconditions.checkNotNull(req.getRequests());
for (MarkRequest r : req.getRequests()) { for (MarkRequest r : req.getRequests()) {
markFeedEntry(user, r); markEntry(user, r);
} }
return Response.ok().build(); return Response.ok().build();
@@ -77,7 +78,8 @@ public class EntryREST {
@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 @Timed
public Response starFeedEntry(@SecurityCheck User user, @ApiParam(value = "Star Request", required = true) StarRequest req) { public Response starEntry(@ApiParam(hidden = true) @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());
Preconditions.checkNotNull(req.getFeedId()); Preconditions.checkNotNull(req.getFeedId());
@@ -92,7 +94,7 @@ public class EntryREST {
@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 @Timed
public Response getTags(@SecurityCheck User user) { public Response getTags(@ApiParam(hidden = true) @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();
} }
@@ -102,7 +104,8 @@ public class EntryREST {
@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 @Timed
public Response tagFeedEntry(@SecurityCheck User user, @ApiParam(value = "Tag Request", required = true) TagRequest req) { public Response tagEntry(@ApiParam(hidden = true) @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

@@ -95,7 +95,7 @@ import lombok.extern.slf4j.Slf4j;
@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 {
@@ -132,7 +132,7 @@ public class FeedREST {
@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)
@Timed @Timed
public Response getFeedEntries(@SecurityCheck User user, public Response getFeedEntries(@ApiParam(hidden = true) @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( @ApiParam(
value = "all entries or only unread ones", value = "all entries or only unread ones",
@@ -141,7 +141,9 @@ public class FeedREST {
@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,
@ApiParam(value = "ordering", allowableValues = "asc,desc,abc,zyx") @QueryParam("order") @DefaultValue("desc") ReadingOrder order, @ApiParam(
value = "ordering",
allowableValues = "asc,desc,abc,zyx") @QueryParam("order") @DefaultValue("desc") ReadingOrder order,
@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) {
@@ -200,7 +202,7 @@ public class FeedREST {
@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)
@Timed @Timed
public Response getFeedEntriesAsFeed(@SecurityCheck(apiKeyAllowed = true) User user, public Response getFeedEntriesAsFeed(@ApiParam(hidden = true) @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( @ApiParam(
value = "all entries or only unread ones", value = "all entries or only unread ones",
@@ -260,7 +262,8 @@ public class FeedREST {
@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 @Timed
public Response fetchFeed(@SecurityCheck User user, @ApiParam(value = "feed url", required = true) FeedInfoRequest req) { public Response fetchFeed(@ApiParam(hidden = true) @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());
@@ -279,7 +282,7 @@ public class FeedREST {
@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 @Timed
public Response queueAllForRefresh(@SecurityCheck User user) { public Response queueAllForRefresh(@ApiParam(hidden = true) @SecurityCheck User user) {
feedSubscriptionService.refreshAll(user); feedSubscriptionService.refreshAll(user);
return Response.ok().build(); return Response.ok().build();
} }
@@ -289,7 +292,8 @@ public class FeedREST {
@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 @Timed
public Response queueForRefresh(@SecurityCheck User user, @ApiParam(value = "Feed id") IDRequest req) { public Response queueForRefresh(@ApiParam(hidden = true) @SecurityCheck User user,
@ApiParam(value = "Feed id", required = true) IDRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -308,7 +312,8 @@ public class FeedREST {
@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 @Timed
public Response markFeedEntries(@SecurityCheck User user, @ApiParam(value = "Mark request") MarkRequest req) { public Response markFeedEntries(@ApiParam(hidden = true) @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());
@@ -326,9 +331,10 @@ public class FeedREST {
@GET @GET
@Path("/get/{id}") @Path("/get/{id}")
@UnitOfWork @UnitOfWork
@ApiOperation(value = "", notes = "") @ApiOperation(value = "get feed", response = Subscription.class)
@Timed @Timed
public Response get(@SecurityCheck User user, @ApiParam(value = "user id", required = true) @PathParam("id") Long id) { public Response getFeed(@ApiParam(hidden = true) @SecurityCheck User user,
@ApiParam(value = "user id", required = true) @PathParam("id") Long id) {
Preconditions.checkNotNull(id); Preconditions.checkNotNull(id);
FeedSubscription sub = feedSubscriptionDAO.findById(user, id); FeedSubscription sub = feedSubscriptionDAO.findById(user, id);
@@ -344,7 +350,8 @@ public class FeedREST {
@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 @Timed
public Response getFavicon(@SecurityCheck User user, @ApiParam(value = "subscription id") @PathParam("id") Long id) { public Response getFeedFavicon(@ApiParam(hidden = true) @SecurityCheck User user,
@ApiParam(value = "subscription id", required = true) @PathParam("id") Long id) {
Preconditions.checkNotNull(id); Preconditions.checkNotNull(id);
FeedSubscription subscription = feedSubscriptionDAO.findById(user, id); FeedSubscription subscription = feedSubscriptionDAO.findById(user, id);
@@ -374,7 +381,8 @@ public class FeedREST {
@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 @Timed
public Response subscribe(@SecurityCheck User user, @ApiParam(value = "subscription request", required = true) SubscribeRequest req) { public Response subscribe(@ApiParam(hidden = true) @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());
Preconditions.checkNotNull(req.getUrl()); Preconditions.checkNotNull(req.getUrl());
@@ -401,7 +409,8 @@ public class FeedREST {
@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 @Timed
public Response subscribe(@SecurityCheck User user, @ApiParam(value = "feed url", required = true) @QueryParam("url") String url) { public Response subscribeFromUrl(@ApiParam(hidden = true) @SecurityCheck User user,
@ApiParam(value = "feed url", required = true) @QueryParam("url") String url) {
try { try {
Preconditions.checkNotNull(url); Preconditions.checkNotNull(url);
@@ -429,7 +438,7 @@ public class FeedREST {
@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 @Timed
public Response unsubscribe(@SecurityCheck User user, @ApiParam(required = true) IDRequest req) { public Response unsubscribe(@ApiParam(hidden = true) @SecurityCheck User user, @ApiParam(required = true) IDRequest req) {
Preconditions.checkNotNull(req); Preconditions.checkNotNull(req);
Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getId());
@@ -446,7 +455,8 @@ public class FeedREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Modify a subscription", notes = "Modify a feed subscription") @ApiOperation(value = "Modify a subscription", notes = "Modify a feed subscription")
@Timed @Timed
public Response modify(@SecurityCheck User user, @ApiParam(value = "subscription id", required = true) FeedModificationRequest req) { public Response modifyFeed(@ApiParam(hidden = true) @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());
@@ -506,7 +516,8 @@ public class FeedREST {
@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 @Timed
public Response importOpml(@SecurityCheck User user, @FormDataParam("file") InputStream input) { public Response importOpml(@ApiParam(hidden = true) @SecurityCheck User user,
@ApiParam(value = "ompl file", required = true) @FormDataParam("file") InputStream input) {
String publicUrl = config.getApplicationSettings().getPublicUrl(); String publicUrl = config.getApplicationSettings().getPublicUrl();
if (StringUtils.isBlank(publicUrl)) { if (StringUtils.isBlank(publicUrl)) {
@@ -533,7 +544,7 @@ public class FeedREST {
@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 @Timed
public Response exportOpml(@SecurityCheck User user) { public Response exportOpml(@ApiParam(hidden = true) @SecurityCheck User user) {
Opml opml = opmlExporter.export(user); Opml opml = opmlExporter.export(user);
WireFeedOutput output = new WireFeedOutput(); WireFeedOutput output = new WireFeedOutput();
String opmlString = null; String opmlString = null;

View File

@@ -25,13 +25,14 @@ import com.commafeed.frontend.model.ServerInfo;
import io.dropwizard.hibernate.UnitOfWork; import io.dropwizard.hibernate.UnitOfWork;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@Path("/server") @Path("/server")
@Api(value = "/server") @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 {
@@ -43,7 +44,7 @@ public class ServerREST {
@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 @Timed
public Response get() { public Response getServerInfos() {
ServerInfo infos = new ServerInfo(); ServerInfo infos = new ServerInfo();
infos.setAnnouncement(config.getApplicationSettings().getAnnouncement()); infos.setAnnouncement(config.getApplicationSettings().getAnnouncement());
infos.setVersion(config.getVersion()); infos.setVersion(config.getVersion());
@@ -60,7 +61,8 @@ public class ServerREST {
@ApiOperation(value = "proxy image") @ApiOperation(value = "proxy image")
@Produces("image/png") @Produces("image/png")
@Timed @Timed
public Response get(@SecurityCheck User user, @QueryParam("u") String url) { public Response getProxiedImage(@ApiParam(hidden = true) @SecurityCheck User user,
@ApiParam(value = "image url", required = true) @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

@@ -83,7 +83,7 @@ public class UserREST {
@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 @Timed
public Response getSettings(@SecurityCheck User user) { public Response getUserSettings(@ApiParam(hidden = true) @SecurityCheck User user) {
Settings s = new Settings(); Settings s = new Settings();
UserSettings settings = userSettingsDAO.findByUser(user); UserSettings settings = userSettingsDAO.findByUser(user);
if (settings != null) { if (settings != null) {
@@ -138,7 +138,7 @@ public class UserREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Save user settings", notes = "Save user settings") @ApiOperation(value = "Save user settings", notes = "Save user settings")
@Timed @Timed
public Response saveSettings(@SecurityCheck User user, @ApiParam(required = true) Settings settings) { public Response saveUserSettings(@ApiParam(hidden = true) @SecurityCheck User user, @ApiParam(required = true) Settings settings) {
Preconditions.checkNotNull(settings); Preconditions.checkNotNull(settings);
UserSettings s = userSettingsDAO.findByUser(user); UserSettings s = userSettingsDAO.findByUser(user);
@@ -177,7 +177,7 @@ public class UserREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Retrieve user's profile", response = UserModel.class) @ApiOperation(value = "Retrieve user's profile", response = UserModel.class)
@Timed @Timed
public Response get(@SecurityCheck User user) { public Response getUserProfile(@ApiParam(hidden = true) @SecurityCheck User user) {
UserModel userModel = new UserModel(); UserModel userModel = new UserModel();
userModel.setId(user.getId()); userModel.setId(user.getId());
userModel.setName(user.getName()); userModel.setName(user.getName());
@@ -197,7 +197,8 @@ public class UserREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Save user's profile") @ApiOperation(value = "Save user's profile")
@Timed @Timed
public Response save(@SecurityCheck User user, @ApiParam(required = true) ProfileModificationRequest request) { public Response saveUserProfile(@ApiParam(hidden = true) @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())) {
User u = userDAO.findByEmail(request.getEmail()); User u = userDAO.findByEmail(request.getEmail());
@@ -226,7 +227,8 @@ public class UserREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Register a new account") @ApiOperation(value = "Register a new account")
@Timed @Timed
public Response register(@Valid @ApiParam(required = true) RegistrationRequest req, @Context SessionHelper sessionHelper) { public Response registerUser(@Valid @ApiParam(required = true) RegistrationRequest req,
@Context @ApiParam(hidden = true) 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));
userService.login(req.getName(), req.getPassword()); userService.login(req.getName(), req.getPassword());
@@ -243,7 +245,7 @@ public class UserREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Login and create a session") @ApiOperation(value = "Login and create a session")
@Timed @Timed
public Response login(@ApiParam(required = true) LoginRequest req, @Context SessionHelper sessionHelper) { public Response login(@ApiParam(required = true) LoginRequest req, @ApiParam(hidden = true) @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()) {
sessionHelper.setLoggedInUser(user.get()); sessionHelper.setLoggedInUser(user.get());
@@ -258,7 +260,7 @@ public class UserREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "send a password reset email") @ApiOperation(value = "send a password reset email")
@Timed @Timed
public Response sendPasswordReset(@Valid PasswordResetRequest req) { public Response sendPasswordReset(@Valid @ApiParam(required = true) PasswordResetRequest req) {
User user = userDAO.findByEmail(req.getEmail()); User user = userDAO.findByEmail(req.getEmail());
if (user == null) { if (user == null) {
return Response.status(Status.PRECONDITION_FAILED).entity("Email not found.").type(MediaType.TEXT_PLAIN).build(); return Response.status(Status.PRECONDITION_FAILED).entity("Email not found.").type(MediaType.TEXT_PLAIN).build();
@@ -294,7 +296,8 @@ public class UserREST {
@UnitOfWork @UnitOfWork
@Produces(MediaType.TEXT_HTML) @Produces(MediaType.TEXT_HTML)
@Timed @Timed
public Response passwordRecoveryCallback(@QueryParam("email") String email, @QueryParam("token") String token) { public Response passwordRecoveryCallback(@ApiParam(required = true) @QueryParam("email") String email,
@ApiParam(required = true) @QueryParam("token") String token) {
Preconditions.checkNotNull(email); Preconditions.checkNotNull(email);
Preconditions.checkNotNull(token); Preconditions.checkNotNull(token);
@@ -330,7 +333,7 @@ public class UserREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Delete the user account") @ApiOperation(value = "Delete the user account")
@Timed @Timed
public Response delete(@SecurityCheck User user) { public Response deleteUser(@ApiParam(hidden = true) @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

@@ -0,0 +1,48 @@
package com.commafeed.frontend.session;
import java.io.File;
import javax.servlet.SessionTrackingMode;
import org.eclipse.jetty.server.session.DefaultSessionCache;
import org.eclipse.jetty.server.session.FileSessionDataStore;
import org.eclipse.jetty.server.session.SessionCache;
import org.eclipse.jetty.server.session.SessionHandler;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.util.Duration;
public class SessionHandlerFactory {
private String path = "sessions";
private Duration cookieMaxAge = Duration.days(30);
private Duration cookieRefreshAge = Duration.days(1);
private Duration maxInactiveInterval = Duration.days(30);
private Duration savePeriod = Duration.minutes(5);
public SessionHandler build() {
SessionHandler sessionHandler = new SessionHandler() {
{
// no setter available for maxCookieAge
_maxCookieAge = (int) cookieMaxAge.toSeconds();
}
};
SessionCache sessionCache = new DefaultSessionCache(sessionHandler);
sessionHandler.setSessionCache(sessionCache);
FileSessionDataStore dataStore = new FileSessionDataStore();
sessionCache.setSessionDataStore(dataStore);
sessionHandler.setHttpOnly(true);
sessionHandler.setSessionTrackingModes(ImmutableSet.of(SessionTrackingMode.COOKIE));
sessionHandler.setMaxInactiveInterval((int) maxInactiveInterval.toSeconds());
sessionHandler.setRefreshCookieAge((int) cookieRefreshAge.toSeconds());
dataStore.setDeleteUnrestorableFiles(true);
dataStore.setStoreDir(new File(path));
dataStore.setSavePeriodSec((int) savePeriod.toSeconds());
return sessionHandler;
}
}

View File

@@ -1,45 +0,0 @@
package com.commafeed.frontend.session;
import io.dropwizard.util.Duration;
import java.io.File;
import java.io.IOException;
import javax.servlet.SessionTrackingMode;
import lombok.Getter;
import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.session.HashSessionManager;
import com.google.common.collect.ImmutableSet;
@Getter
public class SessionManagerFactory {
private String path = "sessions";
private Duration cookieMaxAge = Duration.days(30);
private Duration cookieRefreshAge = Duration.days(1);
private Duration maxInactiveInterval = Duration.days(30);
private Duration idleSavePeriod = Duration.hours(2);
private Duration savePeriod = Duration.minutes(5);
private Duration scavengePeriod = Duration.minutes(5);
public SessionManager build() throws IOException {
HashSessionManager manager = new HashSessionManager();
manager.setSessionTrackingModes(ImmutableSet.of(SessionTrackingMode.COOKIE));
manager.setHttpOnly(true);
manager.getSessionCookieConfig().setHttpOnly(true);
manager.setDeleteUnrestorableSessions(true);
manager.setStoreDirectory(new File(getPath()));
manager.getSessionCookieConfig().setMaxAge((int) cookieMaxAge.toSeconds());
manager.setRefreshCookieAge((int) cookieRefreshAge.toSeconds());
manager.setMaxInactiveInterval((int) maxInactiveInterval.toSeconds());
manager.setIdleSavePeriod((int) idleSavePeriod.toSeconds());
manager.setSavePeriod((int) savePeriod.toSeconds());
manager.setScavengePeriod((int) scavengePeriod.toSeconds());
return manager;
}
}

View File

@@ -6,6 +6,7 @@
<changeSet author="athou" id="create-app-settings"> <changeSet author="athou" id="create-app-settings">
<validCheckSum>7:6d3ad493d25dd9c50067e804efc9ffcc</validCheckSum> <validCheckSum>7:6d3ad493d25dd9c50067e804efc9ffcc</validCheckSum>
<validCheckSum>7:896a68c1651397288c40f717ce0397b4</validCheckSum> <validCheckSum>7:896a68c1651397288c40f717ce0397b4</validCheckSum>
<validCheckSum>8:1b0879c4739d483c3b1d779e08fe770b</validCheckSum>
<preConditions onFail="MARK_RAN" onFailMessage="existing table skipping"> <preConditions onFail="MARK_RAN" onFailMessage="existing table skipping">
<not> <not>
<tableExists tableName="APPLICATIONSETTINGS" /> <tableExists tableName="APPLICATIONSETTINGS" />
@@ -15,7 +16,7 @@
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" /> <constraints nullable="false" primaryKey="true" />
</column> </column>
<column name="allowRegistrations" type="BIT"> <column name="allowRegistrations" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="backgroundThreads" type="INT"> <column name="backgroundThreads" type="INT">
@@ -29,20 +30,20 @@
<column name="smtpPort" type="INT"> <column name="smtpPort" type="INT">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="smtpTls" type="BIT"> <column name="smtpTls" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="smtpUserName" type="VARCHAR(255)" /> <column name="smtpUserName" type="VARCHAR(255)" />
<column name="googleAnalyticsDomainName" type="VARCHAR(255)" /> <column name="googleAnalyticsDomainName" type="VARCHAR(255)" />
<column name="googleAnalyticsTrackingCode" type="VARCHAR(255)" /> <column name="googleAnalyticsTrackingCode" type="VARCHAR(255)" />
<column name="announcement" type="VARCHAR(255)" /> <column name="announcement" type="VARCHAR(255)" />
<column name="feedbackButton" type="BIT"> <column name="feedbackButton" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="heavyLoad" type="BIT"> <column name="heavyLoad" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="pubsubhubbub" type="BIT"> <column name="pubsubhubbub" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="databaseUpdateThreads" type="INT"> <column name="databaseUpdateThreads" type="INT">
@@ -79,6 +80,7 @@
<changeSet author="athou" id="create-cat"> <changeSet author="athou" id="create-cat">
<validCheckSum>7:93155e15f0feabe936e1de35711bf85b</validCheckSum> <validCheckSum>7:93155e15f0feabe936e1de35711bf85b</validCheckSum>
<validCheckSum>7:c52f258e54d34156208cbfd2d8547fbd</validCheckSum> <validCheckSum>7:c52f258e54d34156208cbfd2d8547fbd</validCheckSum>
<validCheckSum>8:c59e763e4cc7d59ae58d843937cf4e77</validCheckSum>
<preConditions onFail="MARK_RAN" onFailMessage="existing table skipping"> <preConditions onFail="MARK_RAN" onFailMessage="existing table skipping">
<not> <not>
<tableExists tableName="FEEDCATEGORIES" /> <tableExists tableName="FEEDCATEGORIES" />
@@ -88,7 +90,7 @@
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" /> <constraints nullable="false" primaryKey="true" />
</column> </column>
<column name="collapsed" type="BIT"> <column name="collapsed" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="name" type="VARCHAR(128)"> <column name="name" type="VARCHAR(128)">
@@ -160,6 +162,7 @@
<changeSet author="athou" id="create-statuses"> <changeSet author="athou" id="create-statuses">
<validCheckSum>7:a9cf194a01c16b937a897aea934f09ae</validCheckSum> <validCheckSum>7:a9cf194a01c16b937a897aea934f09ae</validCheckSum>
<validCheckSum>7:6a386e0b08e98bdba9ce55e26ab90eba</validCheckSum> <validCheckSum>7:6a386e0b08e98bdba9ce55e26ab90eba</validCheckSum>
<validCheckSum>8:ccfbff3df6f16c8686229c2da514e8b1</validCheckSum>
<preConditions onFail="MARK_RAN" onFailMessage="existing table skipping"> <preConditions onFail="MARK_RAN" onFailMessage="existing table skipping">
<not> <not>
<tableExists tableName="FEEDENTRYSTATUSES" /> <tableExists tableName="FEEDENTRYSTATUSES" />
@@ -169,8 +172,8 @@
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" /> <constraints nullable="false" primaryKey="true" />
</column> </column>
<column name="read_status" type="BIT" /> <column name="read_status" type="BOOLEAN" />
<column name="starred" type="BIT"> <column name="starred" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="entry_id" type="BIGINT"> <column name="entry_id" type="BIGINT">
@@ -298,6 +301,8 @@
<changeSet author="athou" id="create-users"> <changeSet author="athou" id="create-users">
<validCheckSum>7:750e0990a8edebd0252df7d4adc7aa7c</validCheckSum> <validCheckSum>7:750e0990a8edebd0252df7d4adc7aa7c</validCheckSum>
<validCheckSum>8:dd1676f356c3c70822d69d5103947948</validCheckSum>
<validCheckSum>8:ca2d6edef0263a78cab9cc0942972d50</validCheckSum>
<preConditions onFail="MARK_RAN" onFailMessage="existing table skipping"> <preConditions onFail="MARK_RAN" onFailMessage="existing table skipping">
<not> <not>
<tableExists tableName="USERS" /> <tableExists tableName="USERS" />
@@ -307,17 +312,17 @@
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" /> <constraints nullable="false" primaryKey="true" />
</column> </column>
<column name="disabled" type="BIT"> <column name="disabled" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="email" type="VARCHAR(255)" /> <column name="email" type="VARCHAR(255)" />
<column name="name" type="VARCHAR(32)"> <column name="name" type="VARCHAR(32)">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="password" type="BLOB"> <column name="password" type="${blob_type}">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="salt" type="BLOB"> <column name="salt" type="${blob_type}">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="lastLogin" type="DATETIME" /> <column name="lastLogin" type="DATETIME" />
@@ -333,6 +338,7 @@
<changeSet author="athou" id="create-user-settings"> <changeSet author="athou" id="create-user-settings">
<validCheckSum>7:985d6607a4350e032ea345d9a2f2f0c0</validCheckSum> <validCheckSum>7:985d6607a4350e032ea345d9a2f2f0c0</validCheckSum>
<validCheckSum>7:722eaff49d04d43c5b26da0929d3f707</validCheckSum> <validCheckSum>7:722eaff49d04d43c5b26da0929d3f707</validCheckSum>
<validCheckSum>8:451004f3bc72abac6a38d813881d3a87</validCheckSum>
<preConditions onFail="MARK_RAN" onFailMessage="existing table skipping"> <preConditions onFail="MARK_RAN" onFailMessage="existing table skipping">
<not> <not>
<tableExists tableName="USERSETTINGS" /> <tableExists tableName="USERSETTINGS" />
@@ -349,16 +355,16 @@
<column name="readingOrder" type="VARCHAR(255)"> <column name="readingOrder" type="VARCHAR(255)">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="showRead" type="BIT"> <column name="showRead" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="user_id" type="BIGINT"> <column name="user_id" type="BIGINT">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="socialButtons" type="BIT"> <column name="socialButtons" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="scrollMarks" type="BIT"> <column name="scrollMarks" type="BOOLEAN">
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="viewMode" type="VARCHAR(255)"> <column name="viewMode" type="VARCHAR(255)">

View File

@@ -16,7 +16,7 @@
<column name="read_status" /> <column name="read_status" />
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="drop-fes-index"> <changeSet author="athou" id="drop-fes-index">
<preConditions onFail="MARK_RAN" onFailMessage="index not found, skip drop index"> <preConditions onFail="MARK_RAN" onFailMessage="index not found, skip drop index">
<indexExists tableName="FEEDENTRYSTATUSES" indexName="subscription_id" /> <indexExists tableName="FEEDENTRYSTATUSES" indexName="subscription_id" />
@@ -66,7 +66,7 @@
<column name="logLevel" type="VARCHAR(255)" /> <column name="logLevel" type="VARCHAR(255)" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-push-hub"> <changeSet author="athou" id="add-push-hub">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -78,7 +78,7 @@
<column name="pushHub" type="VARCHAR(2048)" /> <column name="pushHub" type="VARCHAR(2048)" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-push-topic"> <changeSet author="athou" id="add-push-topic">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -90,7 +90,7 @@
<column name="pushTopic" type="VARCHAR(2048)" /> <column name="pushTopic" type="VARCHAR(2048)" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-push-lastping"> <changeSet author="athou" id="add-push-lastping">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -102,7 +102,7 @@
<column name="pushLastPing" type="DATETIME" /> <column name="pushLastPing" type="DATETIME" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-lastpublished"> <changeSet author="athou" id="add-lastpublished">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -113,8 +113,8 @@
<addColumn tableName="FEEDS"> <addColumn tableName="FEEDS">
<column name="lastPublishedDate" type="DATETIME" /> <column name="lastPublishedDate" type="DATETIME" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-lastcontenthash"> <changeSet author="athou" id="add-lastcontenthash">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -125,8 +125,8 @@
<addColumn tableName="FEEDS"> <addColumn tableName="FEEDS">
<column name="lastContentHash" type="VARCHAR(40)" /> <column name="lastContentHash" type="VARCHAR(40)" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-lastinterval"> <changeSet author="athou" id="add-lastinterval">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -137,8 +137,8 @@
<addColumn tableName="FEEDS"> <addColumn tableName="FEEDS">
<column name="averageEntryInterval" type="BIGINT" /> <column name="averageEntryInterval" type="BIGINT" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-lastentrydate"> <changeSet author="athou" id="add-lastentrydate">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -150,7 +150,7 @@
<column name="lastEntryDate" type="DATETIME" /> <column name="lastEntryDate" type="DATETIME" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-cat-position"> <changeSet author="athou" id="add-cat-position">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -162,7 +162,7 @@
<column name="position" type="INT" /> <column name="position" type="INT" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-sub-position"> <changeSet author="athou" id="add-sub-position">
<preConditions onFail="MARK_RAN" onFailMessage="column already exists"> <preConditions onFail="MARK_RAN" onFailMessage="column already exists">
<not> <not>
@@ -174,26 +174,26 @@
<column name="position" type="INT" /> <column name="position" type="INT" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="drop-sequence"> <changeSet author="athou" id="drop-sequence">
<preConditions onFail="MARK_RAN" onFailMessage="table does not exist"> <preConditions onFail="MARK_RAN" onFailMessage="table does not exist">
<tableExists tableName="hibernate_sequence" /> <tableExists tableName="hibernate_sequence" />
</preConditions> </preConditions>
<dropTable tableName="hibernate_sequence" /> <dropTable tableName="hibernate_sequence" />
</changeSet> </changeSet>
<changeSet author="athou" id="drop-old-pushinfos"> <changeSet author="athou" id="drop-old-pushinfos">
<preConditions onFail="MARK_RAN" onFailMessage="table does not exist"> <preConditions onFail="MARK_RAN" onFailMessage="table does not exist">
<tableExists tableName="FEEDPUSHINFOS" /> <tableExists tableName="FEEDPUSHINFOS" />
</preConditions> </preConditions>
<dropTable tableName="FEEDPUSHINFOS" /> <dropTable tableName="FEEDPUSHINFOS" />
</changeSet> </changeSet>
<changeSet author="athou" id="add-topic-hash"> <changeSet author="athou" id="add-topic-hash">
<addColumn tableName="FEEDS"> <addColumn tableName="FEEDS">
<column name="push_topic_hash" type="VARCHAR(40)" /> <column name="push_topic_hash" type="VARCHAR(40)" />
</addColumn> </addColumn>
<createIndex tableName="FEEDS" indexName="push_topic_hash_index"> <createIndex tableName="FEEDS" indexName="push_topic_hash_index">
<column name="push_topic_hash"></column> <column name="push_topic_hash"></column>
</createIndex> </createIndex>
@@ -203,83 +203,85 @@
<addColumn tableName="FEEDENTRIES"> <addColumn tableName="FEEDENTRIES">
<column name="author" type="VARCHAR(128)" /> <column name="author" type="VARCHAR(128)" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-inserted-index"> <changeSet author="athou" id="add-inserted-index">
<createIndex tableName="FEEDENTRIES" indexName="inserted_index"> <createIndex tableName="FEEDENTRIES" indexName="inserted_index">
<column name="inserted"></column> <column name="inserted"></column>
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="rename-lang"> <changeSet author="athou" id="rename-lang">
<renameColumn tableName="USERSETTINGS" oldColumnName="language" newColumnName="user_lang" columnDataType="VARCHAR(4)" /> <renameColumn tableName="USERSETTINGS" oldColumnName="language" newColumnName="user_lang" columnDataType="VARCHAR(4)" />
</changeSet> </changeSet>
<changeSet author="athou" id="norwegian-migration"> <changeSet author="athou" id="norwegian-migration">
<sql>update USERSETTINGS set user_lang='nb' where user_lang='no'</sql> <sql>update USERSETTINGS set user_lang='nb' where user_lang='no'</sql>
</changeSet> </changeSet>
<changeSet author="athou" id="add-user-created"> <changeSet author="athou" id="add-user-created">
<validCheckSum>3:b1bbf8d559ac25b785751704f2d24a91</validCheckSum> <validCheckSum>3:b1bbf8d559ac25b785751704f2d24a91</validCheckSum>
<validCheckSum>7:5bd8b28aadce012b56f003539ce99957</validCheckSum> <validCheckSum>7:5bd8b28aadce012b56f003539ce99957</validCheckSum>
<addColumn tableName="USERS"> <addColumn tableName="USERS">
<column name="created" type="DATETIME" /> <column name="created" type="DATETIME" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="add-proxy-setting"> <changeSet author="athou" id="add-proxy-setting">
<validCheckSum>7:ffca06665d2dc182bd3cb718e62e98f0</validCheckSum> <validCheckSum>7:ffca06665d2dc182bd3cb718e62e98f0</validCheckSum>
<validCheckSum>8:a1b2bfccb0b37fec8eb107220f76e3bd</validCheckSum>
<addColumn tableName="APPLICATIONSETTINGS"> <addColumn tableName="APPLICATIONSETTINGS">
<column name="imageProxyEnabled" type="BIT" /> <column name="imageProxyEnabled" type="BOOLEAN" />
</addColumn> </addColumn>
<update tableName="APPLICATIONSETTINGS"> <update tableName="APPLICATIONSETTINGS">
<column name="imageProxyEnabled" valueBoolean="false"></column> <column name="imageProxyEnabled" valueBoolean="false"></column>
</update> </update>
</changeSet> </changeSet>
<changeSet author="athou" id="add-query-timeout-setting"> <changeSet author="athou" id="add-query-timeout-setting">
<addColumn tableName="APPLICATIONSETTINGS"> <addColumn tableName="APPLICATIONSETTINGS">
<column name="queryTimeout" type="INT" /> <column name="queryTimeout" type="INT" />
</addColumn> </addColumn>
<update tableName="APPLICATIONSETTINGS"> <update tableName="APPLICATIONSETTINGS">
<column name="queryTimeout" valueNumeric="0"></column> <column name="queryTimeout" valueNumeric="0"></column>
</update> </update>
</changeSet> </changeSet>
<changeSet author="athou" id="add-normalized-url"> <changeSet author="athou" id="add-normalized-url">
<addColumn tableName="FEEDS"> <addColumn tableName="FEEDS">
<column name="normalizedUrl" type="VARCHAR(2048)" /> <column name="normalizedUrl" type="VARCHAR(2048)" />
</addColumn> </addColumn>
<addColumn tableName="FEEDS"> <addColumn tableName="FEEDS">
<column name="normalizedUrlHash" type="VARCHAR(40)" /> <column name="normalizedUrlHash" type="VARCHAR(40)" />
</addColumn> </addColumn>
<createIndex indexName="norm_url_hash_index" tableName="FEEDS" <createIndex indexName="norm_url_hash_index" tableName="FEEDS"
unique="false"> unique="false">
<column name="normalizedUrlHash" /> <column name="normalizedUrlHash" />
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="add-pause-crawling"> <changeSet author="athou" id="add-pause-crawling">
<validCheckSum>8:4473505a94945268fcca0f2d77e4be4a</validCheckSum>
<addColumn tableName="APPLICATIONSETTINGS"> <addColumn tableName="APPLICATIONSETTINGS">
<column name="crawlingPaused" type="BIT" /> <column name="crawlingPaused" type="BOOLEAN" />
</addColumn> </addColumn>
<update tableName="APPLICATIONSETTINGS"> <update tableName="APPLICATIONSETTINGS">
<column name="crawlingPaused" valueBoolean="false"></column> <column name="crawlingPaused" valueBoolean="false"></column>
</update> </update>
</changeSet> </changeSet>
<changeSet author="athou" id="add-content-hash-index"> <changeSet author="athou" id="add-content-hash-index">
<createIndex tableName="FEEDS" indexName="last_content_hash_index"> <createIndex tableName="FEEDS" indexName="last_content_hash_index">
<column name="lastContentHash"></column> <column name="lastContentHash"></column>
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="create-settings-index"> <changeSet author="athou" id="create-settings-index">
<createIndex tableName="USERSETTINGS" indexName="user_id_index" unique="true"> <createIndex tableName="USERSETTINGS" indexName="user_id_index" unique="true">
<column name="user_id"></column> <column name="user_id"></column>
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="denormalize-statuses"> <changeSet author="athou" id="denormalize-statuses">
<validCheckSum>7:c73f70fbcbc8bb30f9629028ec8ddb06</validCheckSum> <validCheckSum>7:c73f70fbcbc8bb30f9629028ec8ddb06</validCheckSum>
<addColumn tableName="FEEDENTRYSTATUSES"> <addColumn tableName="FEEDENTRYSTATUSES">
@@ -290,18 +292,18 @@
<column name="entryUpdated" type="DATETIME" /> <column name="entryUpdated" type="DATETIME" />
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="populate-status-dates"> <changeSet author="athou" id="populate-status-dates">
<validCheckSum>7:d6b5ab6920948b0a84e614870128e2f5</validCheckSum> <validCheckSum>7:d6b5ab6920948b0a84e614870128e2f5</validCheckSum>
<sql>update FEEDENTRYSTATUSES SET entryUpdated = (select e.updated from FEEDENTRIES e where e.id = FEEDENTRYSTATUSES.entry_id)</sql> <sql>update FEEDENTRYSTATUSES SET entryUpdated = (select e.updated from FEEDENTRIES e where e.id = FEEDENTRYSTATUSES.entry_id)</sql>
</changeSet> </changeSet>
<changeSet author="athou" id="populate-status-users"> <changeSet author="athou" id="populate-status-users">
<validCheckSum>7:4227fdf2e7b9fe8e59544d536a7ee963</validCheckSum> <validCheckSum>7:4227fdf2e7b9fe8e59544d536a7ee963</validCheckSum>
<sql>update FEEDENTRYSTATUSES SET user_id = (select sub.user_id from FEEDSUBSCRIPTIONS sub where sub.id = FEEDENTRYSTATUSES.subscription_id)</sql> <sql>update FEEDENTRYSTATUSES SET user_id = (select sub.user_id from FEEDSUBSCRIPTIONS sub where sub.id = FEEDENTRYSTATUSES.subscription_id)</sql>
</changeSet> </changeSet>
<changeSet author="athou" id="recreate-fes-index-2"> <changeSet author="athou" id="recreate-fes-index-2">
<createIndex indexName="sub_entry_index" tableName="FEEDENTRYSTATUSES"> <createIndex indexName="sub_entry_index" tableName="FEEDENTRYSTATUSES">
<column name="subscription_id" /> <column name="subscription_id" />
<column name="entry_id" /> <column name="entry_id" />
@@ -317,28 +319,28 @@
<column name="entryUpdated" /> <column name="entryUpdated" />
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="drop-fes-index-2"> <changeSet author="athou" id="drop-fes-index-2">
<dropIndex tableName="FEEDENTRYSTATUSES" indexName="sub_entry_read_index" /> <dropIndex tableName="FEEDENTRYSTATUSES" indexName="sub_entry_read_index" />
</changeSet> </changeSet>
<changeSet author="athou" id="add-entry-updated-to-ffe"> <changeSet author="athou" id="add-entry-updated-to-ffe">
<addColumn tableName="FEED_FEEDENTRIES"> <addColumn tableName="FEED_FEEDENTRIES">
<column name="entryUpdated" type="DATETIME"></column> <column name="entryUpdated" type="DATETIME"></column>
</addColumn> </addColumn>
</changeSet> </changeSet>
<changeSet author="athou" id="populate-entry-dates"> <changeSet author="athou" id="populate-entry-dates">
<sql>update FEED_FEEDENTRIES SET entryUpdated = (select e.updated from FEEDENTRIES e where e.id = FEED_FEEDENTRIES.feedentry_id)</sql> <sql>update FEED_FEEDENTRIES SET entryUpdated = (select e.updated from FEEDENTRIES e where e.id = FEED_FEEDENTRIES.feedentry_id)</sql>
</changeSet> </changeSet>
<changeSet author="athou" id="create-ffe-entry-updated-index"> <changeSet author="athou" id="create-ffe-entry-updated-index">
<createIndex tableName="FEED_FEEDENTRIES" indexName="feed_updated_index"> <createIndex tableName="FEED_FEEDENTRIES" indexName="feed_updated_index">
<column name="FEED_ID"></column> <column name="FEED_ID"></column>
<column name="entryUpdated"></column> <column name="entryUpdated"></column>
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="create-count-index"> <changeSet author="athou" id="create-count-index">
<createIndex indexName="user_read_sub_index" tableName="FEEDENTRYSTATUSES"> <createIndex indexName="user_read_sub_index" tableName="FEEDENTRYSTATUSES">
<column name="user_id" /> <column name="user_id" />
@@ -346,21 +348,21 @@
<column name="subscription_id" /> <column name="subscription_id" />
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="add-trim-status-setting"> <changeSet author="athou" id="add-trim-status-setting">
<addColumn tableName="APPLICATIONSETTINGS"> <addColumn tableName="APPLICATIONSETTINGS">
<column name="keepStatusDays" type="INT" /> <column name="keepStatusDays" type="INT" />
</addColumn> </addColumn>
<update tableName="APPLICATIONSETTINGS"> <update tableName="APPLICATIONSETTINGS">
<column name="keepStatusDays" valueNumeric="0"></column> <column name="keepStatusDays" valueNumeric="0"></column>
</update> </update>
</changeSet> </changeSet>
<changeSet author="athou" id="status-cleanup"> <changeSet author="athou" id="status-cleanup">
<validCheckSum>7:cf40ae235c2d4086c5fa6ac64102c6a9</validCheckSum> <validCheckSum>7:cf40ae235c2d4086c5fa6ac64102c6a9</validCheckSum>
<delete tableName="FEEDENTRYSTATUSES"> <delete tableName="FEEDENTRYSTATUSES">
<where>read_status = false and starred = false</where> <where>read_status = false and starred = false</where>
</delete> </delete>
</changeSet> </changeSet>
</databaseChangeLog> </databaseChangeLog>

View File

@@ -3,35 +3,36 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"> xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="add-detailed-social-options" author="athou"> <changeSet id="add-detailed-social-options" author="athou">
<validCheckSum>8:58e8060bba0ec9d448f4346eb35d815c</validCheckSum>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="email" type="BIT"></column> <column name="email" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="gmail" type="BIT"></column> <column name="gmail" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="facebook" type="BIT"></column> <column name="facebook" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="twitter" type="BIT"></column> <column name="twitter" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="googleplus" type="BIT"></column> <column name="googleplus" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="tumblr" type="BIT"></column> <column name="tumblr" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="pocket" type="BIT"></column> <column name="pocket" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="instapaper" type="BIT"></column> <column name="instapaper" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="buffer" type="BIT"></column> <column name="buffer" type="BOOLEAN"></column>
</addColumn> </addColumn>
<addColumn tableName="USERSETTINGS"> <addColumn tableName="USERSETTINGS">
<column name="readability" type="BIT"></column> <column name="readability" type="BOOLEAN"></column>
</addColumn> </addColumn>
<dropColumn tableName="USERSETTINGS" columnName="socialButtons" /> <dropColumn tableName="USERSETTINGS" columnName="socialButtons" />
@@ -49,7 +50,7 @@
<column name="readability" valueBoolean="true"></column> <column name="readability" valueBoolean="true"></column>
</update> </update>
</changeSet> </changeSet>
<changeSet id="delete-settings" author="athou"> <changeSet id="delete-settings" author="athou">
<dropTable tableName="APPLICATIONSETTINGS" /> <dropTable tableName="APPLICATIONSETTINGS" />
</changeSet> </changeSet>

View File

@@ -2,6 +2,11 @@
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"> xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<property name="blob_type" value="bytea" dbms="postgresql"/>
<property name="blob_type" value="blob" dbms="h2" />
<property name="blob_type" value="blob" dbms="mysql" />
<property name="blob_type" value="blob" dbms="mssql" />
<include file="changelogs/db.changelog-1.0.xml" /> <include file="changelogs/db.changelog-1.0.xml" />
<include file="changelogs/db.changelog-1.1.xml" /> <include file="changelogs/db.changelog-1.1.xml" />

View File

@@ -80,7 +80,7 @@ public class UserRestTest {
SessionHelper sessionHelper = mock(SessionHelper.class); SessionHelper sessionHelper = mock(SessionHelper.class);
UserREST userREST = new UserREST(null, null, null, service, null, null, null); UserREST userREST = new UserREST(null, null, null, service, null, null, null);
userREST.register(req, sessionHelper); userREST.registerUser(req, sessionHelper);
inOrder.verify(service).register("user", "password", "test@test.com", Arrays.asList(Role.USER)); inOrder.verify(service).register("user", "password", "test@test.com", Arrays.asList(Role.USER));
inOrder.verify(service).login("user", "password"); inOrder.verify(service).login("user", "password");
@@ -104,7 +104,7 @@ public class UserRestTest {
SessionHelper sessionHelper = mock(SessionHelper.class); SessionHelper sessionHelper = mock(SessionHelper.class);
UserREST userREST = new UserREST(null, null, null, service, null, null, null); UserREST userREST = new UserREST(null, null, null, service, null, null, null);
userREST.register(req, sessionHelper); userREST.registerUser(req, sessionHelper);
verify(sessionHelper).setLoggedInUser(user); verify(sessionHelper).setLoggedInUser(user);
} }