mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
Compare commits
402 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a16d9877cc | ||
|
|
c24e9e083c | ||
|
|
101602c6f6 | ||
|
|
18a7bd1fd1 | ||
|
|
dfbd556bb8 | ||
|
|
040cdde8ba | ||
|
|
06373480ae | ||
|
|
5713a78f2e | ||
|
|
b9f2f17a24 | ||
|
|
9adc993472 | ||
|
|
dcd5f3d529 | ||
|
|
18e70a0e6b | ||
|
|
5ad57d1608 | ||
|
|
74eaf48ceb | ||
|
|
30bb0cb291 | ||
|
|
b50e6b93bd | ||
|
|
a0b5a1462d | ||
|
|
4910f93c94 | ||
|
|
4a52bd0cb7 | ||
|
|
b0bfb73952 | ||
|
|
69d049a69a | ||
|
|
7d75153362 | ||
|
|
748bfa31ae | ||
|
|
e7d995edbc | ||
|
|
a144fb2e48 | ||
|
|
7521013e11 | ||
|
|
c6321fc6b2 | ||
|
|
7d92d5d096 | ||
|
|
ab201d5016 | ||
|
|
efa38d5ee9 | ||
|
|
e8769d09a8 | ||
|
|
a216444825 | ||
|
|
fee3e10e6b | ||
|
|
4d71a8f3c2 | ||
|
|
fc104b0b01 | ||
|
|
3dcb351b36 | ||
|
|
600d05d08f | ||
|
|
6b6ff70ad3 | ||
|
|
891f660738 | ||
|
|
6901b9b728 | ||
|
|
c7f211a7f8 | ||
|
|
c48ea1152c | ||
|
|
f5d0eb94b4 | ||
|
|
cebeef04a0 | ||
|
|
3e77a83ca6 | ||
|
|
c872b335e7 | ||
|
|
cc1e173552 | ||
|
|
35e0567705 | ||
|
|
fb2add305e | ||
|
|
74d4c18c4c | ||
|
|
da3ce07485 | ||
|
|
c7ab179a9e | ||
|
|
6fd11fcd56 | ||
|
|
3966cf165b | ||
|
|
0b2ada5d1c | ||
|
|
4278101bbe | ||
|
|
8b43af49fc | ||
|
|
6e29e8426b | ||
|
|
af11d3c771 | ||
|
|
e5c5af4d57 | ||
|
|
3dbdf5adf2 | ||
|
|
4d7a030b70 | ||
|
|
3351262dd7 | ||
|
|
5ec4377502 | ||
|
|
9c8402c3a5 | ||
|
|
928a45e48e | ||
|
|
1d088c5eae | ||
|
|
cdcf81ab7c | ||
|
|
9f196bafe9 | ||
|
|
5c9e1406a1 | ||
|
|
0b42e00b29 | ||
|
|
88b98a138f | ||
|
|
136c37885d | ||
|
|
812988b31a | ||
|
|
191680a01b | ||
|
|
467d1a754d | ||
|
|
d1973922cd | ||
|
|
3b7689975d | ||
|
|
3386a71c5e | ||
|
|
7bb65a5e76 | ||
|
|
f3a9c8e0e2 | ||
|
|
22861ca8d0 | ||
|
|
19118ea241 | ||
|
|
4a9dc7249f | ||
|
|
5dad9c2eb8 | ||
|
|
d6b35b00b9 | ||
|
|
fda8ab500b | ||
|
|
66df421de2 | ||
|
|
33c62f08ca | ||
|
|
b660602809 | ||
|
|
6dfce2ca30 | ||
|
|
655e20e99e | ||
|
|
f2b80bdc08 | ||
|
|
10af873fa5 | ||
|
|
d87a5b14f8 | ||
|
|
b87a18b993 | ||
|
|
c4185034e4 | ||
|
|
9d64426b00 | ||
|
|
c81cc8bea4 | ||
|
|
90e680d6be | ||
|
|
04c0833111 | ||
|
|
06151eab3b | ||
|
|
3dcb8590f6 | ||
|
|
a9b313aa4a | ||
|
|
1f2e35060b | ||
|
|
a96862fffa | ||
|
|
68cb8e194d | ||
|
|
c164926c54 | ||
|
|
de7516116d | ||
|
|
fccfe5b088 | ||
|
|
23aa5fa0a3 | ||
|
|
d384c0a141 | ||
|
|
18058c2a36 | ||
|
|
71727202f3 | ||
|
|
eee0b949de | ||
|
|
3cbbb67b0c | ||
|
|
7879f66e78 | ||
|
|
c14ac37495 | ||
|
|
73a77183aa | ||
|
|
09cfa21091 | ||
|
|
c193571ece | ||
|
|
04bc92b071 | ||
|
|
94e58a449c | ||
|
|
9d044195aa | ||
|
|
caff34cc3b | ||
|
|
34c5c0b1f7 | ||
|
|
906801e13c | ||
|
|
dad4c6b866 | ||
|
|
090462022f | ||
|
|
cbf9f65fb4 | ||
|
|
5a493cd55d | ||
|
|
dfc204ef05 | ||
|
|
56c6e2d29c | ||
|
|
db03dd12a0 | ||
|
|
6c67e6363a | ||
|
|
e2888beb4c | ||
|
|
bba9166885 | ||
|
|
504e4eab3e | ||
|
|
2e475c35cc | ||
|
|
ccf18758fb | ||
|
|
68f9852790 | ||
|
|
d0150de003 | ||
|
|
e2b792335b | ||
|
|
ece38c9e59 | ||
|
|
a19b5090bf | ||
|
|
e4b3c35892 | ||
|
|
4b229a759a | ||
|
|
1e9e42ac48 | ||
|
|
245a48f66e | ||
|
|
e6d8397550 | ||
|
|
d59bd43846 | ||
|
|
c1579c83c7 | ||
|
|
4d782e60ad | ||
|
|
c702f47927 | ||
|
|
9110cfd923 | ||
|
|
e40dd14bbf | ||
|
|
90aaae9959 | ||
|
|
e81dda0fa8 | ||
|
|
f93796d036 | ||
|
|
d06359cb81 | ||
|
|
8b68fb578f | ||
|
|
cca300e419 | ||
|
|
77c3ec0bbe | ||
|
|
ed81fc576a | ||
|
|
435fcb9669 | ||
|
|
9020d95b62 | ||
|
|
84d7a501d4 | ||
|
|
e65dd49d69 | ||
|
|
a705cbe6c2 | ||
|
|
60b8af3860 | ||
|
|
9ac4187aa8 | ||
|
|
6419d29489 | ||
|
|
4684e43f42 | ||
|
|
a477c9fa6d | ||
|
|
d1be331f99 | ||
|
|
cbc792d406 | ||
|
|
0313c5c560 | ||
|
|
18aa2fcd92 | ||
|
|
10461941d7 | ||
|
|
e6050219bc | ||
|
|
81481c37fe | ||
|
|
5ea92a7d18 | ||
|
|
f40630aced | ||
|
|
81850acdfe | ||
|
|
6819d5aa8b | ||
|
|
2aef4e5d05 | ||
|
|
6d4d2c3e7e | ||
|
|
87bcaa4731 | ||
|
|
5d2378f291 | ||
|
|
253507d14b | ||
|
|
548fb7099b | ||
|
|
0dd7c777ee | ||
|
|
6812bf2388 | ||
|
|
12bcbfa9f7 | ||
|
|
b5dfd371d9 | ||
|
|
e09d7fb103 | ||
|
|
0fe3afe254 | ||
|
|
db50d50c19 | ||
|
|
691bdb1512 | ||
|
|
d50b712bca | ||
|
|
3b68e4f32b | ||
|
|
259b9a90dd | ||
|
|
f4c5fd7eb4 | ||
|
|
3cd42d03f0 | ||
|
|
3497b82e8c | ||
|
|
15a24e4e75 | ||
|
|
96837f908e | ||
|
|
4ea5ebbf9e | ||
|
|
281e015376 | ||
|
|
5825a16aff | ||
|
|
2586a8c433 | ||
|
|
9f7c9c3428 | ||
|
|
9790ba735b | ||
|
|
e3dbcac9fb | ||
|
|
1c99929429 | ||
|
|
9b2cdbbb18 | ||
|
|
928cf1220e | ||
|
|
c0557856a3 | ||
|
|
97c2cc3d15 | ||
|
|
a94ef980bb | ||
|
|
eea0c24d2b | ||
|
|
c8fded3c56 | ||
|
|
8f2ba5e186 | ||
|
|
5ce2823d0b | ||
|
|
a0c70d326f | ||
|
|
5f28fd4114 | ||
|
|
7151db0909 | ||
|
|
e82888f8f3 | ||
|
|
4fb60a6ec6 | ||
|
|
27f22f6094 | ||
|
|
7497a0151a | ||
|
|
41f133afb1 | ||
|
|
4b15ecbc1b | ||
|
|
6498130850 | ||
|
|
24bd1121af | ||
|
|
3cccf741d6 | ||
|
|
0a2d2c3f43 | ||
|
|
969da0f2a6 | ||
|
|
2061b68a2f | ||
|
|
443dea5055 | ||
|
|
a4c6365ede | ||
|
|
c9c044386e | ||
|
|
2744f8285c | ||
|
|
7bf5f20b06 | ||
|
|
b43aa84c2a | ||
|
|
dd27d88309 | ||
|
|
8dc36a72b2 | ||
|
|
d3ca301675 | ||
|
|
43e3469e63 | ||
|
|
cdc3dc6740 | ||
|
|
6fba8b61e7 | ||
|
|
b34594a1dc | ||
|
|
19964d253e | ||
|
|
165f3ed25a | ||
|
|
5058290103 | ||
|
|
358a6029a1 | ||
|
|
fa4bfa729d | ||
|
|
9c9e43cf46 | ||
|
|
b7e5bd0144 | ||
|
|
58dc6f5832 | ||
|
|
f409af1c37 | ||
|
|
9e0c94f1a4 | ||
|
|
3794d61a77 | ||
|
|
d22da54d53 | ||
|
|
8e34c44e0d | ||
|
|
b71434acf6 | ||
|
|
7e158ed9b9 | ||
|
|
2ec0d067f3 | ||
|
|
effc65b777 | ||
|
|
c48e248283 | ||
|
|
f9e9a4547c | ||
|
|
63e35aba6d | ||
|
|
8f852fb9ac | ||
|
|
bf6a13b43f | ||
|
|
12030f6ce9 | ||
|
|
07da878bba | ||
|
|
8d5c3bdec8 | ||
|
|
ce95772afa | ||
|
|
b9f27b2b00 | ||
|
|
0059cabebe | ||
|
|
326ee79c8c | ||
|
|
54cc265ee6 | ||
|
|
e38778b4d0 | ||
|
|
6152d3c14a | ||
|
|
8a172170ea | ||
|
|
64b5d64709 | ||
|
|
67d7315003 | ||
|
|
47da4a2a1a | ||
|
|
174be9c2d1 | ||
|
|
9b68539322 | ||
|
|
2a4660ffa6 | ||
|
|
dce0cf7ee4 | ||
|
|
d6c39d4aba | ||
|
|
fd7e183f40 | ||
|
|
bf78a80f29 | ||
|
|
0ff630b8bd | ||
|
|
49b9e3f278 | ||
|
|
a4cc65c6a4 | ||
|
|
0b46187ac5 | ||
|
|
14ef5af936 | ||
|
|
539d9c6d0e | ||
|
|
56bcc5ef5e | ||
|
|
d6b0324e24 | ||
|
|
ff044e2592 | ||
|
|
3c7747ab97 | ||
|
|
34d97221ed | ||
|
|
84e78d34cd | ||
|
|
ac73806aee | ||
|
|
2105e9a5c9 | ||
|
|
2a36cc4327 | ||
|
|
c3feaf9a15 | ||
|
|
d8537a98aa | ||
|
|
42a6001ba5 | ||
|
|
4d9eb35230 | ||
|
|
e4ac296a1f | ||
|
|
01b49e7864 | ||
|
|
bd0b85a8d2 | ||
|
|
3d59a4c516 | ||
|
|
08ceff0f03 | ||
|
|
d6ae88ac43 | ||
|
|
5c8f016dd6 | ||
|
|
17288017d8 | ||
|
|
1e2757b52f | ||
|
|
0dce2f057e | ||
|
|
e017c5c304 | ||
|
|
a3e828f90a | ||
|
|
74e5c24fdc | ||
|
|
76c0abaa22 | ||
|
|
a52b5fd711 | ||
|
|
ffa51406b6 | ||
|
|
0b3b267e63 | ||
|
|
fcdb9d8257 | ||
|
|
04943ca525 | ||
|
|
574d4a1223 | ||
|
|
7349814cb2 | ||
|
|
114c5eb356 | ||
|
|
191f861f6e | ||
|
|
fac1fcc3a6 | ||
|
|
d0490c5eb5 | ||
|
|
2673efa9fc | ||
|
|
d4bce7b0a1 | ||
|
|
ba4a7ce6ab | ||
|
|
58f10153ab | ||
|
|
e7b65e3f26 | ||
|
|
fe91473748 | ||
|
|
0140402ad4 | ||
|
|
f56cba59ae | ||
|
|
fed74f05fc | ||
|
|
0888f11257 | ||
|
|
7205d5bb9c | ||
|
|
17a5ef882f | ||
|
|
ea68dbc56f | ||
|
|
0cec8af074 | ||
|
|
f7d0fc5768 | ||
|
|
bcaab694c8 | ||
|
|
247a3d5ab3 | ||
|
|
8e262a1e10 | ||
|
|
f63695bdc7 | ||
|
|
b051613b62 | ||
|
|
b886379d34 | ||
|
|
2a780dd2bb | ||
|
|
9cf7b80110 | ||
|
|
8fee73f1d1 | ||
|
|
36edb9373b | ||
|
|
374c4b265a | ||
|
|
db0b685ae1 | ||
|
|
23d33b8402 | ||
|
|
8a57be3e63 | ||
|
|
823cb03f9b | ||
|
|
e96cbcb057 | ||
|
|
fa0e7bcb54 | ||
|
|
20292a7742 | ||
|
|
943bde7eed | ||
|
|
9701af0736 | ||
|
|
1456cc40e1 | ||
|
|
dc1f88c44c | ||
|
|
55c916956f | ||
|
|
51eda57618 | ||
|
|
d6a55e1ec0 | ||
|
|
b78210421c | ||
|
|
1324269f1d | ||
|
|
cda6cb5cc0 | ||
|
|
c1b8619b26 | ||
|
|
4203e25321 | ||
|
|
aa02c7b93a | ||
|
|
0ff477579b | ||
|
|
62a8e8c119 | ||
|
|
fa212e0911 | ||
|
|
c8ad902a60 | ||
|
|
f05515d7d6 | ||
|
|
95bbcce941 | ||
|
|
d6b98f1518 | ||
|
|
bd9b1b11c5 | ||
|
|
e4c4960972 | ||
|
|
2a26031261 | ||
|
|
1d6e212955 | ||
|
|
9fa3743d21 | ||
|
|
7b373c79d9 | ||
|
|
4e9266e2d5 | ||
|
|
ea957e297c | ||
|
|
9320b6beb8 | ||
|
|
1319bf4a8c |
3
.openshift/README.md
Normal file
3
.openshift/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
For information about .openshift directory, consult the documentation:
|
||||
|
||||
http://openshift.github.io/documentation/oo_user_guide.html#the-openshift-directory
|
||||
3
.openshift/action_hooks/README.md
Normal file
3
.openshift/action_hooks/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
For information about action hooks, consult the documentation:
|
||||
|
||||
http://openshift.github.io/documentation/oo_user_guide.html#action-hooks
|
||||
36
.openshift/action_hooks/build
Executable file
36
.openshift/action_hooks/build
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
if [ ! -d $OPENSHIFT_DATA_DIR/jdk1.8.0_20 ]
|
||||
then
|
||||
cd $OPENSHIFT_DATA_DIR
|
||||
wget http://www.java.net/download/jdk8u20/archive/b17/binaries/jdk-8u20-ea-bin-b17-linux-x64-04_jun_2014.tar.gz
|
||||
tar xvf *.tar.gz
|
||||
rm -f *.tar.gz
|
||||
fi
|
||||
if [ ! -d $OPENSHIFT_DATA_DIR/apache-maven-3.2.3 ]
|
||||
then
|
||||
cd $OPENSHIFT_DATA_DIR
|
||||
wget http://archive.apache.org/dist/maven/maven-3/3.2.3/binaries/apache-maven-3.2.3-bin.tar.gz
|
||||
tar xvf *.tar.gz
|
||||
rm -f *.tar.gz
|
||||
fi
|
||||
export M2=$OPENSHIFT_DATA_DIR/apache-maven-3.2.3/bin
|
||||
export JAVA_HOME=$OPENSHIFT_DATA_DIR/jdk1.8.0_20
|
||||
export PATH=$JAVA_HOME/bin:$M2:$PATH
|
||||
|
||||
cd $OPENSHIFT_REPO_DIR
|
||||
rm -rf $OPENSHIFT_REPO_DIR/node
|
||||
rm -rf $OPENSHIFT_REPO_DIR/node_modules
|
||||
rm -rf $OPENSHIFT_TMP_DIR/npm
|
||||
rm -rf $OPENSHIFT_TMP_DIR/npmrc
|
||||
rm -rf $OPENSHIFT_TMP_DIR/m2
|
||||
rm -rf $OPENSHIFT_TMP_DIR/local
|
||||
|
||||
export NPM_CONFIG_PREFIX="$OPENSHIFT_TMP_DIR/npm"
|
||||
export NPM_CONFIG_USERCONFIG="$OPENSHIFT_TMP_DIR/npmrc"
|
||||
export NPM_CONFIG_CACHE="$OPENSHIFT_TMP_DIR/npm/cache"
|
||||
export MAVEN_OPTS="-Dmaven.repo.local=$OPENSHIFT_TMP_DIR/m2"
|
||||
export HOME="$OPENSHIFT_TMP_DIR/local"
|
||||
|
||||
export NPM_CONFIG_ARCH="x64"
|
||||
|
||||
mvn clean package -DskipTests -Dos.arch=x64 -s .openshift/settings.xml
|
||||
9
.openshift/action_hooks/deploy
Executable file
9
.openshift/action_hooks/deploy
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
cd $OPENSHIFT_REPO_DIR
|
||||
sed -i 's/@OPENSHIFT_DIY_IP@/'"$OPENSHIFT_DIY_IP"'/g' .openshift/config.mysql.yml
|
||||
sed -i 's/@OPENSHIFT_DIY_PORT@/'"$OPENSHIFT_DIY_PORT"'/g' .openshift/config.mysql.yml
|
||||
sed -i 's/@OPENSHIFT_APP_DNS@/'"$OPENSHIFT_APP_DNS"'/g' .openshift/config.mysql.yml
|
||||
sed -i 's/@OPENSHIFT_APP_NAME@/'"$OPENSHIFT_APP_NAME"'/g' .openshift/config.mysql.yml
|
||||
sed -i 's/@OPENSHIFT_MYSQL_DB_HOST@/'"$OPENSHIFT_MYSQL_DB_HOST"'/g' .openshift/config.mysql.yml
|
||||
sed -i 's/@OPENSHIFT_MYSQL_DB_USERNAME@/'"$OPENSHIFT_MYSQL_DB_USERNAME"'/g' .openshift/config.mysql.yml
|
||||
sed -i 's/@OPENSHIFT_MYSQL_DB_PASSWORD@/'"$OPENSHIFT_MYSQL_DB_PASSWORD"'/g' .openshift/config.mysql.yml
|
||||
4
.openshift/action_hooks/start
Executable file
4
.openshift/action_hooks/start
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
cd $OPENSHIFT_REPO_DIR
|
||||
export JAVA_HOME=$OPENSHIFT_DATA_DIR/jdk1.8.0_20
|
||||
nohup $JAVA_HOME/bin/java -jar target/commafeed.jar server .openshift/config.mysql.yml > ${OPENSHIFT_DIY_LOG_DIR}/commafeed.log 2>&1 &
|
||||
8
.openshift/action_hooks/stop
Executable file
8
.openshift/action_hooks/stop
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
source $OPENSHIFT_CARTRIDGE_SDK_BASH
|
||||
if [ -z "$(ps -ef | grep commafeed.jar | grep -v grep)" ]
|
||||
then
|
||||
client_result "Application is already stopped"
|
||||
else
|
||||
kill `ps -ef | grep commafeed.jar | grep -v grep | awk '{ print $2 }'` > /dev/null 2>&1
|
||||
fi
|
||||
113
.openshift/config.mysql.yml
Normal file
113
.openshift/config.mysql.yml
Normal file
@@ -0,0 +1,113 @@
|
||||
# CommaFeed settings
|
||||
# ------------------
|
||||
app:
|
||||
# url used to access commafeed
|
||||
publicUrl: https://@OPENSHIFT_APP_DNS@/
|
||||
|
||||
# wether to allow user registrations
|
||||
allowRegistrations: false
|
||||
|
||||
# create a demo account the first time the app starts
|
||||
createDemoAccount: false
|
||||
|
||||
# put your google analytics tracking code here
|
||||
googleAnalyticsTrackingCode:
|
||||
|
||||
# put your google server key (used for youtube favicon fetching)
|
||||
googleAuthKey:
|
||||
|
||||
# number of http threads
|
||||
backgroundThreads: 3
|
||||
|
||||
# number of database updating threads
|
||||
databaseUpdateThreads: 1
|
||||
|
||||
# settings for sending emails (password recovery)
|
||||
smtpHost:
|
||||
smtpPort:
|
||||
smtpTls: false
|
||||
smtpUserName:
|
||||
smtpPassword:
|
||||
|
||||
# wether this commafeed instance has a lot of feeds to refresh
|
||||
# leave this to false in almost all cases
|
||||
heavyLoad: false
|
||||
|
||||
# minimum amount of time commafeed will wait before refreshing the same feed
|
||||
refreshIntervalMinutes: 15
|
||||
|
||||
# wether to enable pubsub
|
||||
# probably not needed if refreshIntervalMinutes is low
|
||||
pubsubhubbub: false
|
||||
|
||||
# if enabled, images in feed entries will be proxied through the server instead of accessed directly by the browser
|
||||
# useful if commafeed is usually accessed through a restricting proxy
|
||||
imageProxyEnabled: false
|
||||
|
||||
# database query timeout (in milliseconds), 0 to disable
|
||||
queryTimeout: 0
|
||||
|
||||
# time to keep unread statuses (in days), 0 to disable
|
||||
keepStatusDays: 0
|
||||
|
||||
# entries to keep per feed, old entries will be deleted, 0 to disable
|
||||
maxFeedCapacity: 500
|
||||
|
||||
# cache service to use, possible values are 'noop' and 'redis'
|
||||
cache: noop
|
||||
|
||||
# announcement string displayed on the main page
|
||||
announcement:
|
||||
|
||||
# Database connection
|
||||
# -------------------
|
||||
# for MySQL
|
||||
# driverClass is com.mysql.jdbc.Driver
|
||||
# url is jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
|
||||
#
|
||||
# for PostgreSQL
|
||||
# driverClass is org.postgresql.Driver
|
||||
# url is jdbc:postgresql://localhost:5432/commafeed
|
||||
#
|
||||
# for Microsoft SQL Server
|
||||
# driverClass is net.sourceforge.jtds.jdbc.Driver
|
||||
# url is jdbc:jtds:sqlserver://localhost:1433/commafeed;instance=<instanceName, remove if not needed>
|
||||
|
||||
database:
|
||||
driverClass: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://@OPENSHIFT_MYSQL_DB_HOST@/@OPENSHIFT_APP_NAME@?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
|
||||
user: @OPENSHIFT_MYSQL_DB_USERNAME@
|
||||
password: @OPENSHIFT_MYSQL_DB_PASSWORD@
|
||||
properties:
|
||||
charSet: UTF-8
|
||||
maxWaitForConnection: 1s
|
||||
validationQuery: "/* CommaFeed Health Check */ SELECT 1"
|
||||
minSize: 1
|
||||
maxSize: 50
|
||||
checkConnectionWhileIdle: true
|
||||
maxConnectionAge: 30m
|
||||
|
||||
server:
|
||||
applicationConnectors:
|
||||
- type: http
|
||||
port: @OPENSHIFT_DIY_PORT@
|
||||
bindHost: @OPENSHIFT_DIY_IP@
|
||||
adminConnectors:
|
||||
- type: http
|
||||
port: 15000
|
||||
bindHost: @OPENSHIFT_DIY_IP@
|
||||
logging:
|
||||
level: WARN
|
||||
loggers:
|
||||
com.commafeed: INFO
|
||||
liquibase: INFO
|
||||
io.dropwizard.server.ServerFactory: INFO
|
||||
appenders:
|
||||
- type: console
|
||||
- type: file
|
||||
currentLogFilename: log/commafeed.log
|
||||
threshold: ALL
|
||||
archive: true
|
||||
archivedLogFilenamePattern: log/commafeed-%d.log
|
||||
archivedFileCount: 5
|
||||
timeZone: UTC
|
||||
0
.openshift/cron/daily/.gitignore
vendored
Normal file
0
.openshift/cron/daily/.gitignore
vendored
Normal file
0
.openshift/cron/hourly/.gitignore
vendored
Normal file
0
.openshift/cron/hourly/.gitignore
vendored
Normal file
0
.openshift/cron/minutely/.gitignore
vendored
Normal file
0
.openshift/cron/minutely/.gitignore
vendored
Normal file
0
.openshift/cron/monthly/.gitignore
vendored
Normal file
0
.openshift/cron/monthly/.gitignore
vendored
Normal file
16
.openshift/cron/weekly/README
Normal file
16
.openshift/cron/weekly/README
Normal file
@@ -0,0 +1,16 @@
|
||||
Run scripts or jobs on a weekly basis
|
||||
=====================================
|
||||
Any scripts or jobs added to this directory will be run on a scheduled basis
|
||||
(weekly) using run-parts.
|
||||
|
||||
run-parts ignores any files that are hidden or dotfiles (.*) or backup
|
||||
files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} and handles
|
||||
the files named jobs.deny and jobs.allow specially.
|
||||
|
||||
In this specific example, the chronograph script is the only script or job file
|
||||
executed on a weekly basis (due to white-listing it in jobs.allow). And the
|
||||
README and chrono.dat file are ignored either as a result of being black-listed
|
||||
in jobs.deny or because they are NOT white-listed in the jobs.allow file.
|
||||
|
||||
For more details, please see ../README.cron file.
|
||||
|
||||
1
.openshift/cron/weekly/chrono.dat
Normal file
1
.openshift/cron/weekly/chrono.dat
Normal file
@@ -0,0 +1 @@
|
||||
Time And Relative D...n In Execution (Open)Shift!
|
||||
3
.openshift/cron/weekly/chronograph
Executable file
3
.openshift/cron/weekly/chronograph
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "`date`: `cat $(dirname \"$0\")/chrono.dat`"
|
||||
12
.openshift/cron/weekly/jobs.allow
Normal file
12
.openshift/cron/weekly/jobs.allow
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# Script or job files listed in here (one entry per line) will be
|
||||
# executed on a weekly-basis.
|
||||
#
|
||||
# Example: The chronograph script will be executed weekly but the README
|
||||
# and chrono.dat files in this directory will be ignored.
|
||||
#
|
||||
# The README file is actually ignored due to the entry in the
|
||||
# jobs.deny which is checked before jobs.allow (this file).
|
||||
#
|
||||
chronograph
|
||||
|
||||
7
.openshift/cron/weekly/jobs.deny
Normal file
7
.openshift/cron/weekly/jobs.deny
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Any script or job files listed in here (one entry per line) will NOT be
|
||||
# executed (read as ignored by run-parts).
|
||||
#
|
||||
|
||||
README
|
||||
|
||||
3
.openshift/markers/README.md
Normal file
3
.openshift/markers/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
For information about markers, consult the documentation:
|
||||
|
||||
http://openshift.github.io/documentation/oo_user_guide.html#markers
|
||||
0
.openshift/markers/java8
Normal file
0
.openshift/markers/java8
Normal file
41
.openshift/settings.xml
Normal file
41
.openshift/settings.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<settings>
|
||||
<mirrors>
|
||||
<mirror>
|
||||
<id>nexus</id>
|
||||
<mirrorOf>central</mirrorOf>
|
||||
<url>http://mirror1.ops.rhcloud.com/nexus/content/groups/public</url>
|
||||
</mirror>
|
||||
</mirrors>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>nexus</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>http://central</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>central</id>
|
||||
<url>http://central</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
<activeProfiles>
|
||||
<activeProfile>nexus</activeProfile>
|
||||
</activeProfiles>
|
||||
</settings>
|
||||
3
.travis.yml
Normal file
3
.travis.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
40
CHANGELOG
Normal file
40
CHANGELOG
Normal file
@@ -0,0 +1,40 @@
|
||||
v 2.2.0
|
||||
- fix youtube and instagram favicon fetching
|
||||
- mark as read filter was lost when a feed was rearranged with drag&drop
|
||||
- feed entry categories are now displayed if available
|
||||
- various performance and dependencies upgrades
|
||||
- java8 is now required
|
||||
v 2.1.0
|
||||
- dropwizard upgrade to 0.8.0
|
||||
- you have to remove the "app.contextPath" setting from your yml file, you can optionally use server.applicationContextPath instead
|
||||
- new setting app.maxFeedCapacity for deleting old entries
|
||||
- ability to set filtering expressions for subscriptions to automatically mark new entries as read based on title, content, author or url.
|
||||
- ability to use !keyword or -keyword to exclude a keyword from a search query
|
||||
- facebook feeds now show user favicon instead of facebook favicon
|
||||
- new dark theme 'nightsky'
|
||||
v 2.0.3
|
||||
- internet explorer ajax cache workaround
|
||||
- categories are now deletable again
|
||||
- openshift support is back
|
||||
- youtube feeds now show user favicon instead of youtube favicon
|
||||
v 2.0.2
|
||||
- api using the api key is now working again
|
||||
- context path is now configurable in config.yml (see app.contextPath in config.yml.example)
|
||||
- fix login on firefox when fields are autofilled by the browser
|
||||
- fix scrolling of subscriptions list on mobile
|
||||
- user is now logged in after registration
|
||||
- fix link to documentation on home page and about page
|
||||
- fields autocomplete is disabled on the profile page
|
||||
- users are able to delete their account again
|
||||
- chinese and malaysian translation files are now correctly loaded
|
||||
- software version in user-agent when fetching feeds is no longer hardcoded
|
||||
- admin settings page is now read only, settings are configured in config.yml
|
||||
- added link to metrics on the admin settings page
|
||||
- Rome (rss library) upgrade to 1.5.0
|
||||
v 2.0.1
|
||||
- the redis pool no longer throws an exception when it is unable to aquire a new connection
|
||||
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.
|
||||
See the README on how to build CommaFeed from now on.
|
||||
- CommaFeed should no longer fetch the same feed multiple times in a row
|
||||
- Users can use their username or email to log in
|
||||
217
README.md
217
README.md
@@ -1,90 +1,127 @@
|
||||
CommaFeed [](https://buildhive.cloudbees.com/job/Athou/job/commafeed/)
|
||||
=========
|
||||
Sources for [CommaFeed.com](http://www.commafeed.com/).
|
||||
|
||||
Google Reader inspired self-hosted RSS reader, based on Dropwizard and AngularJS.
|
||||
|
||||
Related open-source projects
|
||||
----------------------------
|
||||
|
||||
Android apps: [News+ extension](https://github.com/Athou/commafeed-newsplus) - [Android app](https://github.com/doomrobo/CommaFeed-Android-Reader)
|
||||
|
||||
Browser extensions: [Chrome](https://github.com/Athou/commafeed-chrome) - [Firefox](https://github.com/Athou/commafeed-firefox) - [Opera](https://github.com/Athou/commafeed-opera) - [Safari](https://github.com/Athou/commafeed-safari)
|
||||
|
||||
Deployment on your own server
|
||||
-----------------------------
|
||||
|
||||
CommaFeed 2.0 has been rewritten to use Dropwizard and gulp instead of using tomee and wro4j. The latest version of the 1.x branch is available [here](https://github.com/Athou/commafeed/tree/1.x).
|
||||
|
||||
For storage, you can either use an embedded H2 database or an external MySQL, PostgreSQL or SQLServer database.
|
||||
You also need Maven 3.x (and a Java 1.7+ JDK) installed in order to build the application.
|
||||
|
||||
To install maven and openjdk on Ubuntu, issue the following commands
|
||||
|
||||
sudo add-apt-repository ppa:natecarlson/maven3
|
||||
sudo apt-get update
|
||||
sudo apt-get install openjdk-7-jdk maven3
|
||||
|
||||
# Not required but if you don't, use 'mvn3' instead of 'mvn' for the rest of the instructions.
|
||||
sudo ln -s /usr/bin/mvn3 /usr/bin/mvn
|
||||
|
||||
On Windows and other operating systems, just download maven 3.x from the [official site](http://maven.apache.org/), extract it somewhere and add the `bin` directory to your `PATH` environment variable.
|
||||
|
||||
Clone this repository. If you don't have git you can download the sources as a zip file from [here](https://github.com/Athou/commafeed/archive/master.zip)
|
||||
|
||||
git clone https://github.com/Athou/commafeed.git
|
||||
cd commafeed
|
||||
|
||||
Now build the application
|
||||
|
||||
mvn clean package
|
||||
|
||||
Copy `config.yml.example` to `config.yml` then edit the file to your liking.
|
||||
Issue the following command to run the app, the server will listen by default on ``http://localhost:8082`. The default user is `admin` and the default password is `admin`.
|
||||
|
||||
java -jar target/commafeed.jar server config.yml
|
||||
|
||||
You can use nginx or apache as a proxy http server. Note that when using apache, the `ProxyPreserveHost on` option should be set in your config file.
|
||||
|
||||
Local development
|
||||
-----------------
|
||||
|
||||
To start the dropwizard backend, use your IDE to run CommaFeedApplication as your main class, and pass `server config.dev.yml` as arguments to the program.
|
||||
To start the client-side webserver with watches on assets, run `gulp dev`. The server is now running on port 8082 and is proxying REST requests to dropwizard on port 8083.
|
||||
|
||||
|
||||
Translate CommaFeed into your language
|
||||
--------------------------------------
|
||||
|
||||
Files for internationalization are located [here](https://github.com/Athou/commafeed/tree/master/src/main/app/i18n).
|
||||
|
||||
To add a new language, create a new file in that directory.
|
||||
The name of the file should be the two-letters [ISO-639-1 language code](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
|
||||
The language has to be referenced in the `src/main/app/js/i18n.js` file to be picked up.
|
||||
|
||||
Themes
|
||||
---------------------
|
||||
|
||||
To create a theme, create a new file `src/main/webapp/sass/themes/_<theme>.scss`. Your styles should be wrapped in a `#theme-<theme>` element and use the [SCSS format](http://sass-lang.com/) which is a superset of CSS.
|
||||
|
||||
Don't forget to reference your theme in `src/main/webapp/sass/app.scss` and in `src/main/webapp/js/controllers.js` (look for `$scope.themes`).
|
||||
|
||||
See [_test.scss](https://github.com/Athou/commafeed/blob/master/src/main/webapp/sass/themes/_test.scss) for an example.
|
||||
|
||||
|
||||
Copyright and license
|
||||
---------------------
|
||||
|
||||
Copyright 2013-2014 CommaFeed.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this work except in compliance with the License.
|
||||
You may obtain a copy of the License in the LICENSE file, or at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
# CommaFeed [](https://travis-ci.org/Athou/commafeed)
|
||||
|
||||
Sources for [CommaFeed.com](http://www.commafeed.com/).
|
||||
|
||||
Google Reader inspired self-hosted RSS reader, based on Dropwizard and AngularJS.
|
||||
|
||||
## Related open-source projects
|
||||
|
||||
|
||||
Android apps: [News+ extension](https://github.com/Athou/commafeed-newsplus)
|
||||
|
||||
Browser extensions: [Chrome](https://github.com/Athou/commafeed-chrome) - [Firefox](https://github.com/Athou/commafeed-firefox) - [Opera](https://github.com/Athou/commafeed-opera) - [Safari](https://github.com/Athou/commafeed-safari)
|
||||
|
||||
## Deployment on your own server
|
||||
|
||||
### The short version
|
||||
|
||||
git clone https://github.com/Athou/commafeed.git
|
||||
cd commafeed
|
||||
mvn clean package
|
||||
cp config.yml.example config.yml
|
||||
vi config.yml
|
||||
java -Djava.net.preferIPv4Stack=true -jar target/commafeed.jar server config.yml
|
||||
|
||||
### The long version
|
||||
|
||||
CommaFeed 2.0 has been rewritten to use Dropwizard and gulp instead of using tomee and wro4j. The latest version of the 1.x branch is available [here](https://github.com/Athou/commafeed/tree/1.x).
|
||||
|
||||
For storage, you can either use an embedded H2 database (use it only to test CommaFeed) or an external MySQL, PostgreSQL or SQLServer database.
|
||||
You also need Maven 3.x (and a Java 1.8+ JDK) installed in order to build the application.
|
||||
|
||||
To install maven and openjdk on Ubuntu, issue the following commands
|
||||
|
||||
sudo apt-get install g++ build-essential openjdk-8-jdk maven
|
||||
# Make sure java8 is the selected java version
|
||||
sudo update-alternatives --config java
|
||||
sudo update-alternatives --config javac
|
||||
|
||||
|
||||
On Windows and other operating systems, just download maven 3.x from the [official site](http://maven.apache.org/), extract it somewhere and add the `bin` directory to your `PATH` environment variable.
|
||||
|
||||
Clone this repository. If you don't have git you can download the sources as a zip file from [here](https://github.com/Athou/commafeed/archive/master.zip)
|
||||
|
||||
git clone https://github.com/Athou/commafeed.git
|
||||
cd commafeed
|
||||
|
||||
Now build the application
|
||||
|
||||
mvn clean package
|
||||
|
||||
Copy `config.yml.example` to `config.yml` then edit the file to your liking.
|
||||
Issue the following command to run the app, the server will listen by default on `http://localhost:8082`. The default user is `admin` and the default password is `admin`.
|
||||
|
||||
java -Djava.net.preferIPv4Stack=true -jar target/commafeed.jar server config.yml
|
||||
|
||||
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
|
||||
|
||||
Files for internationalization are located [here](https://github.com/Athou/commafeed/tree/master/src/main/app/i18n).
|
||||
|
||||
To add a new language, create a new file in that directory.
|
||||
The name of the file should be the two-letters [ISO-639-1 language code](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
|
||||
The language has to be referenced in the `src/main/app/js/i18n.js` file to be picked up.
|
||||
|
||||
## Themes
|
||||
|
||||
To create a theme, create a new file `src/main/app/sass/themes/_<theme>.scss`. Your styles should be wrapped in a `#theme-<theme>` element and use the [SCSS format](http://sass-lang.com/) which is a superset of CSS.
|
||||
|
||||
Don't forget to reference your theme in `src/main/app/sass/app.scss` and in `src/main/app/js/controllers.js` (look for `$scope.themes`).
|
||||
|
||||
See [_test.scss](https://github.com/Athou/commafeed/blob/master/src/main/app/sass/themes/_test.scss) for an example.
|
||||
|
||||
|
||||
## Local development
|
||||
|
||||
Steps to configuring a development environment for CommaFeed may include, but may not be limited to:
|
||||
|
||||
1. `git clone https://github.com/Athou/CommaFeed` into some folder to get the project files.
|
||||
2. Install Eclipse Luna (or latest) from http://www.eclipse.org/downloads/packages/eclipse-ide-java-developers/lunasr1 or your repo if available.
|
||||
3. In Eclipse, Window → Preferences → Maven → Annotation Processing. Check "Automatically configure JDT APT"
|
||||
* You may have to install the m2e-apt connector to have "Annotation Processing" as an option. Do so from Window → Preferences → Maven → Discovery → Open Catalog → type "m2e-apt" in the search box
|
||||
* If you have installed Eclipse EE instead of Luna, you may have trouble installing m2e-apt
|
||||
4. Install Lombok into Eclipse from http://projectlombok.org/download.html
|
||||
* You may have to run `java -jar lombok.jar` as an administrator if your eclipse installation is not in your home folder
|
||||
5. In Eclipse, File → Import → Maven → Existing Maven Projects. Navigate to where you cloned the CommaFeed files into, and select that as the root directory. Click Finish.
|
||||
* You may notice some errors along the lines of "Plugin execution not covered by lifecycle configuration". These are inconsequential.
|
||||
6. Find the file "CommaFeedApplication.java" under the navigation pane.
|
||||
7. Right click it to bring up the context menu → Debug as... → Debug Configurations
|
||||
8. Type `server config.dev.yml` under "Program arguments" in the "Arguments" tab for the Java Application setting "CommaFeedApplication"
|
||||
9. Apply and hit "Debug"
|
||||
10. The debugger is now working. To connect to it, open a terminal (or command prompt) and navigate to the directory where you cloned the CommaFeed files.
|
||||
11. Issue the command `gulp dev` on Unix based systems or `gulp.cmd dev` in Windows.
|
||||
12. The development server is now running at http://localhost:8082 and is proxying REST requests to dropwizard on port 8083.
|
||||
13. Connect to the server from your browser; you should have functional breakpoints and watches on assets.
|
||||
14. When you're done developing, create a fork at the top of https://github.com/Athou/CommaFeed page and commit your changes to it.
|
||||
15. If you'd like to contribute to CommaFeed, create a pull request from your repository to https://github.com/Athou/CommaFeed when your changes are ready. There's a button to do so at the top of https://github.com/Athou/CommaFeed.
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright 2013-2014 CommaFeed.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this work except in compliance with the License.
|
||||
You may obtain a copy of the License in the LICENSE file, or at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
40
bower.json
40
bower.json
@@ -2,32 +2,36 @@
|
||||
"name": "commafeed",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"jquery": "1.11.0",
|
||||
"jquery-ui": "1.11",
|
||||
"jquery": "2.1.3",
|
||||
"jquery-ui": "1.10.3",
|
||||
"jquery-mousewheel": "3.1.12",
|
||||
"lodash": "2.4.1",
|
||||
"bootstrap": "3.1.1",
|
||||
"lodash": "3.4.0",
|
||||
"bootstrap": "3.3.2",
|
||||
"font-awesome": "3.2.1",
|
||||
"angular": "1.2.16",
|
||||
"angular-resource": "1.2.16",
|
||||
"angular-route": "1.2.16",
|
||||
"angular-sanitize": "1.2.16",
|
||||
"angular-touch": "1.2.16",
|
||||
"angular-animate": "1.2.16",
|
||||
"angular-ui-router": "0.2.8",
|
||||
"angular": "1.3.14",
|
||||
"angular-resource": "1.3.14",
|
||||
"angular-route": "1.3.14",
|
||||
"angular-sanitize": "1.3.14",
|
||||
"angular-touch": "1.3.14",
|
||||
"angular-animate": "1.3.14",
|
||||
"angular-ui-router": "0.2.13",
|
||||
"angular-ui-utils": "0.1.0",
|
||||
"angular-ui-select2": "0.0.5",
|
||||
"angular-bootstrap": "0.2.0",
|
||||
"angular-loading-bar": "0.5.0",
|
||||
"angular-translate": "2.2.0",
|
||||
"angular-translate-loader-static-files": "2.2.0",
|
||||
"angular-loading-bar": "0.6.0",
|
||||
"angular-translate": "2.6.1",
|
||||
"angular-translate-loader-static-files": "2.6.1",
|
||||
"ngInfiniteScroll": "1.0.0",
|
||||
"ng-grid": "2.0.6",
|
||||
"mousetrap": "1.4.6",
|
||||
"momentjs": "2.6.0",
|
||||
"device.js": "matthewhudson/device.js#2ae5c775e35ccc837589e5af34e292c54936778c",
|
||||
"momentjs": "2.9.0",
|
||||
"devicejs": "0.2.4",
|
||||
"readabilicons": "arc90/readability-readabilicons#34c55561c5b8ec6e90714b50237c06b13cb9d59c",
|
||||
"zocial": "samcollins/css-social-buttons#1f59ecacde475e563fb6771667597493ec4eecb6",
|
||||
"swagger-ui": "2.0.21"
|
||||
"zocial-less": "1.0.0",
|
||||
"swagger-ui": "2.1.8-M1"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "1.3.14",
|
||||
"angular-translate": "2.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,15 @@ app:
|
||||
# wether to allow user registrations
|
||||
allowRegistrations: true
|
||||
|
||||
# create a demo account the first time the app starts
|
||||
createDemoAccount: false
|
||||
|
||||
# put your google analytics tracking code here
|
||||
googleAnalyticsTrackingCode:
|
||||
|
||||
# put your google server key (used for youtube favicon fetching)
|
||||
googleAuthKey:
|
||||
|
||||
# number of http threads
|
||||
backgroundThreads: 3
|
||||
|
||||
@@ -44,6 +50,9 @@ app:
|
||||
# time to keep unread statuses (in days), 0 to disable
|
||||
keepStatusDays: 0
|
||||
|
||||
# entries to keep per feed, old entries will be deleted, 0 to disable
|
||||
maxFeedCapacity: 500
|
||||
|
||||
# cache service to use, possible values are 'noop' and 'redis'
|
||||
cache: noop
|
||||
|
||||
@@ -66,16 +75,12 @@ app:
|
||||
|
||||
database:
|
||||
driverClass: org.h2.Driver
|
||||
url: jdbc:h2:./target/example
|
||||
url: jdbc:h2:./target/example;mv_store=false
|
||||
user: sa
|
||||
password: sa
|
||||
properties:
|
||||
charSet: UTF-8
|
||||
maxWaitForConnection: 1s
|
||||
validationQuery: "/* CommaFeed Health Check */ SELECT 1"
|
||||
minSize: 1
|
||||
maxSize: 50
|
||||
checkConnectionWhileIdle: true
|
||||
validationQuery: "/* CommaFeed Health Check */ SELECT 1"
|
||||
|
||||
server:
|
||||
applicationConnectors:
|
||||
@@ -84,12 +89,13 @@ server:
|
||||
adminConnectors:
|
||||
- type: http
|
||||
port: 8084
|
||||
|
||||
logging:
|
||||
level: INFO
|
||||
loggers:
|
||||
com.commafeed: DEBUG
|
||||
liquibase: INFO
|
||||
org.hibernate.SQL: ALL
|
||||
org.hibernate.SQL: INFO # or ALL for sql debugging
|
||||
org.hibernate.engine.internal.StatisticalLoggingSessionEventListener: WARN
|
||||
appenders:
|
||||
- type: console
|
||||
@@ -99,4 +105,16 @@ logging:
|
||||
archive: true
|
||||
archivedLogFilenamePattern: log/commafeed-%d.log
|
||||
archivedFileCount: 5
|
||||
timeZone: UTC
|
||||
timeZone: UTC
|
||||
|
||||
# Redis pool configuration
|
||||
# (only used if app.cache is 'redis')
|
||||
# -----------------------------------
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password:
|
||||
timeout: 2000
|
||||
database: 0
|
||||
maxTotal: 500
|
||||
|
||||
@@ -7,9 +7,15 @@ app:
|
||||
# wether to allow user registrations
|
||||
allowRegistrations: false
|
||||
|
||||
# create a demo account the first time the app starts
|
||||
createDemoAccount: false
|
||||
|
||||
# put your google analytics tracking code here
|
||||
googleAnalyticsTrackingCode:
|
||||
|
||||
# put your google server key (used for youtube favicon fetching)
|
||||
googleAuthKey:
|
||||
|
||||
# number of http threads
|
||||
backgroundThreads: 3
|
||||
|
||||
@@ -22,6 +28,7 @@ app:
|
||||
smtpTls: false
|
||||
smtpUserName:
|
||||
smtpPassword:
|
||||
smtpFromAddress:
|
||||
|
||||
# wether this commafeed instance has a lot of feeds to refresh
|
||||
# leave this to false in almost all cases
|
||||
@@ -44,6 +51,9 @@ app:
|
||||
# time to keep unread statuses (in days), 0 to disable
|
||||
keepStatusDays: 0
|
||||
|
||||
# entries to keep per feed, old entries will be deleted, 0 to disable
|
||||
maxFeedCapacity: 500
|
||||
|
||||
# cache service to use, possible values are 'noop' and 'redis'
|
||||
cache: noop
|
||||
|
||||
@@ -66,16 +76,15 @@ app:
|
||||
|
||||
database:
|
||||
driverClass: org.h2.Driver
|
||||
url: jdbc:h2:./target/example
|
||||
url: jdbc:h2:/home/commafeed/db;mv_store=false
|
||||
user: sa
|
||||
password: sa
|
||||
properties:
|
||||
charSet: UTF-8
|
||||
maxWaitForConnection: 1s
|
||||
validationQuery: "/* CommaFeed Health Check */ SELECT 1"
|
||||
minSize: 1
|
||||
maxSize: 50
|
||||
checkConnectionWhileIdle: true
|
||||
validationQuery: "/* CommaFeed Health Check */ SELECT 1"
|
||||
minSize: 1
|
||||
maxSize: 50
|
||||
maxConnectionAge: 30m
|
||||
|
||||
server:
|
||||
applicationConnectors:
|
||||
@@ -84,6 +93,7 @@ server:
|
||||
adminConnectors:
|
||||
- type: http
|
||||
port: 8084
|
||||
|
||||
logging:
|
||||
level: WARN
|
||||
loggers:
|
||||
@@ -98,4 +108,16 @@ logging:
|
||||
archive: true
|
||||
archivedLogFilenamePattern: log/commafeed-%d.log
|
||||
archivedFileCount: 5
|
||||
timeZone: UTC
|
||||
timeZone: UTC
|
||||
|
||||
# Redis pool configuration
|
||||
# (only used if app.cache is 'redis')
|
||||
# -----------------------------------
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password:
|
||||
timeout: 2000
|
||||
database: 0
|
||||
maxTotal: 500
|
||||
|
||||
@@ -1,283 +1,295 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="12">
|
||||
<profile kind="CodeFormatterProfile" name="Eclipse [built-in] 140 chars" version="12">
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.source" value="1.5"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="140"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.5"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="140"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.5"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="140"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="140"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
28
gulpfile.js
28
gulpfile.js
@@ -4,7 +4,6 @@ var revReplace = require('gulp-rev-replace');
|
||||
var minifyCSS = require('gulp-minify-css');
|
||||
var uglify = require('gulp-uglify');
|
||||
var filter = require('gulp-filter');
|
||||
var bower = require('gulp-bower');
|
||||
var connect = require('gulp-connect');
|
||||
var modRewrite = require('connect-modrewrite');
|
||||
var sass = require('gulp-sass');
|
||||
@@ -15,10 +14,6 @@ var SRC_DIR = 'src/main/app/';
|
||||
var TEMP_DIR = 'target/gulp/'
|
||||
var BUILD_DIR = 'target/classes/assets/';
|
||||
|
||||
gulp.task('bower', function() {
|
||||
return bower();
|
||||
});
|
||||
|
||||
gulp.task('images', function() {
|
||||
return gulp.src(SRC_DIR + 'images/**/*').pipe(gulp.dest(BUILD_DIR + 'images'));
|
||||
});
|
||||
@@ -27,31 +22,32 @@ gulp.task('i18n', function() {
|
||||
return gulp.src(SRC_DIR + 'i18n/**/*.js').pipe(gulp.dest(BUILD_DIR + 'i18n'));
|
||||
});
|
||||
|
||||
gulp.task('favicons', function() {
|
||||
gulp.task('resources', function() {
|
||||
var favicons_png = SRC_DIR + '*.png';
|
||||
var favicons_ico = SRC_DIR + '*.ico';
|
||||
var favicons_svg = SRC_DIR + '*.svg';
|
||||
return gulp.src([favicons_png, favicons_ico, favicons_svg]).pipe(gulp.dest(BUILD_DIR));
|
||||
var manifest = SRC_DIR + 'manifest.json';
|
||||
return gulp.src([favicons_png, favicons_ico, favicons_svg, manifest]).pipe(gulp.dest(BUILD_DIR));
|
||||
});
|
||||
|
||||
gulp.task('sass', function() {
|
||||
return gulp.src(SRC_DIR + 'sass/app.scss').pipe(sass()).pipe(gulp.dest(TEMP_DIR + 'css'));
|
||||
});
|
||||
|
||||
gulp.task('fonts', ['bower'], function() {
|
||||
gulp.task('fonts', function() {
|
||||
var font_awesome = SRC_DIR + 'lib/font-awesome/font/fontawesome-webfont.*';
|
||||
var zocial = SRC_DIR + 'lib/zocial/css/zocial-regular-*';
|
||||
var zocial = SRC_DIR + 'lib/zocial-less/css/zocial-regular-*';
|
||||
var readabilicons = SRC_DIR + 'lib/readabilicons/webfont/fonts/readabilicons-*';
|
||||
return gulp.src([font_awesome, zocial, readabilicons]).pipe(gulp.dest(BUILD_DIR + 'font'));
|
||||
});
|
||||
|
||||
gulp.task('select2', ['bower'], function() {
|
||||
gulp.task('select2', function() {
|
||||
var gif = SRC_DIR + 'lib/select2/*.gif';
|
||||
var png = SRC_DIR + 'lib/select2/*.png';
|
||||
return gulp.src([gif, png]).pipe(gulp.dest(BUILD_DIR + 'css'));
|
||||
});
|
||||
|
||||
gulp.task('swagger-ui', ['bower'], function() {
|
||||
gulp.task('swagger-ui', function() {
|
||||
var index_html = SRC_DIR + 'api/index.html';
|
||||
var lib = SRC_DIR + 'lib/swagger-ui/dist/**/*';
|
||||
return gulp.src([lib, index_html]).pipe(gulp.dest(BUILD_DIR + 'api'));
|
||||
@@ -65,17 +61,17 @@ gulp.task('template-cache', function() {
|
||||
return gulp.src(SRC_DIR + 'templates/**/*.html').pipe(templateCache(options)).pipe(gulp.dest(TEMP_DIR + 'js'));
|
||||
});
|
||||
|
||||
gulp.task('build-dev', ['images', 'i18n', 'favicons', 'sass', 'fonts', 'select2', 'swagger-ui', 'template-cache', 'bower'], function() {
|
||||
gulp.task('build-dev', ['images', 'i18n', 'resources', 'sass', 'fonts', 'select2', 'swagger-ui', 'template-cache'], function() {
|
||||
var assets = useref.assets({
|
||||
searchPath : [SRC_DIR, TEMP_DIR]
|
||||
});
|
||||
var jsFilter = filter("**/*.js");
|
||||
var cssFilter = filter("**/*.css");
|
||||
return gulp.src([SRC_DIR + 'index.html', TEMP_DIR + 'app.css']).pipe(assets).pipe(rev()).pipe(assets.restore()).pipe(useref()).pipe(
|
||||
revReplace()).pipe(gulp.dest(BUILD_DIR));
|
||||
revReplace()).pipe(gulp.dest(BUILD_DIR)).pipe(connect.reload());
|
||||
});
|
||||
|
||||
gulp.task('build', ['images', 'i18n', 'favicons', 'sass', 'fonts', 'select2', 'swagger-ui', 'template-cache', 'bower'], function() {
|
||||
gulp.task('build', ['images', 'i18n', 'resources', 'sass', 'fonts', 'select2', 'swagger-ui', 'template-cache'], function() {
|
||||
var assets = useref.assets({
|
||||
searchPath : [SRC_DIR, TEMP_DIR]
|
||||
});
|
||||
@@ -101,7 +97,9 @@ gulp.task('serve', function() {
|
||||
connect.server({
|
||||
root : BUILD_DIR,
|
||||
port : 8082,
|
||||
livereload : true,
|
||||
middleware : function() {
|
||||
var api = '^/api/(.*)$ http://localhost:8083/rest/$1 [P]';
|
||||
var rest = '^/rest/(.*)$ http://localhost:8083/rest/$1 [P]';
|
||||
var next = '^/next(.*)$ http://localhost:8083/next$1 [P]';
|
||||
var logout = '^/logout$ http://localhost:8083/logout [P]';
|
||||
@@ -113,4 +111,4 @@ gulp.task('serve', function() {
|
||||
});
|
||||
|
||||
gulp.task('dev', ['build-dev', 'watch', 'serve']);
|
||||
gulp.task('default', ['build']);
|
||||
gulp.task('default', ['build']);
|
||||
|
||||
24
package.json
24
package.json
@@ -4,17 +4,17 @@
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"gulp": "3.8.7",
|
||||
"gulp-rev": "1.0.0",
|
||||
"gulp-rev-replace": "0.3.0",
|
||||
"gulp-minify-css": "0.3.7",
|
||||
"gulp-uglify": "0.3.1",
|
||||
"gulp-filter": "1.0.0",
|
||||
"gulp-bower": "0.0.6",
|
||||
"gulp-connect": "2.0.6",
|
||||
"connect-modrewrite": "0.7.7",
|
||||
"gulp-sass": "0.7.2",
|
||||
"gulp-useref": "0.6.0",
|
||||
"gulp-angular-templatecache": "1.3.0"
|
||||
"bower": "1.4.1",
|
||||
"gulp": "3.8.11",
|
||||
"gulp-rev": "4.0.0",
|
||||
"gulp-rev-replace": "0.4.1",
|
||||
"gulp-minify-css": "1.1.5",
|
||||
"gulp-uglify": "1.2.0",
|
||||
"gulp-filter": "2.0.2",
|
||||
"gulp-connect": "2.2.0",
|
||||
"connect-modrewrite": "0.8.1",
|
||||
"gulp-sass": "2.0.1",
|
||||
"gulp-useref": "1.1.2",
|
||||
"gulp-angular-templatecache": "1.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
243
pom.xml
243
pom.xml
@@ -4,19 +4,29 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>2.0.1</version>
|
||||
<version>2.2.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>CommaFeed</name>
|
||||
|
||||
<prerequisites>
|
||||
<maven>3.0.0</maven>
|
||||
<maven>3.0.5</maven>
|
||||
</prerequisites>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<dropwizard.version>0.7.1</dropwizard.version>
|
||||
<java.version>1.8</java.version>
|
||||
<dropwizard.version>0.8.1</dropwizard.version>
|
||||
<guice.version>4.0</guice.version>
|
||||
<querydsl.version>3.6.4</querydsl.version>
|
||||
<rome.version>1.5.0</rome.version>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://github.com/Athou/commafeed.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/Athou/commafeed.git</developerConnection>
|
||||
<url>https://github.com/Athou/commafeed</url>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<finalName>commafeed</finalName>
|
||||
<resources>
|
||||
@@ -25,20 +35,53 @@
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</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>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>2.1.7</version>
|
||||
<version>2.1.13</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -49,6 +92,7 @@
|
||||
<configuration>
|
||||
<generateGitPropertiesFile>false</generateGitPropertiesFile>
|
||||
<failOnNoGitDirectory>false</failOnNoGitDirectory>
|
||||
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -88,7 +132,7 @@
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>0.0.15</version>
|
||||
<version>0.0.22</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>install node and npm</id>
|
||||
@@ -97,8 +141,8 @@
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<nodeVersion>v0.10.30</nodeVersion>
|
||||
<npmVersion>1.3.8</npmVersion>
|
||||
<nodeVersion>v0.12.4</nodeVersion>
|
||||
<npmVersion>2.10.1</npmVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
@@ -108,6 +152,15 @@
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>bower install</id>
|
||||
<goals>
|
||||
<goal>bower</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>install</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>gulp build</id>
|
||||
<goals>
|
||||
@@ -117,6 +170,18 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -124,13 +189,24 @@
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.14.4</version>
|
||||
<version>1.16.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<version>1.7.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>${guice.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject.extensions</groupId>
|
||||
<artifactId>guice-multibindings</artifactId>
|
||||
<version>${guice.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -143,11 +219,6 @@
|
||||
<artifactId>dropwizard-hibernate</artifactId>
|
||||
<version>${dropwizard.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-client</artifactId>
|
||||
<version>${dropwizard.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-migrations</artifactId>
|
||||
@@ -159,36 +230,52 @@
|
||||
<version>${dropwizard.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.wordnik</groupId>
|
||||
<artifactId>swagger-jaxrs_2.10</artifactId>
|
||||
<version>1.3.7</version>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-forms</artifactId>
|
||||
<version>${dropwizard.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>jsr311-api</artifactId>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>javassist</artifactId>
|
||||
<groupId>javassist</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.wordnik</groupId>
|
||||
<artifactId>swagger-jaxrs</artifactId>
|
||||
<version>1.5.3-M1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>jsr311-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
<version>3.4.2</version>
|
||||
<version>${querydsl.version}</version>
|
||||
<scope>provided</scope>
|
||||
<classifier>hibernate</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<artifactId>querydsl-jpa</artifactId>
|
||||
<version>3.4.2</version>
|
||||
<version>${querydsl.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey.contribs</groupId>
|
||||
<artifactId>jersey-multipart</artifactId>
|
||||
<version>1.18.1</version>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>18.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
@@ -196,92 +283,108 @@
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.9</version>
|
||||
<version>1.10</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-math</artifactId>
|
||||
<version>2.2</version>
|
||||
<artifactId>commons-math3</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-jexl</artifactId>
|
||||
<version>2.1.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<version>2.7.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
<version>1.5.2</version>
|
||||
<version>1.5.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- upgrade jdom to 2.0.5 for performance reasons (https://github.com/hunterhacker/jdom/issues/112) -->
|
||||
<dependency>
|
||||
<groupId>net.java.dev.rome</groupId>
|
||||
<groupId>com.rometools</groupId>
|
||||
<artifactId>rome</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>${rome.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>jdom</groupId>
|
||||
<artifactId>jdom</artifactId>
|
||||
<groupId>org.jdom</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.rometools</groupId>
|
||||
<groupId>com.rometools</groupId>
|
||||
<artifactId>rome-opml</artifactId>
|
||||
<version>1.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>rome</groupId>
|
||||
<artifactId>rome</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<version>${rome.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jdom</groupId>
|
||||
<artifactId>jdom</artifactId>
|
||||
<version>1.1.3</version>
|
||||
<artifactId>jdom2</artifactId>
|
||||
<version>2.0.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.7.3</version>
|
||||
<version>1.8.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.juniversalchardet</groupId>
|
||||
<artifactId>juniversalchardet</artifactId>
|
||||
<version>1.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.gwt</groupId>
|
||||
<artifactId>gwt-servlet</artifactId>
|
||||
<version>2.6.1</version>
|
||||
<groupId>com.ibm.icu</groupId>
|
||||
<artifactId>icu4j</artifactId>
|
||||
<version>55.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.cssparser</groupId>
|
||||
<artifactId>cssparser</artifactId>
|
||||
<version>0.9.14</version>
|
||||
<version>0.9.16</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-youtube</artifactId>
|
||||
<version>v3-rev139-1.20.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava-jdk5</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.181</version>
|
||||
<version>1.4.187</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.32</version>
|
||||
<version>5.1.35</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>postgresql</groupId>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>9.1-901-1.jdbc4</version>
|
||||
<version>9.4-1201-jdbc41</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.jtds</groupId>
|
||||
@@ -292,8 +395,14 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>2.0.11-beta</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Swagger UI</title>
|
||||
<link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
|
||||
<link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
|
||||
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
|
||||
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
|
||||
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
|
||||
@@ -12,25 +12,23 @@
|
||||
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
|
||||
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
|
||||
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
|
||||
<script src='lib/handlebars-1.0.0.js' type='text/javascript'></script>
|
||||
<script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
|
||||
<script src='lib/underscore-min.js' type='text/javascript'></script>
|
||||
<script src='lib/backbone-min.js' type='text/javascript'></script>
|
||||
<script src='lib/swagger.js' type='text/javascript'></script>
|
||||
<script src='lib/swagger-client.js' type='text/javascript'></script>
|
||||
<script src='swagger-ui.js' type='text/javascript'></script>
|
||||
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
|
||||
<script src='lib/marked.js' type='text/javascript'></script>
|
||||
|
||||
<!-- enabling this will enable oauth2 implicit scope support -->
|
||||
<script src='lib/swagger-oauth.js' type='text/javascript'></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
window.swaggerUi = new SwaggerUi({
|
||||
url: "../rest/api-docs",
|
||||
url: "../rest/swagger.json",
|
||||
dom_id: "swagger-ui-container",
|
||||
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
|
||||
onComplete: function(swaggerApi, swaggerUi){
|
||||
log("Loaded SwaggerUI");
|
||||
|
||||
if(typeof initOAuth == "function") {
|
||||
/*
|
||||
initOAuth({
|
||||
|
||||
BIN
src/main/app/app-icon-192.png
Normal file
BIN
src/main/app/app-icon-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
@@ -98,6 +98,8 @@
|
||||
"next_refresh" : "Next refresh",
|
||||
"queued_for_refresh" : "Queued for refresh",
|
||||
"feed_url" : "Feed URL",
|
||||
"filtering_expression" : "Filtering expression",
|
||||
"filtering_expression_help" : "If not empty, an expression evaluating to 'true' or 'false'. If false, new entries for this feed will be marked as read automatically.\nAvailable variables are 'title', 'content', 'url' 'author' and 'categories' and their content is converted to lower case to ease string comparison.\nExample: url.contains('youtube') or (author eq 'athou' and title.contains('github').\nComplete available syntax is available <a href='http://commons.apache.org/proper/commons-jexl/reference/syntax.html' target='_blank'>here</a>.",
|
||||
"generate_api_key_first" : "Generate an API key in your profile first.",
|
||||
"unsubscribe" : "Unsubscribe",
|
||||
"unsubscribe_confirmation" : "Are you sure you want to unsubscribe from this feed?",
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"new_category" : {
|
||||
"name" : "Nome",
|
||||
"parent" : "Pai"
|
||||
"parent" : "Subcategoría de "
|
||||
},
|
||||
"toolbar" : {
|
||||
"unread" : "Sen Ler",
|
||||
@@ -39,12 +39,12 @@
|
||||
"previous_entry" : "Entrada Anterior",
|
||||
"next_entry" : "Próxima Entrada",
|
||||
"refresh" : "Actualizar",
|
||||
"refresh_all" : "Force refresh all my feeds ",
|
||||
"refresh_all" : "Forzar a actualización de todas as fontes ",
|
||||
"sort_by_asc_desc" : "Ordenar por data asc/desc",
|
||||
"titles_only" : "Só títulos",
|
||||
"expanded_view" : "Vista expandida",
|
||||
"mark_all_as_read" : "Marcar todos como lidos",
|
||||
"mark_all_older_12_hours" : "Items older than 12 hours ",
|
||||
"mark_all_older_12_hours" : "Elementos anteriores a 12 h. ",
|
||||
"mark_all_older_day" : "Artigos anteriores a un día",
|
||||
"mark_all_older_week" : "Artigos de máis de unha semana",
|
||||
"mark_all_older_two_weeks" : "Artigos de máis de dúas semanas",
|
||||
@@ -56,14 +56,14 @@
|
||||
"donate" : "Doar"
|
||||
},
|
||||
"view" : {
|
||||
"entry_source" : "from ",
|
||||
"entry_author" : "by ",
|
||||
"entry_source" : "desde ",
|
||||
"entry_author" : "por ",
|
||||
"error_while_loading_feed" : "Erro mentras se cargaba esta fonte",
|
||||
"keep_unread" : "Gardar non lidos",
|
||||
"no_unread_items" : "non ten elementos sen ler.",
|
||||
"mark_up_to_here" : "Mark as read up to here ",
|
||||
"search_for" : "searching for: ",
|
||||
"no_search_results" : "No match found for the requested keywords "
|
||||
"mark_up_to_here" : "Marcar como lidos ate aquí ",
|
||||
"search_for" : "buscando por: ",
|
||||
"no_search_results" : "Sen coincidencias para as palabras introducidas "
|
||||
},
|
||||
"feedsearch" : {
|
||||
"hint" : "Escriba unha suscrición...",
|
||||
@@ -80,8 +80,8 @@
|
||||
"scroll_marks" : "En vista expandida, o desplazamento polas entradas márcaas como lidas."
|
||||
},
|
||||
"appearance" : "Aspecto",
|
||||
"scroll_speed" : "Scrolling speed when navigating between entries (in milliseconds) ",
|
||||
"scroll_speed_help" : "set to 0 to disable ",
|
||||
"scroll_speed" : "Velocidade de desplazamento navegando entre entradas (en milisegundos) ",
|
||||
"scroll_speed_help" : "escriba 0 para deshabilitar ",
|
||||
"theme" : "Decorado",
|
||||
"submit_your_theme" : "Envíe o seu decorado",
|
||||
"custom_css" : "CSS Personalizado"
|
||||
@@ -89,21 +89,21 @@
|
||||
"details" : {
|
||||
"feed_details" : "Detalles de fontes",
|
||||
"url" : "URL",
|
||||
"website" : "Website ",
|
||||
"website" : "Sitio web ",
|
||||
"name" : "Nome",
|
||||
"category" : "Categoría",
|
||||
"position" : "Position ",
|
||||
"position" : "Posición ",
|
||||
"last_refresh" : "Última actualización",
|
||||
"message" : "Last refresh message ",
|
||||
"message" : "Última mensaxe da actualización ",
|
||||
"next_refresh" : "Próxima actualización",
|
||||
"queued_for_refresh" : "En cola para actualizar",
|
||||
"feed_url" : "URL da fonte",
|
||||
"generate_api_key_first" : "Antes debes xerar unha chave API no teu perfil.",
|
||||
"unsubscribe" : "Rematar suscripción",
|
||||
"unsubscribe_confirmation" : "Are you sure you want to unsubscribe from this feed? ",
|
||||
"delete_category_confirmation" : "Are you sure you want to delete this category? ",
|
||||
"unsubscribe_confirmation" : "Seguro que queres desuscribirte de esta fonte? ",
|
||||
"delete_category_confirmation" : "Seguro que queres eliminar esta categoría? ",
|
||||
"category_details" : "Detalles da categoría",
|
||||
"tag_details" : "Tag details ",
|
||||
"tag_details" : "Detalles da etiqueta ",
|
||||
"parent_category" : "Categoría principal"
|
||||
},
|
||||
"profile" : {
|
||||
@@ -119,7 +119,7 @@
|
||||
"generate_new_api_key_info" : "Ao cambiar o contrasinal xerarase unha nova chave API",
|
||||
"opml_export" : "Exportación de OPML",
|
||||
"delete_account" : "Eliminar conta",
|
||||
"delete_account_confirmation" : "Delete your acount? There's no turning back! "
|
||||
"delete_account_confirmation" : "Eliminar conta? Non hai volta atrás! "
|
||||
},
|
||||
"about" : {
|
||||
"rest_api" : {
|
||||
@@ -158,7 +158,7 @@
|
||||
"open_next_entry" : "abrir próxima entrada",
|
||||
"open_previous_entry" : "abrir entrada anterior",
|
||||
"spacebar" : "space/shift+space ",
|
||||
"move_page_down_up" : "moves the page down/up ",
|
||||
"move_page_down_up" : "move a páxina arriba/abaixo ",
|
||||
"focus_next_entry" : "Establecer o foco na próxima entrada sen abrila",
|
||||
"focus_previous_entry" : "Establecer o foco na entrada anterior sen abrila",
|
||||
"open_next_feed" : "abrir a seguinte fonte ou categoría",
|
||||
@@ -170,11 +170,11 @@
|
||||
"mark_current_entry" : "marcar como lida/non lida a entrada actual",
|
||||
"mark_all_as_read" : "marcar todas as entradas como lidas",
|
||||
"open_in_new_tab_mark_as_read" : "abrir entrada nunha nova lapela e marcar como lida",
|
||||
"fullscreen" : "toggle full screen mode ",
|
||||
"font_size" : "increase/decrease font size of the current entry ",
|
||||
"go_to_all" : "go to the All view ",
|
||||
"go_to_starred" : "go to the Starred view ",
|
||||
"fullscreen" : "habilita a pantalla completa ",
|
||||
"font_size" : "aumenta/diminúe o tamaño da letra da entrada activa ",
|
||||
"go_to_all" : "ir a vista TODOS",
|
||||
"go_to_starred" : "ir a vista Destacados ",
|
||||
"feed_search" : "navegue ate unha suscrición introducindo o nome da suscrición"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
"save" : "Salva",
|
||||
"cancel" : "Cancella",
|
||||
"delete" : "Elimina",
|
||||
"required" : "Required",
|
||||
"required" : "Richiesto",
|
||||
"download" : "Download",
|
||||
"link" : "Link",
|
||||
"bookmark" : "Segnalibro ",
|
||||
"bookmark" : "Segnalibro",
|
||||
"close" : "Chiudi",
|
||||
"tags" : "Tags "
|
||||
"tags" : "Etichette "
|
||||
},
|
||||
"tree" : {
|
||||
"subscribe" : "Iscriviti",
|
||||
"subscribe" : "Abbonati",
|
||||
"import" : "Importa",
|
||||
"new_category" : "Nuova categoria",
|
||||
"all" : "Tutto",
|
||||
@@ -19,19 +19,19 @@
|
||||
},
|
||||
"subscribe" : {
|
||||
"feed_url" : "Feed URL",
|
||||
"feed_name" : "Nome Feed",
|
||||
"feed_name" : "Nome feed",
|
||||
"category" : "Categoria"
|
||||
},
|
||||
"import" : {
|
||||
"google_reader_prefix" : "Importa i tuoi feed dal tuo ",
|
||||
"google_reader_prefix" : "Permettimi di importare i tuoi feed dal tuo ",
|
||||
"google_reader_suffix" : " account.",
|
||||
"google_download" : "Oppure, carica il tuo subscriptions.xml",
|
||||
"google_download_link" : "Scaricalo da qui",
|
||||
"google_download" : "Oppure, carica il tuo file subscriptions.xml.",
|
||||
"google_download_link" : "Scaricalo da qui.",
|
||||
"xml_file" : "OPML File"
|
||||
},
|
||||
"new_category" : {
|
||||
"name" : "Nome",
|
||||
"parent" : "Parent"
|
||||
"parent" : "Gruppo"
|
||||
},
|
||||
"toolbar" : {
|
||||
"unread" : "Non letti",
|
||||
@@ -39,12 +39,12 @@
|
||||
"previous_entry" : "Precedente",
|
||||
"next_entry" : "Successivo",
|
||||
"refresh" : "Ricarica",
|
||||
"refresh_all" : "Force refresh all my feeds ",
|
||||
"sort_by_asc_desc" : "Sort by date asc/desc",
|
||||
"titles_only" : "Solo titoli",
|
||||
"refresh_all" : "Forza l'aggiornamento di tutte i miei feed",
|
||||
"sort_by_asc_desc" : "Ordina per data ascendente/decrescente",
|
||||
"titles_only" : "Solo i titoli",
|
||||
"expanded_view" : "Espandi",
|
||||
"mark_all_as_read" : "Contrassegna tutto come già letto",
|
||||
"mark_all_older_12_hours" : "Items older than 12 hours ",
|
||||
"mark_all_as_read" : "Segna tutto come già letto",
|
||||
"mark_all_older_12_hours" : "Elementi più vecchi di 12 ore",
|
||||
"mark_all_older_day" : "Elementi più vecchi di un giorno",
|
||||
"mark_all_older_week" : "Elementi più vecchi di una settimana",
|
||||
"mark_all_older_two_weeks" : "Elementi più vecchi di due settimane",
|
||||
@@ -52,129 +52,131 @@
|
||||
"profile" : "Profilo",
|
||||
"admin" : "Admin",
|
||||
"about" : "Informazioni",
|
||||
"logout" : "Logout",
|
||||
"logout" : "Esci",
|
||||
"donate" : "Dona"
|
||||
},
|
||||
"view" : {
|
||||
"entry_source" : "from ",
|
||||
"entry_author" : "by ",
|
||||
"error_while_loading_feed" : "Si è verificato un errore, mentre caricavo il feed",
|
||||
"keep_unread" : "Mantiene come da leggere",
|
||||
"entry_source" : "da ",
|
||||
"entry_author" : "di ",
|
||||
"error_while_loading_feed" : "Si è verificato un errore durante il caricamento di questo feed",
|
||||
"keep_unread" : "Mantiene come non leggere",
|
||||
"no_unread_items" : "Non ci sono elementi da leggere.",
|
||||
"mark_up_to_here" : "Mark as read up to here ",
|
||||
"search_for" : "searching for: ",
|
||||
"no_search_results" : "No match found for the requested keywords "
|
||||
"mark_up_to_here" : "Segna come letto fino qui",
|
||||
"search_for" : "cercando: ",
|
||||
"no_search_results" : "Nessun risultato trovato per le parole chiave cercate"
|
||||
},
|
||||
"feedsearch" : {
|
||||
"hint" : "Type in a subscription... ",
|
||||
"help" : "Use the return key to select and arrow keys to navigate. ",
|
||||
"result_prefix" : "Le tue sottoscrizioni"
|
||||
"hint" : "Digita in una sottoscrizione... ",
|
||||
"help" : "Usa il tasto invio per selezionare e le frecce per navigare.",
|
||||
"result_prefix" : "Le tue sottoscrizioni:"
|
||||
},
|
||||
"settings" : {
|
||||
"general" : {
|
||||
"value" : "Generali",
|
||||
"language" : "Lingua",
|
||||
"language_contribute" : "Contribuisci con le traduzioni",
|
||||
"show_unread" : "Show feeds and categories with no unread entries",
|
||||
"social_buttons" : "Visualizza i social button",
|
||||
"scroll_marks" : "Marca come letto quando scorri"
|
||||
"language_contribute" : "Contribuisci nelle traduzioni",
|
||||
"show_unread" : "Mostra i feed e le categorie con elementi non letti",
|
||||
"social_buttons" : "Mostra i pulsanti social network di condivisione",
|
||||
"scroll_marks" : "In modalità estesa, segna come letto le voci quando scorri"
|
||||
},
|
||||
"appearance" : "Appearance ",
|
||||
"scroll_speed" : "Scrolling speed when navigating between entries (in milliseconds) ",
|
||||
"scroll_speed_help" : "set to 0 to disable ",
|
||||
"theme" : "Tema ",
|
||||
"submit_your_theme" : "Sottoponi il tuo tema ",
|
||||
"custom_css" : "Css modificato"
|
||||
"appearance" : "Aspetto",
|
||||
"scroll_speed" : "Velocità dello scorrimento durante la navigazione tra i feed (in millisecondi) ",
|
||||
"scroll_speed_help" : "Imposta 0 per disabilitare",
|
||||
"theme" : "Tema",
|
||||
"submit_your_theme" : "Proponi il tuo tema",
|
||||
"custom_css" : "CSS personalizzato"
|
||||
},
|
||||
"details" : {
|
||||
"feed_details" : "Dettagli feed",
|
||||
"url" : "URL",
|
||||
"website" : "Website ",
|
||||
"url" : "URL ",
|
||||
"website" : "Sito Web",
|
||||
"name" : "Nome",
|
||||
"category" : "Categoria",
|
||||
"position" : "Posizione ",
|
||||
"position" : "Posizione",
|
||||
"last_refresh" : "Ultimo aggiornamento",
|
||||
"message" : "Last refresh message ",
|
||||
"next_refresh" : "Next refresh ",
|
||||
"queued_for_refresh" : "In attesa per l'aggiornamento ",
|
||||
"feed_url" : "Feed URL",
|
||||
"generate_api_key_first" : "Generate an API key in your profile first.",
|
||||
"unsubscribe" : "Annulla l\"'\"iscrizione",
|
||||
"unsubscribe_confirmation" : "Are you sure you want to unsubscribe from this feed? ",
|
||||
"delete_category_confirmation" : "Are you sure you want to delete this category? ",
|
||||
"message" : "Ultimo messaggio di aggiornamento",
|
||||
"next_refresh" : "Prossimo aggiornamento",
|
||||
"queued_for_refresh" : "In attesa per l'aggiornamento",
|
||||
"feed_url" : "URL del feed ",
|
||||
"filtering_expression" : "Espressione del filtro",
|
||||
"filtering_expression_help" : "Se non è vuoto, una espressione viene misurata in 'true' o 'false'. Se falsa, i nuovi elementi di questo feed verranno segnati automaticamente come letti.\nLe variabili accettate sono 'title', 'content', 'url' 'author' e 'categories' e il loro contenuto è convertito in minuscolo per una facile confronto di stringhe.\Esempio: url.contains('youtube') o (author eq 'athou' and title.contains('github').\nLa sintassi completa è disponibile <a href='http://commons.apache.org/proper/commons-jexl/reference/syntax.html' target='_blank'>qui</a>.",
|
||||
"generate_api_key_first" : "Genera prima una chiave API nelle impostazioni del tuo profilo.",
|
||||
"unsubscribe" : "Annulla la sottoscrizione",
|
||||
"unsubscribe_confirmation" : "Sei sicuro di voler annullare la sottoscrizione da questo feed?",
|
||||
"delete_category_confirmation" : "Sei sicuro di voler eliminare questa categoria?",
|
||||
"category_details" : "Dettagli categoria",
|
||||
"tag_details" : "Tag details ",
|
||||
"parent_category" : "Parent category"
|
||||
"tag_details" : "Dettagli etichette ",
|
||||
"parent_category" : "Categoria principale"
|
||||
},
|
||||
"profile" : {
|
||||
"user_name" : "User name",
|
||||
"user_name" : "Nome utente",
|
||||
"email" : "E-mail",
|
||||
"change_password" : "Cambia password",
|
||||
"confirm_password" : "Conferma password",
|
||||
"minimum_6_chars" : "Minimo 6 caratteri",
|
||||
"passwords_do_not_match" : "Le password non corrispondono",
|
||||
"api_key" : "API key",
|
||||
"api_key_not_generated" : "Non generata ancora",
|
||||
"generate_new_api_key" : "Genera una nuova chiave API",
|
||||
"generate_new_api_key_info" : "Cambiando la password sarà generata una nuova chiave API",
|
||||
"api_key" : "chiave API",
|
||||
"api_key_not_generated" : "Non ancora generata",
|
||||
"generate_new_api_key" : "Genera una nuova chiave API ",
|
||||
"generate_new_api_key_info" : "Cambiando la password sarà generata una nuova chiave API ì",
|
||||
"opml_export" : "Esporta OPML",
|
||||
"delete_account" : "Elimina account",
|
||||
"delete_account_confirmation" : "Delete your acount? There's no turning back! "
|
||||
"delete_account" : "Elimina il profilo",
|
||||
"delete_account_confirmation" : "Eliminare il tuo profilo? Non si può tornare indietro!"
|
||||
},
|
||||
"about" : {
|
||||
"rest_api" : {
|
||||
"value" : "REST API",
|
||||
"line1" : "CommaFeed is built on top of JAX-RS and AngularJS. As such, a REST API is available.",
|
||||
"link_to_documentation" : "Link alla documentazione."
|
||||
"line1" : "CommaFeed è costruito sopra JAX-RS e AngularJS. Ed ovviamente, una REST API è disponibile.",
|
||||
"link_to_documentation" : "Collegamento alla documentazione."
|
||||
},
|
||||
"keyboard_shortcuts" : "Scorciatoie da tastiera",
|
||||
"version" : "CommaFeed version ",
|
||||
"line1_prefix" : "Commefeed è un progetto open-source. I codici sono hostati su ",
|
||||
"keyboard_shortcuts" : "Scorciatoie da tastiera",
|
||||
"version" : "Versione di CommaFeed",
|
||||
"line1_prefix" : "CommaFeed è un progetto open source. I codici sono ospitati su ",
|
||||
"line1_suffix" : ".",
|
||||
"line2_prefix" : "Se hai qualche problema, segnalalo sulla pagina del ",
|
||||
"line2_suffix" : " progetto.",
|
||||
"line3" : "Se ti piace il progetto, prendi in considerazione una donazione per supportare lo sviluppatore e contribuire a coprire i costi di mantenenimento di questo sito on-line.",
|
||||
"line4" : "Per chi preferisce Bitcoin, questo è l\"'\"indirizzo ",
|
||||
"line3" : "Se ti piace il progetto, considera una donazione per supportare lo sviluppatore ed a aiutare per coprire i costi di mantenenimento di questo sito online.",
|
||||
"line4" : "Se preferisci i Bitcoin, questo è l'indirizzo",
|
||||
"goodies" : {
|
||||
"value" : "Goodies",
|
||||
"android_app" : "Android app ",
|
||||
"subscribe_url" : "Subscribe URL ",
|
||||
"chrome_extension" : "Estenzione per Chrome ",
|
||||
"android_app" : "Applicazione Android",
|
||||
"subscribe_url" : "Sottoscrivi URL",
|
||||
"chrome_extension" : "Estensione per Chrome",
|
||||
"firefox_extension" : "Estensione per Firefox",
|
||||
"opera_extension" : "Estensione per Opera",
|
||||
"subscribe_bookmarklet" : "Add subscription bookmarklet (click) ",
|
||||
"subscribe_bookmarklet_asc" : "Oldest first ",
|
||||
"subscribe_bookmarklet_desc" : "Newest first ",
|
||||
"next_unread_bookmarklet" : "Next unread item bookmarklet (drag to bookmark bar) "
|
||||
"subscribe_bookmarklet" : "Aggiungi la sottoscrizione ai segnalibri (clicca)",
|
||||
"subscribe_bookmarklet_asc" : "I più vecchi prima",
|
||||
"subscribe_bookmarklet_desc" : "I più nuovi prima",
|
||||
"next_unread_bookmarklet" : "Prossimo elemento non letto nei segnalibri (trascinali nella barra dei segnalibri)"
|
||||
},
|
||||
"translation" : {
|
||||
"value" : "Traduzioni",
|
||||
"message" : "Abbiamo bisogno del tuo aiuto per tradurre CommaFeed.",
|
||||
"link" : "Vedi come aiutare con le traduzioni."
|
||||
"link" : "Vedi come aiutarci nella traduzioni."
|
||||
},
|
||||
"announcements" : "Annunci",
|
||||
"shortcuts" : {
|
||||
"mouse_middleclick" : "mouse middleclick",
|
||||
"open_next_entry" : "open next entry",
|
||||
"open_previous_entry" : "open previous entry",
|
||||
"spacebar" : "space/shift+space ",
|
||||
"move_page_down_up" : "moves the page down/up ",
|
||||
"focus_next_entry" : "set focus on next entry without opening it ",
|
||||
"focus_previous_entry" : "set focus on previous entry without opening it ",
|
||||
"open_next_feed" : "open next feed or category ",
|
||||
"open_previous_feed" : "open previous feed or category ",
|
||||
"open_close_current_entry" : "open/close current entry",
|
||||
"open_current_entry_in_new_window" : "open current entry in a new window",
|
||||
"open_current_entry_in_new_window_background" : "open current entry in a new window in the background ",
|
||||
"star_unstar" : "star/unstar current entry",
|
||||
"mark_current_entry" : "mark as read/unread current entry",
|
||||
"mark_all_as_read" : "mark all entries as read",
|
||||
"open_in_new_tab_mark_as_read" : "open entry in new tab and mark as read",
|
||||
"fullscreen" : "toggle full screen mode ",
|
||||
"font_size" : "increase/decrease font size of the current entry ",
|
||||
"go_to_all" : "go to the All view ",
|
||||
"go_to_starred" : "go to the Starred view ",
|
||||
"feed_search" : "navigate to a subscription by entering the subscription name "
|
||||
"mouse_middleclick" : "click centrale del mouse",
|
||||
"open_next_entry" : "apri l'elemento successivo",
|
||||
"open_previous_entry" : "apri l'elemento precedente",
|
||||
"spacebar" : "spazio/shift+spazio",
|
||||
"move_page_down_up" : "muovi la pagina sopra/sotto",
|
||||
"focus_next_entry" : "imposta il fuoco sull'elemento successivo senza aprirlo",
|
||||
"focus_previous_entry" : "imposta il fuoco sull'elemento precedente senza aprirlo",
|
||||
"open_next_feed" : "apri il feed successivo od una categoria",
|
||||
"open_previous_feed" : "apri il feed precedente od una categoria",
|
||||
"open_close_current_entry" : "apri/chiusi la categoria corrente",
|
||||
"open_current_entry_in_new_window" : "apri il corrente elemento in una nuova finestra",
|
||||
"open_current_entry_in_new_window_background" : "apri il corrente elemento in una nuova finestra in secondo piano",
|
||||
"star_unstar" : "segna/togli il segno all'elemento corrente",
|
||||
"mark_current_entry" : "segna come letto/non letto l'elemento corrente",
|
||||
"mark_all_as_read" : "segna come letti tutti gli elementi",
|
||||
"open_in_new_tab_mark_as_read" : "apri l'elemento in una nuova finestra e segnala come letta",
|
||||
"fullscreen" : "alterna la modalità a schermo intero",
|
||||
"font_size" : "aumenta/decrementa la grandezza del font dell'elemento corrente",
|
||||
"go_to_all" : "vai nella visione totale",
|
||||
"go_to_starred" : "vai nella visione dei preferiti",
|
||||
"feed_search" : "naviga in una sottoscrizione scrivendo il suo nome"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"link" : "Bağlantı",
|
||||
"bookmark" : "Yer imi",
|
||||
"close" : "Kapat",
|
||||
"tags" : "Tags "
|
||||
"tags" : "Etiketler "
|
||||
},
|
||||
"tree" : {
|
||||
"subscribe" : "Abone ol",
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"import" : {
|
||||
"google_reader_prefix" : "Aboneliklerinizi ",
|
||||
"google_reader_suffix" : " hesabınızdan aktarmama izin verin.",
|
||||
"google_reader_suffix" : "Hesabınızdan aktarmama izin verin.",
|
||||
"google_download" : "Veya, subscriptions.xml dosyanızı yükleyin.",
|
||||
"google_download_link" : "Buradan indirebilirsiniz.",
|
||||
"xml_file" : "OPML dosyası"
|
||||
@@ -39,15 +39,15 @@
|
||||
"previous_entry" : "Önceki ileti",
|
||||
"next_entry" : "Sonraki ileti",
|
||||
"refresh" : "Yenile",
|
||||
"refresh_all" : "Force refresh all my feeds ",
|
||||
"refresh_all" : "Tüm yayınları yenilemek için zorla",
|
||||
"sort_by_asc_desc" : "Tarihe göre sırala artan/azalan",
|
||||
"titles_only" : "Sadece başlıklar",
|
||||
"expanded_view" : "Genişletilmiş görünüm",
|
||||
"mark_all_as_read" : "Tümünü okundu işaretle",
|
||||
"mark_all_older_12_hours" : "Items older than 12 hours ",
|
||||
"mark_all_older_day" : "Bir günden eski yazılar",
|
||||
"mark_all_older_week" : "Bir haftadan eski yazılar",
|
||||
"mark_all_older_two_weeks" : "İki haftadan eski yazılar",
|
||||
"mark_all_older_12_hours" : "12 saatten daha eski yayınlar ",
|
||||
"mark_all_older_day" : "Bir günden eski yayınlar",
|
||||
"mark_all_older_week" : "Bir haftadan eski yayınlar",
|
||||
"mark_all_older_two_weeks" : "İki haftadan eski yayınlar",
|
||||
"settings" : "Ayarlar",
|
||||
"profile" : "Profil",
|
||||
"admin" : "Yönetim",
|
||||
@@ -56,14 +56,14 @@
|
||||
"donate" : "Bağış"
|
||||
},
|
||||
"view" : {
|
||||
"entry_source" : "from ",
|
||||
"entry_author" : "by ",
|
||||
"entry_source" : "kaynak: ",
|
||||
"entry_author" : "yazar: ",
|
||||
"error_while_loading_feed" : "Bu aboneliği çekerken hata oluştu.",
|
||||
"keep_unread" : "Okunmadı olarak sakla",
|
||||
"no_unread_items" : "okunmamış ileti yok.",
|
||||
"mark_up_to_here" : "Mark as read up to here ",
|
||||
"no_unread_items" : "Okunmamış ileti yok.",
|
||||
"mark_up_to_here" : "Buraya kadar olan bütün yayınları okundu olarak işaretle!",
|
||||
"search_for" : "searching for: ",
|
||||
"no_search_results" : "No match found for the requested keywords "
|
||||
"no_search_results" : "İstenen anahtar kelimeler için eşleşme bulunamadı"
|
||||
},
|
||||
"feedsearch" : {
|
||||
"hint" : "Bir abonelik yazın...",
|
||||
@@ -80,8 +80,8 @@
|
||||
"scroll_marks" : "Genişletilmiş görünümde götüntülenen iletileri okunmuş işaretle"
|
||||
},
|
||||
"appearance" : "Görünüm",
|
||||
"scroll_speed" : "Scrolling speed when navigating between entries (in milliseconds) ",
|
||||
"scroll_speed_help" : "set to 0 to disable ",
|
||||
"scroll_speed" : "İçerikler arasında gezinirken kaydırma hızı (milisaniye cinsinden)",
|
||||
"scroll_speed_help" : "ayarı kapatmak için 0 yazınız",
|
||||
"theme" : "Tema",
|
||||
"submit_your_theme" : "Tema gönder",
|
||||
"custom_css" : "Kişiselleştirilmiş CSS"
|
||||
@@ -100,10 +100,10 @@
|
||||
"feed_url" : "Yayın URL'si",
|
||||
"generate_api_key_first" : "Öncelikle profilinizden bir API anahtarı oluşturun.",
|
||||
"unsubscribe" : "Aboneliği iptal et",
|
||||
"unsubscribe_confirmation" : "Are you sure you want to unsubscribe from this feed? ",
|
||||
"delete_category_confirmation" : "Are you sure you want to delete this category? ",
|
||||
"unsubscribe_confirmation" : "Bu yayından çıkmak istediğinizden emin misiniz? ",
|
||||
"delete_category_confirmation" : "Bu kategoriyi silmek istediğinizden emin misiniz? ",
|
||||
"category_details" : "Kategori detayları",
|
||||
"tag_details" : "Tag details ",
|
||||
"tag_details" : "Etiket detayları ",
|
||||
"parent_category" : "Üst kategori"
|
||||
},
|
||||
"profile" : {
|
||||
@@ -116,10 +116,10 @@
|
||||
"api_key" : "API anahtarı",
|
||||
"api_key_not_generated" : "Henüz oluşturulmadı",
|
||||
"generate_new_api_key" : "Yeni bir API anahtarı oluştur",
|
||||
"generate_new_api_key_info" : "Şifre değiştirmek API anahtarının da değiştirilmesine neden olcak.",
|
||||
"generate_new_api_key_info" : "Şifreyi değiştirmek API anahtarının da değiştirilmesine neden olcak.",
|
||||
"opml_export" : "OPML dışa aktar",
|
||||
"delete_account" : "Hesabı sil",
|
||||
"delete_account_confirmation" : "Delete your acount? There's no turning back! "
|
||||
"delete_account_confirmation" : "Hesabı silmek istediğinize emin misiniz? Bu işlemde geri dönüş yoktur! "
|
||||
},
|
||||
"about" : {
|
||||
"rest_api" : {
|
||||
@@ -128,24 +128,24 @@
|
||||
"link_to_documentation" : "Dökümantasyon için tıklayın."
|
||||
},
|
||||
"keyboard_shortcuts" : "Klavye kısayolları",
|
||||
"version" : "CommaFeed version ",
|
||||
"version" : "CommaFeed versiyon ",
|
||||
"line1_prefix" : "CommaFeed bir açık kaynak projedir. Kaynak dosyaları ",
|
||||
"line1_suffix" : " adresinde yayınlanır.",
|
||||
"line1_suffix" : "adresinde yayınlanır.",
|
||||
"line2_prefix" : "Lütfen, bir hata ile karşılaşırsanız bunu ",
|
||||
"line2_suffix" : " projesinde hatalar sayfasından rapor edin.",
|
||||
"line2_suffix" : "projesinde hatalar sayfasından rapor edin.",
|
||||
"line3" : "Eğer bu projeyi beğendiyseniz, lütfen bağış yaparak geliştiriciye bu sayfayı ayakta tutmasında yardımcı olun.",
|
||||
"line4" : "Bitcoin'i tercih edenler için adres ",
|
||||
"goodies" : {
|
||||
"value" : "Extralar",
|
||||
"android_app" : "Android app ",
|
||||
"value" : "Ekstralar",
|
||||
"android_app" : "Android eklentisi",
|
||||
"subscribe_url" : "Abonelik URL'si",
|
||||
"chrome_extension" : "Chrome eklentisi",
|
||||
"firefox_extension" : "Firefox eklentisi",
|
||||
"opera_extension" : "Opera eklentisi",
|
||||
"subscribe_bookmarklet" : "Bookmarklet'a abonelik ekle (tıklayın)",
|
||||
"subscribe_bookmarklet_asc" : "Oldest first ",
|
||||
"subscribe_bookmarklet_desc" : "Newest first ",
|
||||
"next_unread_bookmarklet" : "Bookmarklet'daki en son okunmamış ileti (Sık kullanılan çubuğuna sürükleyin)"
|
||||
"subscribe_bookmarklet" : "Yer imilerine abonelik ekle (tıklayın)",
|
||||
"subscribe_bookmarklet_asc" : "Eskiler önce",
|
||||
"subscribe_bookmarklet_desc" : "Yeniler önce ",
|
||||
"next_unread_bookmarklet" : "Yer imilerindeki en son okunmamış ileti (Sık kullanılan çubuğuna sürükleyin)"
|
||||
},
|
||||
"translation" : {
|
||||
"value" : "Çeviri",
|
||||
@@ -158,7 +158,7 @@
|
||||
"open_next_entry" : "sonraki öğeyi görüntüle",
|
||||
"open_previous_entry" : "önceki öğeyi görüntüle",
|
||||
"spacebar" : "space/shift+space ",
|
||||
"move_page_down_up" : "moves the page down/up ",
|
||||
"move_page_down_up" : "sayfayı aşağı/yukarı hareket ettir",
|
||||
"focus_next_entry" : "sonraki öğeyi görüntülemeden işaretle",
|
||||
"focus_previous_entry" : "önceki öğeyi görüntülemeden işaretle",
|
||||
"open_next_feed" : "sonraki aboneliği veya kategoriyi görüntüle",
|
||||
@@ -170,11 +170,11 @@
|
||||
"mark_current_entry" : "görüntülenen öğeyi okundu/okunmadı işaretle",
|
||||
"mark_all_as_read" : "tümünü okundu işaretle",
|
||||
"open_in_new_tab_mark_as_read" : "öğeyi yeni bir sekmede aç ve okundu işaretle",
|
||||
"fullscreen" : "toggle full screen mode ",
|
||||
"font_size" : "increase/decrease font size of the current entry ",
|
||||
"go_to_all" : "go to the All view ",
|
||||
"go_to_starred" : "go to the Starred view ",
|
||||
"fullscreen" : "tam ekran moduna geç ",
|
||||
"font_size" : "mevcut içerik için yazı boyunutunu arttır/azalt",
|
||||
"go_to_all" : "Tüm öğeleri görüntüle",
|
||||
"go_to_starred" : "yıldızlı öğerleri görüntüle",
|
||||
"feed_search" : "abonelik ismini yazarak aboneliğe git"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB |
@@ -4,6 +4,10 @@
|
||||
<title>CommaFeed</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
||||
<link rel="apple-touch-icon" href="app-icon-57.png" />
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="app-icon-72.png" />
|
||||
@@ -12,7 +16,9 @@
|
||||
<link rel="icon" sizes="32x32" href="app-icon-32.png" />
|
||||
<link rel="icon" sizes="64x64" href="app-icon-64.png" />
|
||||
<link rel="icon" sizes="128x128" href="app-icon-128.png" />
|
||||
<link rel="icon" sizes="192x192" href="app-icon-192.png" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
||||
<meta name="theme-color" content="#F88A14" />
|
||||
<meta name="application-name" content="CommaFeed" />
|
||||
<meta name="msapplication-navbutton-color" content="#F88A14" />
|
||||
<meta name="msapplication-starturl" content="/" />
|
||||
@@ -26,7 +32,7 @@
|
||||
<link rel="stylesheet" href="lib/font-awesome/css/font-awesome.css" />
|
||||
<link rel="stylesheet" href="lib/select2/select2.css" />
|
||||
<link rel="stylesheet" href="lib/ng-grid/ng-grid.css" />
|
||||
<link rel="stylesheet" href="lib/jquery-ui/themes/smoothness/jquery-ui.css" />
|
||||
<link rel="stylesheet" href="lib/jquery-ui/themes/base/jquery-ui.css" />
|
||||
<link rel="stylesheet" href="lib/angular-loading-bar/build/loading-bar.css" />
|
||||
|
||||
<link rel="stylesheet" href="css/app.css" />
|
||||
@@ -39,9 +45,9 @@
|
||||
</div>
|
||||
|
||||
<!-- build:js js/app.js -->
|
||||
<script type="text/javascript" src="lib/lodash/dist/lodash.js"></script>
|
||||
<script type="text/javascript" src="lib/lodash/lodash.js"></script>
|
||||
<script type="text/javascript" src="lib/jquery/dist/jquery.js"></script>
|
||||
<script type="text/javascript" src="lib/jquery-ui/jquery-ui.js"></script>
|
||||
<script type="text/javascript" src="lib/jquery-ui/ui/jquery-ui.js"></script>
|
||||
<script type="text/javascript" src="lib/jquery-mousewheel/jquery.mousewheel.js"></script>
|
||||
<script type="text/javascript" src="lib/bootstrap/dist/js/bootstrap.js"></script>
|
||||
<script type="text/javascript" src="lib/angular/angular.js"></script>
|
||||
@@ -62,8 +68,8 @@
|
||||
<script type="text/javascript" src="lib/angular-ui-select2/src/select2.js"></script>
|
||||
<script type="text/javascript" src="lib/select2/select2.js"></script>
|
||||
<script type="text/javascript" src="lib/mousetrap/mousetrap.js"></script>
|
||||
<script type="text/javascript" src="lib/momentjs/min/moment-with-langs.js"></script>
|
||||
<script type="text/javascript" src="lib/device.js/lib/device.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="js/controllers.js"></script>
|
||||
<script type="text/javascript" src="js/directives.js"></script>
|
||||
|
||||
@@ -322,17 +322,21 @@ module.controller('FeedDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedS
|
||||
|
||||
$scope.save = function() {
|
||||
var sub = $scope.sub;
|
||||
$scope.error = null;
|
||||
FeedService.modify({
|
||||
id : sub.id,
|
||||
name : sub.name,
|
||||
position : sub.position,
|
||||
categoryId : sub.categoryId
|
||||
categoryId : sub.categoryId,
|
||||
filter : sub.filter
|
||||
}, function() {
|
||||
CategoryService.init();
|
||||
$state.transitionTo('feeds.view', {
|
||||
_id : 'all',
|
||||
_type : 'category'
|
||||
});
|
||||
}, function(e) {
|
||||
$scope.error = e.data;
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -489,6 +493,7 @@ module.controller('ToolbarCtrl', [
|
||||
type : $stateParams._type,
|
||||
id : $stateParams._id,
|
||||
olderThan : olderThan,
|
||||
keywords : $location.search().q,
|
||||
read : true
|
||||
});
|
||||
};
|
||||
@@ -881,6 +886,7 @@ module.controller('FeedListCtrl', [
|
||||
service.mark({
|
||||
id : $scope.selectedId,
|
||||
olderThan : olderThan || $scope.timestamp,
|
||||
keywords : $location.search().q,
|
||||
read : true
|
||||
}, function() {
|
||||
CategoryService.refresh(function() {
|
||||
@@ -1365,7 +1371,7 @@ module.controller('SettingsCtrl', ['$scope', '$location', 'SettingsService', 'An
|
||||
|
||||
$scope.langs = LangService.langs;
|
||||
|
||||
$scope.themes = ['default', 'bootstrap', 'dark', 'ebraminio', 'MRACHINI', 'svetla', 'third'];
|
||||
$scope.themes = ['default', 'bootstrap', 'dark', 'ebraminio', 'MRACHINI', 'nightsky', 'svetla', 'third'];
|
||||
|
||||
$scope.settingsService = SettingsService;
|
||||
$scope.$watch('settingsService.settings', function(value) {
|
||||
@@ -1432,6 +1438,9 @@ module.controller('ManageSettingsCtrl', ['$scope', '$location', '$state', 'Admin
|
||||
$scope.toUsers = function() {
|
||||
$state.transitionTo('admin.userlist');
|
||||
};
|
||||
$scope.toMetrics = function() {
|
||||
$state.transitionTo('admin.metrics');
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('HelpController', ['$scope', 'CategoryService', 'AnalyticsService', 'ServerService',
|
||||
@@ -1471,11 +1480,6 @@ module.controller('LoginCtrl', ['$scope', '$location', '$timeout', 'SessionServi
|
||||
$scope.recovery_enabled = data.smtpEnabled;
|
||||
});
|
||||
|
||||
// autofilled fields do not trigger model update, do it manually
|
||||
$timeout(function() {
|
||||
$('input[ng-model]').trigger('input');
|
||||
}, 100);
|
||||
|
||||
var login = function(model) {
|
||||
var success = function(data) {
|
||||
window.location.href = window.location.href.substring(0, window.location.href.lastIndexOf('#'));
|
||||
@@ -1496,6 +1500,8 @@ module.controller('LoginCtrl', ['$scope', '$location', '$timeout', 'SessionServi
|
||||
};
|
||||
|
||||
$scope.login = function() {
|
||||
// autofilled fields do not trigger model update, do it manually
|
||||
$('input[ng-model]').trigger('input');
|
||||
login($scope.model);
|
||||
};
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ module.directive('tags', function() {
|
||||
tags : []
|
||||
};
|
||||
if (newValue) {
|
||||
data.tags = newValue.split(',');
|
||||
data.tags = newValue;
|
||||
}
|
||||
EntryService.tag(data);
|
||||
}
|
||||
@@ -193,7 +193,7 @@ module.directive('category', [function() {
|
||||
});
|
||||
var label = '';
|
||||
if (count > 0) {
|
||||
label = '(' + count + ')';
|
||||
label += count;
|
||||
}
|
||||
return label;
|
||||
};
|
||||
@@ -201,7 +201,7 @@ module.directive('category', [function() {
|
||||
$scope.feedCountLabel = function(feed) {
|
||||
var label = '';
|
||||
if (feed.unread > 0) {
|
||||
label = '(' + feed.unread + ')';
|
||||
label += feed.unread;
|
||||
}
|
||||
return label;
|
||||
};
|
||||
@@ -308,7 +308,8 @@ module.directive('droppable', ['CategoryService', 'FeedService', function(Catego
|
||||
|
||||
var data = {
|
||||
id : source.id,
|
||||
name : source.name
|
||||
name : source.name,
|
||||
filter : source.filter
|
||||
};
|
||||
|
||||
if (source.children) {
|
||||
@@ -358,4 +359,4 @@ module.directive('metricGauge', function() {
|
||||
restrict : 'E',
|
||||
templateUrl : 'templates/_metrics.gauge.html'
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,10 +12,9 @@ app.config([
|
||||
'$translateProvider',
|
||||
function($routeProvider, $stateProvider, $urlRouterProvider, $httpProvider, $compileProvider, cfpLoadingBarProvider,
|
||||
$translateProvider) {
|
||||
|
||||
// $translateProvider.useLocalStorage();
|
||||
|
||||
$translateProvider.useStaticFilesLoader({
|
||||
prefix : '/i18n/',
|
||||
prefix : 'i18n/',
|
||||
suffix : '.js'
|
||||
});
|
||||
$translateProvider.preferredLanguage('en');
|
||||
@@ -24,26 +23,23 @@ app.config([
|
||||
|
||||
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|javascript):/);
|
||||
var interceptor = ['$rootScope', '$q', '$injector', function(scope, $q, $injector) {
|
||||
|
||||
var success = function(response) {
|
||||
var f = {};
|
||||
|
||||
f.response = function(response) {
|
||||
return response;
|
||||
};
|
||||
var error = function(response) {
|
||||
|
||||
f.responseError = function(response) {
|
||||
var status = response.status;
|
||||
if (status == 401) {
|
||||
$injector.get('$state').transitionTo('welcome');
|
||||
}
|
||||
return $q.reject(response);
|
||||
};
|
||||
|
||||
var promise = function(promise) {
|
||||
return promise.then(success, error);
|
||||
};
|
||||
|
||||
return promise;
|
||||
return f;
|
||||
}];
|
||||
|
||||
$httpProvider.responseInterceptors.push(interceptor);
|
||||
$httpProvider.interceptors.push(interceptor);
|
||||
|
||||
$stateProvider.state('feeds', {
|
||||
'abstract' : true,
|
||||
|
||||
@@ -56,13 +56,13 @@ module.factory('SettingsService', ['$resource', '$translate', function($resource
|
||||
res.get(function(data) {
|
||||
s.settings = data;
|
||||
var lang = s.settings.language || 'en';
|
||||
$translate.use(lang);
|
||||
if (lang === 'zh') {
|
||||
lang = 'zh-cn';
|
||||
} else if (lang === 'ms') {
|
||||
lang = 'ms-my';
|
||||
}
|
||||
moment.lang(lang, {});
|
||||
$translate.use(lang);
|
||||
moment.locale(lang, {});
|
||||
if (callback) {
|
||||
callback(data);
|
||||
}
|
||||
@@ -182,7 +182,7 @@ module.factory('CategoryService', ['$resource', '$http', function($resource, $ht
|
||||
var actions = {
|
||||
get : {
|
||||
method : 'GET',
|
||||
ignoreLoadingBar: true,
|
||||
ignoreLoadingBar : true,
|
||||
params : {
|
||||
_method : 'get'
|
||||
}
|
||||
@@ -243,11 +243,14 @@ module.factory('CategoryService', ['$resource', '$http', function($resource, $ht
|
||||
callback(data);
|
||||
});
|
||||
};
|
||||
res.refresh = function(callback) {
|
||||
res.refresh = function(success, error) {
|
||||
res.get(function(data) {
|
||||
_.merge(res.subscriptions, data);
|
||||
if (callback)
|
||||
callback(data);
|
||||
if (success)
|
||||
success(data);
|
||||
}, function(data) {
|
||||
if (error)
|
||||
error(data);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -265,7 +268,7 @@ module.factory('EntryService', ['$resource', '$http', function($resource, $http)
|
||||
},
|
||||
mark : {
|
||||
method : 'POST',
|
||||
ignoreLoadingBar: true,
|
||||
ignoreLoadingBar : true,
|
||||
params : {
|
||||
_method : 'mark'
|
||||
}
|
||||
@@ -295,6 +298,7 @@ module.factory('EntryService', ['$resource', '$http', function($resource, $http)
|
||||
$http.get('rest/entry/tags').success(function(data) {
|
||||
res.tags = [];
|
||||
res.tags.push.apply(res.tags, data);
|
||||
res.tags.sort();
|
||||
});
|
||||
};
|
||||
var oldTag = res.tag;
|
||||
|
||||
31
src/main/app/manifest.json
Normal file
31
src/main/app/manifest.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "CommaFeed",
|
||||
"icons": [
|
||||
{
|
||||
"src": "app-icon-72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"density": "1.5"
|
||||
},
|
||||
{
|
||||
"src": "app-icon-114.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"density": "2.0"
|
||||
},
|
||||
{
|
||||
"src": "app-icon-144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"density": "3.0"
|
||||
},
|
||||
{
|
||||
"src": "app-icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"density": "4.0"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "standalone"
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
@import "themes/bootstrap";
|
||||
@import "themes/ebraminio";
|
||||
@import "themes/MRACHINI";
|
||||
@import "themes/nightsky";
|
||||
@import "themes/svetla";
|
||||
@import "themes/dark";
|
||||
@import "themes/third";
|
||||
|
||||
@@ -101,6 +101,14 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.css-treeview .unread ~ .unread-counter::before {
|
||||
content: '(';
|
||||
}
|
||||
|
||||
.css-treeview .unread ~ .unread-counter::after {
|
||||
content: ')';
|
||||
}
|
||||
|
||||
.css-treeview a {
|
||||
cursor: pointer;
|
||||
color: black;
|
||||
|
||||
@@ -13,8 +13,4 @@
|
||||
content: "\e018";
|
||||
font-family: "readabilicons";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 21px;
|
||||
top: 5px;
|
||||
position: relative;
|
||||
line-height: 0px;
|
||||
}
|
||||
@@ -1,3 +1,8 @@
|
||||
a:focus {
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.container-full {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
@@ -43,6 +48,10 @@ label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pre-wrap {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.form-horizontal .control-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -116,19 +125,23 @@ blockquote p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn,.btn-large,.btn-small,.btn-mini {
|
||||
.form-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.btn, .btn-large, .btn-small, .btn-mini {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
.btn-group>.btn:first-child,.btn-group>.btn-large:first-child {
|
||||
.btn-group>.btn:first-child, .btn-group>.btn-large:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
|
||||
.btn-group>.btn:last-child,.btn-group>.btn-large:last-child,.btn-group>.dropdown-toggle
|
||||
.btn-group>.btn:last-child, .btn-group>.btn-large:last-child, .btn-group>.dropdown-toggle
|
||||
{
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
|
||||
126
src/main/app/sass/themes/_nightsky.scss
Normal file
126
src/main/app/sass/themes/_nightsky.scss
Normal file
@@ -0,0 +1,126 @@
|
||||
#theme-nightsky {
|
||||
a {
|
||||
color: #2A9FD6;
|
||||
}
|
||||
|
||||
.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
|
||||
color: #FFF;
|
||||
background-color: #2A9FD6;
|
||||
}
|
||||
|
||||
body, .toolbar {
|
||||
color: #C6C6C6;
|
||||
background-color: #2F2F2F;
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
color: #C6C6C6;
|
||||
background-color: #424242;
|
||||
border-color: #424242;
|
||||
}
|
||||
|
||||
.btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active,
|
||||
.btn-default.active, .open>.dropdown-toggle.btn-default {
|
||||
background-color: #282828;
|
||||
border-color: #232323;
|
||||
}
|
||||
|
||||
.css-treeview li .tree-item:hover {
|
||||
background-color: #282828;
|
||||
}
|
||||
|
||||
.css-treeview .unread-counter {
|
||||
color: #939393;
|
||||
}
|
||||
|
||||
.css-treeview .category-link, .css-treeview a {
|
||||
color: #939393;
|
||||
}
|
||||
|
||||
.css-treeview a .unread, .css-treeview .category-link .unread {
|
||||
color: #C6C6C6;
|
||||
}
|
||||
|
||||
.css-treeview .selected {
|
||||
color: #C00;
|
||||
}
|
||||
|
||||
.entrylist-header {
|
||||
border-bottom: 1px solid #282828;
|
||||
}
|
||||
|
||||
#feed-accordion .entry {
|
||||
border-bottom: 1px solid #282828;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-body .entry-title {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-heading .entry-name {
|
||||
color: #939393;
|
||||
}
|
||||
|
||||
#feed-accordion .unread .entry-heading .entry-name {
|
||||
font-weight: normal;
|
||||
color: #C6C6C6;
|
||||
}
|
||||
|
||||
#feed-accordion .unread .entry-heading {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-heading {
|
||||
background-color: #2F2F2F;
|
||||
}
|
||||
|
||||
#feed-accordion .unread .entry-heading:hover {
|
||||
background-color: #2F2F2F;
|
||||
}
|
||||
|
||||
#feed-accordion .current.closed .entry-heading {
|
||||
background-color: #151515;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-heading-link {
|
||||
color: #C6C6C6;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-external-link {
|
||||
color: #C6C6C6;
|
||||
}
|
||||
|
||||
#feed-accordion .icon-star-empty {
|
||||
color: #C6C6C6;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-heading .feed-name {
|
||||
color: #939393;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-body-content {
|
||||
color: #C6C6C6;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-buttons {
|
||||
background-color: #494949;
|
||||
border-top: 1px solid #282828;
|
||||
}
|
||||
|
||||
#feed-accordion .share-buttons a {
|
||||
color: #C6C6C6;
|
||||
}
|
||||
|
||||
#feed-accordion a.mark-up-to {
|
||||
color: #C6C6C6;
|
||||
}
|
||||
|
||||
#loading-bar .bar {
|
||||
background: #C6C6C6;
|
||||
}
|
||||
|
||||
#loading-bar .peg {
|
||||
box-shadow: 0 0 10px #C6C6C6, 0 0 5px #C6C6C6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
<i ng-class="{'icon-star' : node.id == 'starred', 'icon-inbox': node.id == 'all', 'icon-tag' : node.isTag}" ng-show="!showChildren"></i>
|
||||
</span>
|
||||
<span ng-class="{selected: (node.id == selectedId && (node.isTag ? selectedType == 'tag' : selectedType == 'category'))}">
|
||||
<span ng-class="{unread: unreadCount({category:node})}" class="bidi-embed"> {{categoryLabel(node)}} </span>
|
||||
<span class="unread-counter"> {{categoryCountLabel(node)}} </span>
|
||||
<span ng-class="{unread: unreadCount({category:node})}" class="bidi-embed">{{categoryLabel(node)}}</span>
|
||||
<span class="unread-counter">{{categoryCountLabel(node)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -30,8 +30,8 @@
|
||||
<a ng-click="feedClicked(feed.id, $event)" ng-dblclick="showFeedDetails(feed)" class="feed-link" href="{{feed.feedLink}}" target="_blank"
|
||||
ng-class="{error: feed.message && feed.errorCount > 10, selected: (feed.id == selectedId && selectedType == 'feed') }">
|
||||
<favicon url="feed.iconUrl" />
|
||||
<span ng-class="{unread: feed.unread}" class="bidi-embed"> {{feed.name}} </span>
|
||||
<span class="unread-counter"> {{feedCountLabel(feed)}} </span>
|
||||
<span ng-class="{unread: feed.unread}" class="bidi-embed">{{feed.name}}</span>
|
||||
<span class="unread-counter">{{feedCountLabel(feed)}}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<button type="button" class="close" ng-click="close()">×</button>
|
||||
<h4>
|
||||
<input ng-model="filter" class="filter-input"
|
||||
ui-keydown="{'up': 'focusPrevious($event)', 'down': 'focusNext($event)', 'enter': 'openFocused()' }" placeholder="'feedsearch.hint' | translate"
|
||||
ui-keydown="{'up': 'focusPrevious($event)', 'down': 'focusNext($event)', 'enter': 'openFocused()' }" placeholder="{{'feedsearch.hint' | translate}}"
|
||||
focus="feedSearchModal">
|
||||
</h4>
|
||||
<small>{{ 'feedsearch.help' | translate }}</small>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<span>
|
||||
<a ng-click="edit_mode=!edit_mode" class="nolink pointer">
|
||||
<span ng-click="edit_mode=!edit_mode" class="nolink pointer">
|
||||
<i class="icon-tags"></i>
|
||||
{{ 'global.tags' | translate }}
|
||||
</a>
|
||||
</span>
|
||||
<span ng-if="!edit_mode">
|
||||
<span class="label label-info" ng-repeat="tag in entry.tags">{{tag}}</span>
|
||||
</span>
|
||||
<span ng-if="edit_mode">
|
||||
<input type="text" ui-select2="select2Options" ng-model="entry.tags" class="tag-input" autofocus />
|
||||
<input type="hidden" ui-select2="select2Options" ng-model="entry.tags" class="tag-input" autofocus />
|
||||
</span>
|
||||
</span>
|
||||
@@ -124,9 +124,9 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="btn-group donate">
|
||||
<a class="btn btn-success" type="button" ng-click="toHelp()" title="{{ 'toolbar.about' | translate }} / {{ 'toolbar.donate' | translate }}">
|
||||
<button class="btn btn-success" type="button" ng-click="toHelp()" title="{{ 'toolbar.about' | translate }} / {{ 'toolbar.donate' | translate }}">
|
||||
<i class="icon-info-sign"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<div class="row">
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
Application settings
|
||||
Application settings -
|
||||
<small>
|
||||
<a ng-click="toUsers()" class="pointer">Manage users</a>
|
||||
</small>
|
||||
-
|
||||
<small>
|
||||
<a ng-click="toMetrics()" class="pointer">Metrics</a>
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -28,26 +32,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="feedbackButton">Feedback button</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="feedbackButton" name="feedbackButton" ng-model="settings.feedbackButton" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="googleClientId">Google client ID</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="googleClientId" class="form-control" ng-model="settings.googleClientId" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="googleClientSecret">Google client secret</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="googleClientSecret" class="form-control" ng-model="settings.googleClientSecret" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="googleAnalyticsTrackingCode">Google Analytics tracking code</label>
|
||||
<div class="col-sm-9">
|
||||
@@ -134,14 +118,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="logLevel">Logging level</label>
|
||||
<div class="col-sm-9">
|
||||
<select name="logLevel" ng-model="settings.logLevel" class="form-control"
|
||||
ng-options="level for level in ['DEBUG', 'INFO', 'WARN', 'ERROR']">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="logLevel">Database query timeout (ms)</label>
|
||||
<div class="col-sm-9">
|
||||
@@ -173,8 +149,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
|
||||
<button type="button" class="btn btn-default" ng-click="cancel()">Back</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{ 'details.feed_url' | translate }}</label>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<div class="col-sm-10 form-control-static">
|
||||
<a ng-show="user.apiKey" href="{{'rest/category/entriesAsFeed?id=' + category.id + '&apiKey=' + user.apiKey}}" target="_blank">{{ 'global.link' | translate }}</a>
|
||||
<span ng-show="!user.apiKey">{{ 'details.generate_api_key_first' | translate }}</span>
|
||||
</div>
|
||||
@@ -40,7 +40,7 @@
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-primary" ng-if="!isMeta()">{{ 'global.save' | translate }}</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="deleteCategory()" ng-show="!isMeta()"
|
||||
confirm-click="{{ 'details.delete_category_confirmation}">{{ 'global.delete' | translate }}</button>
|
||||
confirm-click="'details.delete_category_confirmation' | translate">{{ 'global.delete' | translate }}</button>
|
||||
<button type="button" class="btn btn-default" ng-click="back()">{{ 'global.cancel' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
<h3>{{ 'details.feed_details' | translate }}</h3>
|
||||
</div>
|
||||
<form name="form" class="form-horizontal" ng-submit="save()">
|
||||
<div class="alert alert-danger" ng-if="error">{{ error }}</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{ 'details.url' | translate }}</label>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<div class="col-sm-10 form-control-static">
|
||||
<a href="{{sub.feedUrl}}" target="_blank">{{sub.feedUrl}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{ 'details.website' | translate }}</label>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<div class="col-sm-10 form-control-static">
|
||||
<a href="{{sub.feedLink}}" target="_blank">{{sub.feedLink}}</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,26 +50,34 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{ 'details.next_refresh' | translate }}</label>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<div class="col-sm-10 form-control-static">
|
||||
<span>{{sub.nextRefresh|entryDate:('details.queued_for_refresh' | translate) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{ 'details.message' | translate }}</label>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<div class="col-sm-10 form-control-static">
|
||||
<span>{{sub.message}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{ 'details.feed_url' | translate }}</label>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<div class="col-sm-10 form-control-static">
|
||||
<a ng-show="user.apiKey" href="{{'rest/feed/entriesAsFeed?id=' + sub.id + '&apiKey=' + user.apiKey}}" target="_blank">{{ 'global.link' | translate }}</a>
|
||||
<span ng-show="!user.apiKey">{{ 'details.generate_api_key_first' | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{ 'details.filtering_expression' | translate }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" name="filter" ng-model="sub.filter" class="form-control"></input>
|
||||
<p class="help-block pre-wrap" ng-bind-html="'details.filtering_expression_help' | translate"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-primary">{{ 'global.save' | translate }}</button>
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
</h4>
|
||||
<p>{{ 'about.rest_api.line1' | translate }}</p>
|
||||
<p>
|
||||
<a href="api" target="_blank">{{ 'about.rest_api.link_to_documentation' | translate }}</a>
|
||||
<a href="api/" target="_blank">{{ 'about.rest_api.link_to_documentation' | translate }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<div infinite-scroll="loadMoreEntries()" infinite-scroll-disabled="busy || !settingsService.settings.readingMode"
|
||||
infinite-scroll-distance="1" id="feed-accordion" ng-class="{'expanded' : settingsService.settings.viewMode == 'expanded' }">
|
||||
<div ng-show="message && errorCount > 10">{{ 'view.error_while_loading_feed} : {{message}' | translate }}</div>
|
||||
<div ng-show="message && errorCount > 10">{{ 'view.error_while_loading_feed' | translate }} : {{ message }}</div>
|
||||
<div ng-repeat="entry in entries" class="entry entry-font-size-{{font_size}}" id="entry_{{entry.id}}"
|
||||
ng-class="{unread: entry.read == false, current: current==entry, open: isOpen, closed: !isOpen }">
|
||||
<div class="entry-heading" ng-swipe-right="mark(entry, !entry.read)">
|
||||
@@ -51,6 +51,9 @@
|
||||
<span class="entry-author-prefix">{{ 'view.entry_author' | translate }}</span>
|
||||
<span class="entry-author-name">{{entry.author}}</span>
|
||||
</span>
|
||||
<span class="entry-categories" ng-if="entry.categories">
|
||||
<span class="entry-categories-name">({{entry.categories}})</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="email">{{ 'profile.email' | translate }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" id="email" ng-model="user.email" class="form-control" />
|
||||
<input type="email" id="email" ng-model="user.email" class="form-control" autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="password">{{ 'profile.change_password' | translate }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" name="password" id="password" ng-model="user.password" class="form-control" ng-minlength="6" />
|
||||
<input type="password" name="password" id="password" ng-model="user.password" class="form-control" ng-minlength="6" autocomplete="off" />
|
||||
<span class="help-inline" ng-show="profileForm.password.$error.minlength">{{ 'profile.minimum_6_chars' | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,7 +26,7 @@
|
||||
<label class="col-sm-2 control-label" for="password">{{ 'profile.confirm_password' | translate }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password_c" id="password_c" ng-model="password_c"
|
||||
ui-validate="'$value==user.password'" ui-validate-watch="'user.password'">
|
||||
ui-validate="'$value==user.password'" ui-validate-watch="'user.password'" autocomplete="off">
|
||||
<span class="help-inline" ng-show="profileForm.password_c.$error.validator">{{ 'profile.passwords_do_not_match' | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,7 +57,8 @@
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-primary">{{ 'global.save' | translate }}</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="deleteAccount()" confirm-click="'profile.delete_account_confirmation' | translate">{{ 'profile.delete_account' | translate }}</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="deleteAccount()"
|
||||
confirm-click="'profile.delete_account_confirmation' | translate">{{ 'profile.delete_account' | translate }}</button>
|
||||
<button type="button" class="btn btn-default" ng-click="cancel()">{{ 'global.cancel' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
</span>
|
||||
<span>
|
||||
- REST API
|
||||
<a href="api" target="_blank">documentation</a>
|
||||
<a href="api/" target="_blank">documentation</a>
|
||||
</span>
|
||||
<span class="pull-right">
|
||||
<a href="https://twitter.com/CommaFeed" class="twitter-follow-button" data-show-count="false" data-size="large">Follow @CommaFeed</a>
|
||||
|
||||
@@ -3,42 +3,33 @@ package com.commafeed;
|
||||
import io.dropwizard.Application;
|
||||
import io.dropwizard.assets.AssetsBundle;
|
||||
import io.dropwizard.db.DataSourceFactory;
|
||||
import io.dropwizard.forms.MultiPartBundle;
|
||||
import io.dropwizard.hibernate.HibernateBundle;
|
||||
import io.dropwizard.jersey.sessions.HttpSessionProvider;
|
||||
import io.dropwizard.migrations.MigrationsBundle;
|
||||
import io.dropwizard.server.DefaultServerFactory;
|
||||
import io.dropwizard.servlets.CacheBustingFilter;
|
||||
import io.dropwizard.setup.Bootstrap;
|
||||
import io.dropwizard.setup.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.jetty.server.session.HashSessionManager;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration.CacheType;
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import com.commafeed.backend.cache.NoopCacheService;
|
||||
import com.commafeed.backend.cache.RedisCacheService;
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
import com.commafeed.backend.dao.FeedDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryContentDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryTagDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
import com.commafeed.backend.dao.UserDAO;
|
||||
import com.commafeed.backend.dao.UserRoleDAO;
|
||||
import com.commafeed.backend.dao.UserSettingsDAO;
|
||||
import com.commafeed.backend.feed.FaviconFetcher;
|
||||
import com.commafeed.backend.feed.FeedFetcher;
|
||||
import com.commafeed.backend.feed.FeedParser;
|
||||
import com.commafeed.backend.feed.FeedQueues;
|
||||
import com.commafeed.backend.feed.FeedRefreshTaskGiver;
|
||||
import com.commafeed.backend.feed.FeedRefreshUpdater;
|
||||
import com.commafeed.backend.feed.FeedRefreshWorker;
|
||||
@@ -53,26 +44,10 @@ import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserRole;
|
||||
import com.commafeed.backend.model.UserSettings;
|
||||
import com.commafeed.backend.opml.OPMLExporter;
|
||||
import com.commafeed.backend.opml.OPMLImporter;
|
||||
import com.commafeed.backend.service.ApplicationPropertiesService;
|
||||
import com.commafeed.backend.service.DatabaseCleaningService;
|
||||
import com.commafeed.backend.service.FeedEntryContentService;
|
||||
import com.commafeed.backend.service.FeedEntryService;
|
||||
import com.commafeed.backend.service.FeedEntryTagService;
|
||||
import com.commafeed.backend.service.FeedService;
|
||||
import com.commafeed.backend.service.FeedSubscriptionService;
|
||||
import com.commafeed.backend.service.FeedUpdateService;
|
||||
import com.commafeed.backend.service.MailService;
|
||||
import com.commafeed.backend.service.PasswordEncryptionService;
|
||||
import com.commafeed.backend.service.PubSubService;
|
||||
import com.commafeed.backend.service.StartupService;
|
||||
import com.commafeed.backend.service.UserService;
|
||||
import com.commafeed.backend.task.OldStatusesCleanupTask;
|
||||
import com.commafeed.backend.task.OrphansCleanupTask;
|
||||
import com.commafeed.backend.task.SchedulingService;
|
||||
import com.commafeed.frontend.auth.SecurityCheckProvider;
|
||||
import com.commafeed.frontend.auth.SecurityCheckProvider.SecurityCheckUserServiceProvider;
|
||||
import com.commafeed.backend.task.ScheduledTask;
|
||||
import com.commafeed.frontend.auth.SecurityCheckFactoryProvider;
|
||||
import com.commafeed.frontend.resource.AdminREST;
|
||||
import com.commafeed.frontend.resource.CategoryREST;
|
||||
import com.commafeed.frontend.resource.EntryREST;
|
||||
@@ -84,174 +59,134 @@ import com.commafeed.frontend.servlet.AnalyticsServlet;
|
||||
import com.commafeed.frontend.servlet.CustomCssServlet;
|
||||
import com.commafeed.frontend.servlet.LogoutServlet;
|
||||
import com.commafeed.frontend.servlet.NextUnreadServlet;
|
||||
import com.wordnik.swagger.config.ConfigFactory;
|
||||
import com.wordnik.swagger.config.ScannerFactory;
|
||||
import com.wordnik.swagger.config.SwaggerConfig;
|
||||
import com.wordnik.swagger.jaxrs.config.DefaultJaxrsScanner;
|
||||
import com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider;
|
||||
import com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON;
|
||||
import com.wordnik.swagger.jaxrs.listing.ResourceListingProvider;
|
||||
import com.wordnik.swagger.jaxrs.reader.DefaultJaxrsApiReader;
|
||||
import com.wordnik.swagger.reader.ClassReaders;
|
||||
import com.commafeed.frontend.session.SessionHelperFactoryProvider;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.wordnik.swagger.jaxrs.config.BeanConfig;
|
||||
import com.wordnik.swagger.jaxrs.listing.ApiListingResource;
|
||||
|
||||
@Slf4j
|
||||
public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||
|
||||
public static final String USERNAME_ADMIN = "admin";
|
||||
public static final String USERNAME_DEMO = "demo";
|
||||
|
||||
public static final String SESSION_USER = "user";
|
||||
|
||||
public static final Date STARTUP_TIME = new Date();
|
||||
|
||||
private HibernateBundle<CommaFeedConfiguration> hibernateBundle;
|
||||
private MigrationsBundle<CommaFeedConfiguration> migrationsBundle;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "CommaFeed";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Bootstrap<CommaFeedConfiguration> bootstrap) {
|
||||
hibernateBundle = new HibernateBundle<CommaFeedConfiguration>(AbstractModel.class, Feed.class, FeedCategory.class, FeedEntry.class,
|
||||
FeedEntryContent.class, FeedEntryStatus.class, FeedEntryTag.class, FeedSubscription.class, User.class, UserRole.class,
|
||||
UserSettings.class) {
|
||||
bootstrap.addBundle(hibernateBundle = new HibernateBundle<CommaFeedConfiguration>(AbstractModel.class, Feed.class,
|
||||
FeedCategory.class, FeedEntry.class, FeedEntryContent.class, FeedEntryStatus.class, FeedEntryTag.class,
|
||||
FeedSubscription.class, User.class, UserRole.class, UserSettings.class) {
|
||||
@Override
|
||||
public DataSourceFactory getDataSourceFactory(CommaFeedConfiguration configuration) {
|
||||
return configuration.getDatabase();
|
||||
}
|
||||
};
|
||||
bootstrap.addBundle(hibernateBundle);
|
||||
DataSourceFactory factory = configuration.getDataSourceFactory();
|
||||
|
||||
migrationsBundle = new MigrationsBundle<CommaFeedConfiguration>() {
|
||||
// keep using old id generator for backward compatibility
|
||||
factory.getProperties().put(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "false");
|
||||
|
||||
factory.getProperties().put(AvailableSettings.STATEMENT_BATCH_SIZE, "50");
|
||||
factory.getProperties().put(AvailableSettings.BATCH_VERSIONED_DATA, "true");
|
||||
return factory;
|
||||
}
|
||||
});
|
||||
|
||||
bootstrap.addBundle(new MigrationsBundle<CommaFeedConfiguration>() {
|
||||
@Override
|
||||
public DataSourceFactory getDataSourceFactory(CommaFeedConfiguration configuration) {
|
||||
return configuration.getDatabase();
|
||||
return configuration.getDataSourceFactory();
|
||||
}
|
||||
};
|
||||
bootstrap.addBundle(migrationsBundle);
|
||||
});
|
||||
|
||||
bootstrap.addBundle(new AssetsBundle("/assets/", "/", "index.html"));
|
||||
bootstrap.addBundle(new MultiPartBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(CommaFeedConfiguration config, Environment environment) throws Exception {
|
||||
MetricRegistry metrics = environment.metrics();
|
||||
SessionFactory sessionFactory = hibernateBundle.getSessionFactory();
|
||||
// guice init
|
||||
Injector injector = Guice.createInjector(new CommaFeedModule(hibernateBundle.getSessionFactory(), config, environment.metrics()));
|
||||
|
||||
CacheService cacheService = config.getApplicationSettings().getCache() == CacheType.NOOP ? new NoopCacheService()
|
||||
: new RedisCacheService();
|
||||
log.info("using cache {}", cacheService.getClass());
|
||||
// session management
|
||||
environment.servlets().setSessionHandler(new SessionHandler(config.getSessionManagerFactory().build()));
|
||||
|
||||
// DAOs
|
||||
FeedCategoryDAO feedCategoryDAO = new FeedCategoryDAO(sessionFactory);
|
||||
FeedDAO feedDAO = new FeedDAO(sessionFactory);
|
||||
FeedEntryContentDAO feedEntryContentDAO = new FeedEntryContentDAO(sessionFactory);
|
||||
FeedEntryDAO feedEntryDAO = new FeedEntryDAO(sessionFactory);
|
||||
FeedEntryTagDAO feedEntryTagDAO = new FeedEntryTagDAO(sessionFactory);
|
||||
FeedSubscriptionDAO feedSubscriptionDAO = new FeedSubscriptionDAO(sessionFactory);
|
||||
UserDAO userDAO = new UserDAO(sessionFactory);
|
||||
UserRoleDAO userRoleDAO = new UserRoleDAO(sessionFactory);
|
||||
UserSettingsDAO userSettingsDAO = new UserSettingsDAO(sessionFactory);
|
||||
FeedEntryStatusDAO feedEntryStatusDAO = new FeedEntryStatusDAO(sessionFactory, feedEntryDAO, feedEntryTagDAO, config);
|
||||
|
||||
// Queuing system
|
||||
FeedQueues queues = new FeedQueues(feedDAO, config, metrics);
|
||||
|
||||
// Services
|
||||
ApplicationPropertiesService applicationPropertiesService = new ApplicationPropertiesService();
|
||||
DatabaseCleaningService cleaningService = new DatabaseCleaningService(sessionFactory, feedDAO, feedEntryDAO, feedEntryContentDAO,
|
||||
feedEntryStatusDAO);
|
||||
FeedEntryContentService feedEntryContentService = new FeedEntryContentService(feedEntryContentDAO);
|
||||
FeedEntryService feedEntryService = new FeedEntryService(feedSubscriptionDAO, feedEntryDAO, feedEntryStatusDAO, cacheService);
|
||||
FeedEntryTagService feedEntryTagService = new FeedEntryTagService(feedEntryDAO, feedEntryTagDAO);
|
||||
FeedService feedService = new FeedService(feedDAO);
|
||||
FeedSubscriptionService feedSubscriptionService = new FeedSubscriptionService(feedEntryStatusDAO, feedSubscriptionDAO, feedService,
|
||||
queues, cacheService, config);
|
||||
FeedUpdateService feedUpdateService = new FeedUpdateService(feedEntryDAO, feedEntryContentService);
|
||||
MailService mailService = new MailService(config);
|
||||
PasswordEncryptionService encryptionService = new PasswordEncryptionService();
|
||||
PubSubService pubSubService = new PubSubService(config, queues);
|
||||
UserService userService = new UserService(feedCategoryDAO, userDAO, userSettingsDAO, feedSubscriptionService, encryptionService,
|
||||
config);
|
||||
StartupService startupService = new StartupService(sessionFactory, userDAO, userService);
|
||||
OPMLImporter opmlImporter = new OPMLImporter(feedCategoryDAO, feedSubscriptionService, cacheService);
|
||||
OPMLExporter opmlExporter = new OPMLExporter(feedCategoryDAO, feedSubscriptionDAO);
|
||||
|
||||
// Feed fetching/parsing
|
||||
HttpGetter httpGetter = new HttpGetter();
|
||||
FeedParser feedParser = new FeedParser();
|
||||
FaviconFetcher faviconFetcher = new FaviconFetcher(httpGetter);
|
||||
FeedFetcher feedFetcher = new FeedFetcher(feedParser, httpGetter);
|
||||
FeedRefreshUpdater feedUpdater = new FeedRefreshUpdater(sessionFactory, feedUpdateService, pubSubService, queues, config, metrics,
|
||||
feedSubscriptionDAO, cacheService);
|
||||
FeedRefreshWorker feedWorker = new FeedRefreshWorker(feedUpdater, feedFetcher, queues, config, metrics);
|
||||
FeedRefreshTaskGiver taskGiver = new FeedRefreshTaskGiver(sessionFactory, queues, feedDAO, feedWorker, config, metrics);
|
||||
|
||||
// Auth/session management
|
||||
HashSessionManager sessionManager = new HashSessionManager();
|
||||
sessionManager.setHttpOnly(true);
|
||||
sessionManager.getSessionCookieConfig().setHttpOnly(true);
|
||||
|
||||
sessionManager.setStoreDirectory(new File("sessions"));
|
||||
sessionManager.getSessionCookieConfig().setMaxAge((int) TimeUnit.DAYS.toSeconds(30));
|
||||
sessionManager.setMaxInactiveInterval((int) TimeUnit.DAYS.toSeconds(30));
|
||||
|
||||
sessionManager.setDeleteUnrestorableSessions(true);
|
||||
sessionManager.setIdleSavePeriod((int) TimeUnit.HOURS.toSeconds(2));
|
||||
sessionManager.setRefreshCookieAge((int) TimeUnit.DAYS.toSeconds(1));
|
||||
sessionManager.setSavePeriod((int) TimeUnit.MINUTES.toSeconds(5));
|
||||
sessionManager.setScavengePeriod((int) TimeUnit.MINUTES.toSeconds(5));
|
||||
|
||||
environment.servlets().setSessionHandler(new SessionHandler(sessionManager));
|
||||
environment.jersey().register(new SecurityCheckUserServiceProvider(userService));
|
||||
environment.jersey().register(SecurityCheckProvider.class);
|
||||
environment.jersey().register(HttpSessionProvider.class);
|
||||
// support for "@SecurityCheck User user" injection
|
||||
environment.jersey().register(new SecurityCheckFactoryProvider.Binder(injector.getInstance(UserService.class)));
|
||||
// support for "@Context SessionHelper sessionHelper" injection
|
||||
environment.jersey().register(new SessionHelperFactoryProvider.Binder());
|
||||
|
||||
// REST resources
|
||||
environment.jersey().setUrlPattern("/rest/*");
|
||||
environment.jersey()
|
||||
.register(new AdminREST(userDAO, userRoleDAO, userService, encryptionService, cleaningService, config, metrics));
|
||||
environment.jersey().register(
|
||||
new CategoryREST(feedCategoryDAO, feedEntryStatusDAO, feedSubscriptionDAO, feedEntryService, feedSubscriptionService,
|
||||
cacheService, config));
|
||||
environment.jersey().register(new EntryREST(feedEntryTagDAO, feedEntryService, feedEntryTagService));
|
||||
environment.jersey().register(
|
||||
new FeedREST(feedSubscriptionDAO, feedCategoryDAO, feedEntryStatusDAO, faviconFetcher, feedFetcher, feedEntryService,
|
||||
feedSubscriptionService, queues, opmlImporter, opmlExporter, cacheService, config));
|
||||
environment.jersey().register(new PubSubHubbubCallbackREST(feedDAO, feedParser, queues, config, metrics));
|
||||
environment.jersey().register(new ServerREST(httpGetter, config, applicationPropertiesService));
|
||||
environment.jersey().register(
|
||||
new UserREST(userDAO, userRoleDAO, userSettingsDAO, userService, encryptionService, mailService, config));
|
||||
((DefaultServerFactory) config.getServerFactory()).setJerseyRootPath("/rest/*");
|
||||
environment.jersey().register(injector.getInstance(AdminREST.class));
|
||||
environment.jersey().register(injector.getInstance(CategoryREST.class));
|
||||
environment.jersey().register(injector.getInstance(EntryREST.class));
|
||||
environment.jersey().register(injector.getInstance(FeedREST.class));
|
||||
environment.jersey().register(injector.getInstance(PubSubHubbubCallbackREST.class));
|
||||
environment.jersey().register(injector.getInstance(ServerREST.class));
|
||||
environment.jersey().register(injector.getInstance(UserREST.class));
|
||||
|
||||
// Servlets
|
||||
NextUnreadServlet nextUnreadServlet = new NextUnreadServlet(sessionFactory, feedSubscriptionDAO, feedEntryStatusDAO,
|
||||
feedCategoryDAO, config);
|
||||
LogoutServlet logoutServlet = new LogoutServlet(config);
|
||||
CustomCssServlet customCssServlet = new CustomCssServlet(sessionFactory, userSettingsDAO);
|
||||
AnalyticsServlet analyticsServlet = new AnalyticsServlet(config);
|
||||
environment.servlets().addServlet("next", nextUnreadServlet).addMapping("/next");
|
||||
environment.servlets().addServlet("logout", logoutServlet).addMapping("/logout");
|
||||
environment.servlets().addServlet("customCss", customCssServlet).addMapping("/custom_css.css");
|
||||
environment.servlets().addServlet("analytics.js", analyticsServlet).addMapping("/analytics.js");
|
||||
environment.servlets().addServlet("next", injector.getInstance(NextUnreadServlet.class)).addMapping("/next");
|
||||
environment.servlets().addServlet("logout", injector.getInstance(LogoutServlet.class)).addMapping("/logout");
|
||||
environment.servlets().addServlet("customCss", injector.getInstance(CustomCssServlet.class)).addMapping("/custom_css.css");
|
||||
environment.servlets().addServlet("analytics.js", injector.getInstance(AnalyticsServlet.class)).addMapping("/analytics.js");
|
||||
|
||||
// Tasks
|
||||
SchedulingService schedulingService = new SchedulingService();
|
||||
schedulingService.register(new OldStatusesCleanupTask(config, cleaningService));
|
||||
schedulingService.register(new OrphansCleanupTask(cleaningService));
|
||||
// Scheduled tasks
|
||||
Set<ScheduledTask> tasks = injector.getInstance(Key.get(new TypeLiteral<Set<ScheduledTask>>() {
|
||||
}));
|
||||
ScheduledExecutorService executor = environment.lifecycle().scheduledExecutorService("task-scheduler", true).threads(tasks.size())
|
||||
.build();
|
||||
for (ScheduledTask task : tasks) {
|
||||
task.register(executor);
|
||||
}
|
||||
|
||||
// Managed objects
|
||||
environment.lifecycle().manage(startupService);
|
||||
environment.lifecycle().manage(taskGiver);
|
||||
environment.lifecycle().manage(feedWorker);
|
||||
environment.lifecycle().manage(feedUpdater);
|
||||
environment.lifecycle().manage(schedulingService);
|
||||
// database init/changelogs
|
||||
environment.lifecycle().manage(injector.getInstance(StartupService.class));
|
||||
|
||||
// background feed fetching
|
||||
environment.lifecycle().manage(injector.getInstance(FeedRefreshTaskGiver.class));
|
||||
environment.lifecycle().manage(injector.getInstance(FeedRefreshWorker.class));
|
||||
environment.lifecycle().manage(injector.getInstance(FeedRefreshUpdater.class));
|
||||
|
||||
// Swagger
|
||||
environment.jersey().register(new ApiListingResourceJSON());
|
||||
environment.jersey().register(new ApiDeclarationProvider());
|
||||
environment.jersey().register(new ResourceListingProvider());
|
||||
ScannerFactory.setScanner(new DefaultJaxrsScanner());
|
||||
ClassReaders.setReader(new DefaultJaxrsApiReader());
|
||||
SwaggerConfig swaggerConfig = ConfigFactory.config();
|
||||
swaggerConfig.setApiVersion("1");
|
||||
environment.jersey().register(new ApiListingResource());
|
||||
environment.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
String modelsPackage = "com.commafeed.frontend.model";
|
||||
String requestsPackage = "com.commafeed.frontend.model.request";
|
||||
String endpointsPackage = "com.commafeed.frontend.resource";
|
||||
List<String> packages = Arrays.asList(modelsPackage, requestsPackage, endpointsPackage);
|
||||
BeanConfig swaggerConfig = new BeanConfig();
|
||||
swaggerConfig.setTitle("CommaFeed");
|
||||
swaggerConfig.setVersion("1");
|
||||
swaggerConfig.setBasePath("/rest");
|
||||
swaggerConfig.setResourcePackage(StringUtils.join(packages, ","));
|
||||
swaggerConfig.setScan(true);
|
||||
|
||||
// cache configuration
|
||||
// prevent caching on REST resources, except for favicons
|
||||
environment.servlets().addFilter("cache-filter", new CacheBustingFilter() {
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
String path = ((HttpServletRequest) request).getRequestURI();
|
||||
if (path.contains("/feed/favicon")) {
|
||||
chain.doFilter(request, response);
|
||||
} else {
|
||||
super.doFilter(request, response, chain);
|
||||
}
|
||||
}
|
||||
}).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/rest/*");
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
@@ -4,14 +4,19 @@ import io.dropwizard.Configuration;
|
||||
import io.dropwizard.db.DataSourceFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
import com.commafeed.backend.cache.RedisPoolFactory;
|
||||
import com.commafeed.frontend.session.SessionManagerFactory;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Getter
|
||||
@@ -21,70 +26,114 @@ public class CommaFeedConfiguration extends Configuration {
|
||||
NOOP, REDIS
|
||||
}
|
||||
|
||||
private ResourceBundle bundle;
|
||||
|
||||
public CommaFeedConfiguration() {
|
||||
bundle = ResourceBundle.getBundle("application");
|
||||
}
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty("database")
|
||||
private DataSourceFactory database = new DataSourceFactory();
|
||||
private DataSourceFactory dataSourceFactory = new DataSourceFactory();
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty("redis")
|
||||
private RedisPoolFactory redisPoolFactory = new RedisPoolFactory();
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty("session")
|
||||
private SessionManagerFactory sessionManagerFactory = new SessionManagerFactory();
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty("app")
|
||||
private ApplicationSettings applicationSettings;
|
||||
|
||||
public String getVersion() {
|
||||
return bundle.getString("version");
|
||||
}
|
||||
|
||||
public String getGitCommit() {
|
||||
return bundle.getString("git.commit");
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class ApplicationSettings {
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@NotBlank
|
||||
@Valid
|
||||
private String publicUrl;
|
||||
|
||||
@JsonProperty
|
||||
private boolean allowRegistrations;
|
||||
@NotNull
|
||||
@Valid
|
||||
private Boolean allowRegistrations;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
private Boolean createDemoAccount;
|
||||
|
||||
@JsonProperty
|
||||
private String googleAnalyticsTrackingCode;
|
||||
|
||||
@JsonProperty
|
||||
private int backgroundThreads;
|
||||
private String googleAuthKey;
|
||||
|
||||
@JsonProperty
|
||||
private int databaseUpdateThreads;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
@Valid
|
||||
private Integer backgroundThreads;
|
||||
|
||||
@NotNull
|
||||
@Min(1)
|
||||
@Valid
|
||||
private Integer databaseUpdateThreads;
|
||||
|
||||
@JsonProperty
|
||||
private String smtpHost;
|
||||
|
||||
@JsonProperty
|
||||
private int smtpPort;
|
||||
|
||||
@JsonProperty
|
||||
private boolean smtpTls;
|
||||
|
||||
@JsonProperty
|
||||
private String smtpUserName;
|
||||
|
||||
@JsonProperty
|
||||
private String smtpPassword;
|
||||
private String smtpFromAddress;
|
||||
|
||||
@JsonProperty
|
||||
private boolean heavyLoad;
|
||||
@NotNull
|
||||
@Valid
|
||||
private Boolean heavyLoad;
|
||||
|
||||
@JsonProperty
|
||||
private boolean pubsubhubbub;
|
||||
@NotNull
|
||||
@Valid
|
||||
private Boolean pubsubhubbub;
|
||||
|
||||
@JsonProperty
|
||||
private boolean imageProxyEnabled;
|
||||
@NotNull
|
||||
@Valid
|
||||
private Boolean imageProxyEnabled;
|
||||
|
||||
@JsonProperty
|
||||
private int queryTimeout;
|
||||
@NotNull
|
||||
@Min(0)
|
||||
@Valid
|
||||
private Integer queryTimeout;
|
||||
|
||||
@JsonProperty
|
||||
private int keepStatusDays;
|
||||
@NotNull
|
||||
@Min(0)
|
||||
@Valid
|
||||
private Integer keepStatusDays;
|
||||
|
||||
@JsonProperty
|
||||
private int refreshIntervalMinutes;
|
||||
@NotNull
|
||||
@Min(0)
|
||||
@Valid
|
||||
private Integer maxFeedCapacity;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@Min(0)
|
||||
@Valid
|
||||
private Integer refreshIntervalMinutes;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
private CacheType cache;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@Valid
|
||||
private String announcement;
|
||||
|
||||
public Date getUnreadThreshold() {
|
||||
|
||||
58
src/main/java/com/commafeed/CommaFeedModule.java
Normal file
58
src/main/java/com/commafeed/CommaFeedModule.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.commafeed;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration.CacheType;
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import com.commafeed.backend.cache.NoopCacheService;
|
||||
import com.commafeed.backend.cache.RedisCacheService;
|
||||
import com.commafeed.backend.favicon.AbstractFaviconFetcher;
|
||||
import com.commafeed.backend.favicon.DefaultFaviconFetcher;
|
||||
import com.commafeed.backend.favicon.FacebookFaviconFetcher;
|
||||
import com.commafeed.backend.favicon.YoutubeFaviconFetcher;
|
||||
import com.commafeed.backend.task.OldEntriesCleanupTask;
|
||||
import com.commafeed.backend.task.OldStatusesCleanupTask;
|
||||
import com.commafeed.backend.task.OrphanedContentsCleanupTask;
|
||||
import com.commafeed.backend.task.OrphanedFeedsCleanupTask;
|
||||
import com.commafeed.backend.task.ScheduledTask;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class CommaFeedModule extends AbstractModule {
|
||||
|
||||
@Getter(onMethod = @__({ @Provides }))
|
||||
private final SessionFactory sessionFactory;
|
||||
|
||||
@Getter(onMethod = @__({ @Provides }))
|
||||
private final CommaFeedConfiguration config;
|
||||
|
||||
@Getter(onMethod = @__({ @Provides }))
|
||||
private final MetricRegistry metrics;
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
CacheService cacheService = config.getApplicationSettings().getCache() == CacheType.NOOP ? new NoopCacheService()
|
||||
: new RedisCacheService(config.getRedisPoolFactory().build());
|
||||
log.info("using cache {}", cacheService.getClass());
|
||||
bind(CacheService.class).toInstance(cacheService);
|
||||
|
||||
Multibinder<AbstractFaviconFetcher> faviconMultibinder = Multibinder.newSetBinder(binder(), AbstractFaviconFetcher.class);
|
||||
faviconMultibinder.addBinding().to(YoutubeFaviconFetcher.class);
|
||||
faviconMultibinder.addBinding().to(FacebookFaviconFetcher.class);
|
||||
faviconMultibinder.addBinding().to(DefaultFaviconFetcher.class);
|
||||
|
||||
Multibinder<ScheduledTask> taskMultibinder = Multibinder.newSetBinder(binder(), ScheduledTask.class);
|
||||
taskMultibinder.addBinding().to(OldStatusesCleanupTask.class);
|
||||
taskMultibinder.addBinding().to(OldEntriesCleanupTask.class);
|
||||
taskMultibinder.addBinding().to(OrphanedFeedsCleanupTask.class);
|
||||
taskMultibinder.addBinding().to(OrphanedContentsCleanupTask.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.commafeed.backend;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HeaderElement;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpResponseInterceptor;
|
||||
import org.apache.http.entity.HttpEntityWrapper;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
||||
class ContentEncodingInterceptor implements HttpResponseInterceptor {
|
||||
|
||||
private static final Set<String> ALLOWED_CONTENT_ENCODINGS = new HashSet<>(Arrays.asList("gzip", "x-gzip", "deflate", "identity"));
|
||||
|
||||
@Override
|
||||
public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
|
||||
if (hasContent(response)) {
|
||||
Header contentEncodingHeader = response.getEntity().getContentEncoding();
|
||||
if (contentEncodingHeader != null && containsUnsupportedEncodings(contentEncodingHeader)) {
|
||||
overrideContentEncoding(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsUnsupportedEncodings(Header contentEncodingHeader) {
|
||||
HeaderElement[] codecs = contentEncodingHeader.getElements();
|
||||
|
||||
for (final HeaderElement codec : codecs) {
|
||||
String codecName = codec.getName().toLowerCase(Locale.US);
|
||||
if (!ALLOWED_CONTENT_ENCODINGS.contains(codecName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void overrideContentEncoding(HttpResponse response) {
|
||||
HttpEntity wrapped = new HttpEntityWrapper(response.getEntity()) {
|
||||
@Override
|
||||
public Header getContentEncoding() {
|
||||
return null;
|
||||
};
|
||||
};
|
||||
|
||||
response.setEntity(wrapped);
|
||||
}
|
||||
|
||||
private boolean hasContent(HttpResponse response) {
|
||||
return response.getEntity() != null && response.getEntity().getContentLength() != 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,27 +4,25 @@ import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Consts;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HeaderElement;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpResponseInterceptor;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
@@ -36,51 +34,27 @@ import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.config.ConnectionConfig;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.HttpEntityWrapper;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
|
||||
/**
|
||||
* Smart HTTP getter: handles gzip, ssl, last modified and etag headers
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public class HttpGetter {
|
||||
|
||||
private static final String USER_AGENT = "CommaFeed/1.0 (http://www.commafeed.com)";
|
||||
private static final String ACCEPT_LANGUAGE = "en";
|
||||
private static final String PRAGMA_NO_CACHE = "No-cache";
|
||||
private static final String CACHE_CONTROL_NO_CACHE = "no-cache";
|
||||
|
||||
private static final List<String> ALLOWED_CONTENT_ENCODINGS = Arrays.asList("gzip", "x-gzip", "deflate", "identity");
|
||||
private static final HttpResponseInterceptor REMOVE_INCORRECT_CONTENT_ENCODING = new HttpResponseInterceptor() {
|
||||
|
||||
@Override
|
||||
public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null && entity.getContentLength() != 0) {
|
||||
Header header = entity.getContentEncoding();
|
||||
if (header != null) {
|
||||
HeaderElement[] codecs = header.getElements();
|
||||
for (final HeaderElement codec : codecs) {
|
||||
String codecName = codec.getName().toLowerCase(Locale.US);
|
||||
if (!ALLOWED_CONTENT_ENCODINGS.contains(codecName)) {
|
||||
response.setEntity(new HttpEntityWrapper(entity) {
|
||||
@Override
|
||||
public Header getContentEncoding() {
|
||||
return null;
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
private static final HttpResponseInterceptor REMOVE_INCORRECT_CONTENT_ENCODING = new ContentEncodingInterceptor();
|
||||
|
||||
private static SSLContext SSL_CONTEXT = null;
|
||||
static {
|
||||
@@ -92,6 +66,13 @@ public class HttpGetter {
|
||||
}
|
||||
}
|
||||
|
||||
private String userAgent;
|
||||
|
||||
@Inject
|
||||
public HttpGetter(CommaFeedConfiguration config) {
|
||||
this.userAgent = String.format("CommaFeed/%s (https://www.commafeed.com)", config.getVersion());
|
||||
}
|
||||
|
||||
public HttpResult getBinary(String url, int timeout) throws ClientProtocolException, IOException, NotModifiedException {
|
||||
return getBinary(url, null, null, timeout);
|
||||
}
|
||||
@@ -124,7 +105,7 @@ public class HttpGetter {
|
||||
httpget.addHeader(HttpHeaders.ACCEPT_LANGUAGE, ACCEPT_LANGUAGE);
|
||||
httpget.addHeader(HttpHeaders.PRAGMA, PRAGMA_NO_CACHE);
|
||||
httpget.addHeader(HttpHeaders.CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
|
||||
httpget.addHeader(HttpHeaders.USER_AGENT, USER_AGENT);
|
||||
httpget.addHeader(HttpHeaders.USER_AGENT, userAgent);
|
||||
|
||||
if (lastModified != null) {
|
||||
httpget.addHeader(HttpHeaders.IF_MODIFIED_SINCE, lastModified);
|
||||
@@ -183,47 +164,15 @@ public class HttpGetter {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public static class HttpResult {
|
||||
|
||||
private byte[] content;
|
||||
private String contentType;
|
||||
private String lastModifiedSince;
|
||||
private String eTag;
|
||||
private long duration;
|
||||
private String urlAfterRedirect;
|
||||
|
||||
public HttpResult(byte[] content, String contentType, String lastModifiedSince, String eTag, long duration, String urlAfterRedirect) {
|
||||
this.content = content;
|
||||
this.contentType = contentType;
|
||||
this.lastModifiedSince = lastModifiedSince;
|
||||
this.eTag = eTag;
|
||||
this.duration = duration;
|
||||
this.urlAfterRedirect = urlAfterRedirect;
|
||||
}
|
||||
|
||||
public byte[] getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public String getLastModifiedSince() {
|
||||
return lastModifiedSince;
|
||||
}
|
||||
|
||||
public String geteTag() {
|
||||
return eTag;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public String getUrlAfterRedirect() {
|
||||
return urlAfterRedirect;
|
||||
}
|
||||
private final byte[] content;
|
||||
private final String contentType;
|
||||
private final String lastModifiedSince;
|
||||
private final String eTag;
|
||||
private final long duration;
|
||||
private final String urlAfterRedirect;
|
||||
}
|
||||
|
||||
public static CloseableHttpClient newClient(int timeout) {
|
||||
@@ -232,8 +181,8 @@ public class HttpGetter {
|
||||
builder.addInterceptorFirst(REMOVE_INCORRECT_CONTENT_ENCODING);
|
||||
builder.disableAutomaticRetries();
|
||||
|
||||
builder.setSslcontext(SSL_CONTEXT);
|
||||
builder.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
builder.setSSLContext(SSL_CONTEXT);
|
||||
builder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
|
||||
|
||||
RequestConfig.Builder configBuilder = RequestConfig.custom();
|
||||
configBuilder.setCookieSpec(CookieSpecs.IGNORE_COOKIES);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package com.commafeed.backend.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.Pipeline;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
@@ -18,41 +19,31 @@ import com.commafeed.frontend.model.Category;
|
||||
import com.commafeed.frontend.model.UnreadCount;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RedisCacheService extends CacheService {
|
||||
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
private static ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
private JedisPool pool;
|
||||
|
||||
public RedisCacheService() {
|
||||
JedisPoolConfig config = new JedisPoolConfig();
|
||||
config.setMaxTotal(500);
|
||||
pool = new JedisPool(config, "localhost");
|
||||
}
|
||||
private final JedisPool pool;
|
||||
|
||||
@Override
|
||||
public List<String> getLastEntries(Feed feed) {
|
||||
List<String> list = Lists.newArrayList();
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
List<String> list = new ArrayList<>();
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
String key = buildRedisEntryKey(feed);
|
||||
Set<String> members = jedis.smembers(key);
|
||||
for (String member : members) {
|
||||
list.add(member);
|
||||
}
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastEntries(Feed feed, List<String> entries) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
String key = buildRedisEntryKey(feed);
|
||||
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
@@ -62,87 +53,72 @@ public class RedisCacheService extends CacheService {
|
||||
}
|
||||
pipe.expire(key, (int) TimeUnit.DAYS.toSeconds(7));
|
||||
pipe.sync();
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category getUserRootCategory(User user) {
|
||||
Category cat = null;
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
String key = buildRedisUserRootCategoryKey(user);
|
||||
String json = jedis.get(key);
|
||||
if (json != null) {
|
||||
cat = mapper.readValue(json, Category.class);
|
||||
cat = MAPPER.readValue(json, Category.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
return cat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserRootCategory(User user, Category category) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
String key = buildRedisUserRootCategoryKey(user);
|
||||
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
pipe.del(key);
|
||||
pipe.set(key, mapper.writeValueAsString(category));
|
||||
pipe.set(key, MAPPER.writeValueAsString(category));
|
||||
pipe.expire(key, (int) TimeUnit.MINUTES.toSeconds(30));
|
||||
pipe.sync();
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnreadCount getUnreadCount(FeedSubscription sub) {
|
||||
UnreadCount count = null;
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
String key = buildRedisUnreadCountKey(sub);
|
||||
String json = jedis.get(key);
|
||||
if (json != null) {
|
||||
count = mapper.readValue(json, UnreadCount.class);
|
||||
count = MAPPER.readValue(json, UnreadCount.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnreadCount(FeedSubscription sub, UnreadCount count) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
String key = buildRedisUnreadCountKey(sub);
|
||||
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
pipe.del(key);
|
||||
pipe.set(key, mapper.writeValueAsString(count));
|
||||
pipe.set(key, MAPPER.writeValueAsString(count));
|
||||
pipe.expire(key, (int) TimeUnit.MINUTES.toSeconds(30));
|
||||
pipe.sync();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateUserRootCategory(User... users) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
if (users != null) {
|
||||
for (User user : users) {
|
||||
@@ -151,15 +127,12 @@ public class RedisCacheService extends CacheService {
|
||||
}
|
||||
}
|
||||
pipe.sync();
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateUnreadCount(FeedSubscription... subs) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
if (subs != null) {
|
||||
for (FeedSubscription sub : subs) {
|
||||
@@ -168,8 +141,6 @@ public class RedisCacheService extends CacheService {
|
||||
}
|
||||
}
|
||||
pipe.sync();
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
28
src/main/java/com/commafeed/backend/cache/RedisPoolFactory.java
vendored
Normal file
28
src/main/java/com/commafeed/backend/cache/RedisPoolFactory.java
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.commafeed.backend.cache;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.Protocol;
|
||||
|
||||
@Getter
|
||||
public class RedisPoolFactory {
|
||||
private String host = "localhost";
|
||||
private int port = Protocol.DEFAULT_PORT;
|
||||
private String password = null;
|
||||
private int timeout = Protocol.DEFAULT_TIMEOUT;
|
||||
private int database = Protocol.DEFAULT_DATABASE;
|
||||
|
||||
private int maxTotal = 500;
|
||||
|
||||
public JedisPool build() {
|
||||
JedisPoolConfig config = new JedisPoolConfig();
|
||||
config.setMaxTotal(maxTotal);
|
||||
|
||||
return new JedisPool(config, host, port, timeout, StringUtils.trimToNull(password), database);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +1,26 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.FeedCategory;
|
||||
import com.commafeed.backend.model.QFeedCategory;
|
||||
import com.commafeed.backend.model.QUser;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
@Singleton
|
||||
public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||
|
||||
private QFeedCategory category = QFeedCategory.feedCategory;
|
||||
|
||||
@Inject
|
||||
public FeedCategoryDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
@@ -49,23 +54,16 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||
}
|
||||
|
||||
public List<FeedCategory> findAllChildrenCategories(User user, FeedCategory parent) {
|
||||
List<FeedCategory> list = Lists.newArrayList();
|
||||
List<FeedCategory> all = findAll(user);
|
||||
for (FeedCategory cat : all) {
|
||||
if (isChild(cat, parent)) {
|
||||
list.add(cat);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
return findAll(user).stream().filter(c -> isChild(c, parent)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public boolean isChild(FeedCategory child, FeedCategory parent) {
|
||||
private boolean isChild(FeedCategory child, FeedCategory parent) {
|
||||
if (parent == null) {
|
||||
return true;
|
||||
}
|
||||
boolean isChild = false;
|
||||
while (child != null) {
|
||||
if (ObjectUtils.equals(child.getId(), parent.getId())) {
|
||||
if (Objects.equals(child.getId(), parent.getId())) {
|
||||
isChild = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ package com.commafeed.backend.dao;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
@@ -16,41 +19,29 @@ import com.mysema.query.BooleanBuilder;
|
||||
import com.mysema.query.jpa.hibernate.HibernateQuery;
|
||||
import com.mysema.query.jpa.hibernate.HibernateSubQuery;
|
||||
|
||||
@Singleton
|
||||
public class FeedDAO extends GenericDAO<Feed> {
|
||||
|
||||
private QFeed feed = QFeed.feed;
|
||||
|
||||
@Inject
|
||||
public FeedDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
public Long getUpdatableCount(Date lastLoginThreshold) {
|
||||
BooleanBuilder disabledDatePredicate = new BooleanBuilder();
|
||||
disabledDatePredicate.or(feed.disabledUntil.isNull());
|
||||
disabledDatePredicate.or(feed.disabledUntil.lt(new Date()));
|
||||
|
||||
HibernateQuery query = newQuery().from(feed).where(disabledDatePredicate);
|
||||
if (lastLoginThreshold != null) {
|
||||
QFeedSubscription sub = QFeedSubscription.feedSubscription;
|
||||
QUser user = QUser.user;
|
||||
HibernateSubQuery subquery = new HibernateSubQuery();
|
||||
subquery.from(sub).join(sub.user, user).where(sub.feed.eq(feed), user.lastLogin.gt(lastLoginThreshold));
|
||||
query.where(subquery.exists());
|
||||
}
|
||||
return query.orderBy(feed.disabledUntil.asc()).count();
|
||||
}
|
||||
|
||||
public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) {
|
||||
BooleanBuilder disabledDatePredicate = new BooleanBuilder();
|
||||
disabledDatePredicate.or(feed.disabledUntil.isNull());
|
||||
disabledDatePredicate.or(feed.disabledUntil.lt(new Date()));
|
||||
|
||||
HibernateQuery query = newQuery().from(feed);
|
||||
HibernateQuery query = null;
|
||||
if (lastLoginThreshold != null) {
|
||||
QFeedSubscription subs = QFeedSubscription.feedSubscription;
|
||||
QUser user = QUser.user;
|
||||
query.join(feed.subscriptions, subs).join(subs.user, user).where(disabledDatePredicate, user.lastLogin.gt(lastLoginThreshold));
|
||||
query = newQuery().from(subs);
|
||||
query.join(subs.feed, feed).join(subs.user, user).where(disabledDatePredicate, user.lastLogin.gt(lastLoginThreshold));
|
||||
} else {
|
||||
query = newQuery().from(feed);
|
||||
query.where(disabledDatePredicate);
|
||||
}
|
||||
|
||||
@@ -72,6 +63,7 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
|
||||
public List<Feed> findWithoutSubscriptions(int max) {
|
||||
QFeedSubscription sub = QFeedSubscription.feedSubscription;
|
||||
return newQuery().from(feed).leftJoin(feed.subscriptions, sub).where(sub.id.isNull()).limit(max).list(feed);
|
||||
return newQuery().from(feed).where(new HibernateSubQuery().from(sub).where(sub.feed.eq(feed)).notExists()).limit(max).list(feed);
|
||||
// return newQuery().from(feed).leftJoin(feed.subscriptions, sub).where(sub.id.isNull()).limit(max).list(feed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,32 +2,39 @@ package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.QFeedEntry;
|
||||
import com.commafeed.backend.model.QFeedEntryContent;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.mysema.query.types.ConstructorExpression;
|
||||
import com.mysema.query.jpa.hibernate.HibernateSubQuery;
|
||||
|
||||
@Singleton
|
||||
public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
|
||||
|
||||
private QFeedEntryContent content = QFeedEntryContent.feedEntryContent;
|
||||
|
||||
@Inject
|
||||
public FeedEntryContentDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
public Long findExisting(String contentHash, String titleHash) {
|
||||
List<Long> list = newQuery().from(content).where(content.contentHash.eq(contentHash), content.titleHash.eq(titleHash)).limit(1)
|
||||
.list(ConstructorExpression.create(Long.class, content.id));
|
||||
.list(content.id);
|
||||
return Iterables.getFirst(list, null);
|
||||
}
|
||||
|
||||
public int deleteWithoutEntries(int max) {
|
||||
QFeedEntry entry = QFeedEntry.feedEntry;
|
||||
List<FeedEntryContent> list = newQuery().from(content).leftJoin(content.entries, entry).where(entry.id.isNull()).limit(max)
|
||||
.list(content);
|
||||
|
||||
HibernateSubQuery subQuery = new HibernateSubQuery().from(entry).where(entry.content.id.eq(content.id));
|
||||
List<FeedEntryContent> list = newQuery().from(content).where(subQuery.notExists()).limit(max).list(content);
|
||||
|
||||
int deleted = list.size();
|
||||
delete(list);
|
||||
return deleted;
|
||||
|
||||
@@ -1,51 +1,60 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.QFeed;
|
||||
import com.commafeed.backend.model.QFeedEntry;
|
||||
import com.commafeed.backend.model.QFeedSubscription;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.mysema.query.types.ConstructorExpression;
|
||||
import com.mysema.query.Tuple;
|
||||
import com.mysema.query.types.expr.NumberExpression;
|
||||
|
||||
@Singleton
|
||||
public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
|
||||
private QFeedEntry entry = QFeedEntry.feedEntry;
|
||||
|
||||
@Inject
|
||||
public FeedEntryDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
public Long findExisting(String guid, Feed feed) {
|
||||
List<Long> list = newQuery().from(entry).where(entry.guidHash.eq(DigestUtils.sha1Hex(guid)), entry.feed.eq(feed)).limit(1)
|
||||
.list(ConstructorExpression.create(Long.class, entry.id));
|
||||
.list(entry.id);
|
||||
return Iterables.getFirst(list, null);
|
||||
}
|
||||
|
||||
public List<FeedEntry> findWithoutSubscriptions(int max) {
|
||||
QFeed feed = QFeed.feed;
|
||||
QFeedSubscription sub = QFeedSubscription.feedSubscription;
|
||||
return newQuery().from(entry).join(entry.feed, feed).leftJoin(feed.subscriptions, sub).where(sub.id.isNull()).limit(max)
|
||||
.list(entry);
|
||||
public List<FeedCapacity> findFeedsExceedingCapacity(long maxCapacity, long max) {
|
||||
NumberExpression<Long> count = entry.id.count();
|
||||
List<Tuple> tuples = newQuery().from(entry).groupBy(entry.feed).having(count.gt(maxCapacity)).limit(max).list(entry.feed.id, count);
|
||||
return tuples.stream().map(t -> new FeedCapacity(t.get(entry.feed.id), t.get(count))).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public int delete(Feed feed, int max) {
|
||||
List<FeedEntry> list = newQuery().from(entry).where(entry.feed.eq(feed)).limit(max).list(entry);
|
||||
int deleted = list.size();
|
||||
delete(list);
|
||||
return deleted;
|
||||
public int delete(Long feedId, long max) {
|
||||
List<FeedEntry> list = newQuery().from(entry).where(entry.feed.id.eq(feedId)).limit(max).list(entry);
|
||||
return delete(list);
|
||||
}
|
||||
|
||||
public int delete(Date olderThan, int max) {
|
||||
List<FeedEntry> list = newQuery().from(entry).where(entry.inserted.lt(olderThan)).limit(max).list(entry);
|
||||
int deleted = list.size();
|
||||
delete(list);
|
||||
return deleted;
|
||||
public int deleteOldEntries(Long feedId, long max) {
|
||||
List<FeedEntry> list = newQuery().from(entry).where(entry.feed.id.eq(feedId)).orderBy(entry.updated.asc()).limit(max).list(entry);
|
||||
return delete(list);
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class FeedCapacity {
|
||||
private Long id;
|
||||
private Long capacity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.builder.CompareToBuilder;
|
||||
import org.hibernate.Criteria;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.criterion.Conjunction;
|
||||
import org.hibernate.criterion.Disjunction;
|
||||
import org.hibernate.criterion.MatchMode;
|
||||
import org.hibernate.criterion.Order;
|
||||
import org.hibernate.criterion.ProjectionList;
|
||||
import org.hibernate.criterion.Projections;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.sql.JoinType;
|
||||
import org.hibernate.transform.Transformers;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.FixedSizeSortedSet;
|
||||
import com.commafeed.backend.feed.FeedEntryKeyword;
|
||||
import com.commafeed.backend.feed.FeedEntryKeyword.Mode;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedEntryTag;
|
||||
@@ -34,22 +29,24 @@ import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserSettings.ReadingOrder;
|
||||
import com.commafeed.frontend.model.UnreadCount;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.mysema.query.BooleanBuilder;
|
||||
import com.mysema.query.Tuple;
|
||||
import com.mysema.query.jpa.hibernate.HibernateQuery;
|
||||
|
||||
@Singleton
|
||||
public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
|
||||
private static final String ALIAS_STATUS = "status";
|
||||
private static final String ALIAS_ENTRY = "entry";
|
||||
private static final String ALIAS_TAG = "tag";
|
||||
|
||||
private FeedEntryDAO feedEntryDAO;
|
||||
private FeedEntryTagDAO feedEntryTagDAO;
|
||||
private CommaFeedConfiguration config;
|
||||
|
||||
private QFeedEntryStatus status = QFeedEntryStatus.feedEntryStatus;
|
||||
private QFeedEntry entry = QFeedEntry.feedEntry;
|
||||
private QFeedEntryContent content = QFeedEntryContent.feedEntryContent;
|
||||
private QFeedEntryTag entryTag = QFeedEntryTag.feedEntryTag;
|
||||
|
||||
@Inject
|
||||
public FeedEntryStatusDAO(SessionFactory sessionFactory, FeedEntryDAO feedEntryDAO, FeedEntryTagDAO feedEntryTagDAO,
|
||||
CommaFeedConfiguration config) {
|
||||
super(sessionFactory);
|
||||
@@ -107,112 +104,105 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
query.orderBy(status.entryUpdated.desc(), status.id.desc());
|
||||
}
|
||||
|
||||
query.offset(offset).limit(limit).setTimeout(config.getApplicationSettings().getQueryTimeout());
|
||||
query.offset(offset).limit(limit);
|
||||
int timeout = config.getApplicationSettings().getQueryTimeout();
|
||||
if (timeout > 0) {
|
||||
query.setTimeout(timeout / 1000);
|
||||
}
|
||||
|
||||
List<FeedEntryStatus> statuses = query.list(status);
|
||||
for (FeedEntryStatus status : statuses) {
|
||||
status = handleStatus(user, status, status.getSubscription(), status.getEntry());
|
||||
status = fetchTags(user, status);
|
||||
fetchTags(user, status);
|
||||
}
|
||||
return lazyLoadContent(includeContent, statuses);
|
||||
}
|
||||
|
||||
private Criteria buildSearchCriteria(User user, FeedSubscription sub, boolean unreadOnly, String keywords, Date newerThan, int offset,
|
||||
int limit, ReadingOrder order, Date last, String tag) {
|
||||
QFeedEntry entry = QFeedEntry.feedEntry;
|
||||
QFeedEntryContent content = QFeedEntryContent.feedEntryContent;
|
||||
QFeedEntryStatus status = QFeedEntryStatus.feedEntryStatus;
|
||||
QFeedEntryTag entryTag = QFeedEntryTag.feedEntryTag;
|
||||
private HibernateQuery buildQuery(User user, FeedSubscription sub, boolean unreadOnly, List<FeedEntryKeyword> keywords, Date newerThan,
|
||||
int offset, int limit, ReadingOrder order, Date last, String tag) {
|
||||
|
||||
Criteria criteria = currentSession().createCriteria(FeedEntry.class, ALIAS_ENTRY);
|
||||
criteria.add(Restrictions.eq(entry.feed.getMetadata().getName(), sub.getFeed()));
|
||||
HibernateQuery query = newQuery().from(entry).where(entry.feed.eq(sub.getFeed()));
|
||||
|
||||
if (keywords != null) {
|
||||
Criteria contentJoin = criteria.createCriteria(entry.content.getMetadata().getName(), "content", JoinType.INNER_JOIN);
|
||||
if (CollectionUtils.isNotEmpty(keywords)) {
|
||||
query.join(entry.content, content);
|
||||
|
||||
for (String keyword : StringUtils.split(keywords)) {
|
||||
Disjunction or = Restrictions.disjunction();
|
||||
or.add(Restrictions.ilike(content.content.getMetadata().getName(), keyword, MatchMode.ANYWHERE));
|
||||
or.add(Restrictions.ilike(content.title.getMetadata().getName(), keyword, MatchMode.ANYWHERE));
|
||||
contentJoin.add(or);
|
||||
for (FeedEntryKeyword keyword : keywords) {
|
||||
BooleanBuilder or = new BooleanBuilder();
|
||||
or.or(content.content.containsIgnoreCase(keyword.getKeyword()));
|
||||
or.or(content.title.containsIgnoreCase(keyword.getKeyword()));
|
||||
if (keyword.getMode() == Mode.EXCLUDE) {
|
||||
or.not();
|
||||
}
|
||||
query.where(or);
|
||||
}
|
||||
}
|
||||
Criteria statusJoin = criteria.createCriteria(entry.statuses.getMetadata().getName(), ALIAS_STATUS, JoinType.LEFT_OUTER_JOIN,
|
||||
Restrictions.eq(status.subscription.getMetadata().getName(), sub));
|
||||
query.leftJoin(entry.statuses, status).on(status.subscription.id.eq(sub.getId()));
|
||||
|
||||
if (unreadOnly && tag == null) {
|
||||
|
||||
Disjunction or = Restrictions.disjunction();
|
||||
or.add(Restrictions.isNull(status.read.getMetadata().getName()));
|
||||
or.add(Restrictions.eq(status.read.getMetadata().getName(), false));
|
||||
statusJoin.add(or);
|
||||
BooleanBuilder or = new BooleanBuilder();
|
||||
or.or(status.read.isNull());
|
||||
or.or(status.read.isFalse());
|
||||
query.where(or);
|
||||
|
||||
Date unreadThreshold = config.getApplicationSettings().getUnreadThreshold();
|
||||
if (unreadThreshold != null) {
|
||||
criteria.add(Restrictions.ge(entry.updated.getMetadata().getName(), unreadThreshold));
|
||||
query.where(entry.updated.goe(unreadThreshold));
|
||||
}
|
||||
}
|
||||
|
||||
if (tag != null) {
|
||||
Conjunction and = Restrictions.conjunction();
|
||||
and.add(Restrictions.eq(entryTag.user.getMetadata().getName(), user));
|
||||
and.add(Restrictions.eq(entryTag.name.getMetadata().getName(), tag));
|
||||
criteria.createCriteria(entry.tags.getMetadata().getName(), ALIAS_TAG, JoinType.INNER_JOIN, and);
|
||||
BooleanBuilder and = new BooleanBuilder();
|
||||
and.and(entryTag.user.id.eq(user.getId()));
|
||||
and.and(entryTag.name.eq(tag));
|
||||
query.join(entry.tags, entryTag).on(and);
|
||||
}
|
||||
|
||||
if (newerThan != null) {
|
||||
criteria.add(Restrictions.ge(entry.inserted.getMetadata().getName(), newerThan));
|
||||
query.where(entry.inserted.goe(newerThan));
|
||||
}
|
||||
|
||||
if (last != null) {
|
||||
if (order == ReadingOrder.desc) {
|
||||
criteria.add(Restrictions.gt(entry.updated.getMetadata().getName(), last));
|
||||
query.where(entry.updated.gt(last));
|
||||
} else {
|
||||
criteria.add(Restrictions.lt(entry.updated.getMetadata().getName(), last));
|
||||
query.where(entry.updated.lt(last));
|
||||
}
|
||||
}
|
||||
|
||||
if (order != null) {
|
||||
if (order == ReadingOrder.asc) {
|
||||
criteria.addOrder(Order.asc(entry.updated.getMetadata().getName())).addOrder(Order.asc(entry.id.getMetadata().getName()));
|
||||
query.orderBy(entry.updated.asc(), entry.id.asc());
|
||||
} else {
|
||||
criteria.addOrder(Order.desc(entry.updated.getMetadata().getName())).addOrder(Order.desc(entry.id.getMetadata().getName()));
|
||||
query.orderBy(entry.updated.desc(), entry.id.desc());
|
||||
}
|
||||
}
|
||||
if (offset > -1) {
|
||||
criteria.setFirstResult(offset);
|
||||
query.offset(offset);
|
||||
}
|
||||
if (limit > -1) {
|
||||
criteria.setMaxResults(limit);
|
||||
query.limit(limit);
|
||||
}
|
||||
int timeout = config.getApplicationSettings().getQueryTimeout();
|
||||
if (timeout > 0) {
|
||||
// hibernate timeout is in seconds, jpa timeout is in millis
|
||||
criteria.setTimeout(timeout / 1000);
|
||||
query.setTimeout(timeout / 1000);
|
||||
}
|
||||
return criteria;
|
||||
return query;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<FeedEntryStatus> findBySubscriptions(User user, List<FeedSubscription> subs, boolean unreadOnly, String keywords,
|
||||
Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent, boolean onlyIds, String tag) {
|
||||
public List<FeedEntryStatus> findBySubscriptions(User user, List<FeedSubscription> subs, boolean unreadOnly,
|
||||
List<FeedEntryKeyword> keywords, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent,
|
||||
boolean onlyIds, String tag) {
|
||||
int capacity = offset + limit;
|
||||
Comparator<FeedEntryStatus> comparator = order == ReadingOrder.desc ? STATUS_COMPARATOR_DESC : STATUS_COMPARATOR_ASC;
|
||||
FixedSizeSortedSet<FeedEntryStatus> set = new FixedSizeSortedSet<FeedEntryStatus>(capacity, comparator);
|
||||
for (FeedSubscription sub : subs) {
|
||||
Date last = (order != null && set.isFull()) ? set.last().getEntryUpdated() : null;
|
||||
Criteria criteria = buildSearchCriteria(user, sub, unreadOnly, keywords, newerThan, -1, capacity, order, last, tag);
|
||||
ProjectionList projection = Projections.projectionList();
|
||||
projection.add(Projections.property("id"), "id");
|
||||
projection.add(Projections.property("updated"), "updated");
|
||||
projection.add(Projections.property(ALIAS_STATUS + ".id"), "status_id");
|
||||
criteria.setProjection(projection);
|
||||
criteria.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
|
||||
List<Map<String, Object>> list = criteria.list();
|
||||
for (Map<String, Object> map : list) {
|
||||
Long id = (Long) map.get("id");
|
||||
Date updated = (Date) map.get("updated");
|
||||
Long statusId = (Long) map.get("status_id");
|
||||
HibernateQuery query = buildQuery(user, sub, unreadOnly, keywords, newerThan, -1, capacity, order, last, tag);
|
||||
List<Tuple> tuples = query.list(entry.id, entry.updated, status.id);
|
||||
for (Tuple tuple : tuples) {
|
||||
Long id = tuple.get(entry.id);
|
||||
Date updated = tuple.get(entry.updated);
|
||||
Long statusId = tuple.get(status.id);
|
||||
|
||||
FeedEntry entry = new FeedEntry();
|
||||
entry.setId(id);
|
||||
@@ -231,7 +221,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
List<FeedEntryStatus> placeholders = set.asList();
|
||||
int size = placeholders.size();
|
||||
if (size < offset) {
|
||||
return Lists.newArrayList();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
placeholders = placeholders.subList(Math.max(offset, 0), size);
|
||||
|
||||
@@ -239,7 +229,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
if (onlyIds) {
|
||||
statuses = placeholders;
|
||||
} else {
|
||||
statuses = Lists.newArrayList();
|
||||
statuses = new ArrayList<>();
|
||||
for (FeedEntryStatus placeholder : placeholders) {
|
||||
Long statusId = placeholder.getId();
|
||||
FeedEntry entry = feedEntryDAO.findById(placeholder.getEntry().getId());
|
||||
@@ -253,19 +243,13 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
return statuses;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public UnreadCount getUnreadCount(User user, FeedSubscription subscription) {
|
||||
UnreadCount uc = null;
|
||||
Criteria criteria = buildSearchCriteria(user, subscription, true, null, null, -1, -1, null, null, null);
|
||||
ProjectionList projection = Projections.projectionList();
|
||||
projection.add(Projections.rowCount(), "count");
|
||||
projection.add(Projections.max(QFeedEntry.feedEntry.updated.getMetadata().getName()), "updated");
|
||||
criteria.setProjection(projection);
|
||||
criteria.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
|
||||
List<Map<String, Object>> list = criteria.list();
|
||||
for (Map<String, Object> row : list) {
|
||||
Long count = (Long) row.get("count");
|
||||
Date updated = (Date) row.get("updated");
|
||||
HibernateQuery query = buildQuery(user, subscription, true, null, null, -1, -1, null, null, null);
|
||||
List<Tuple> tuples = query.list(entry.count(), entry.updated.max());
|
||||
for (Tuple tuple : tuples) {
|
||||
Long count = tuple.get(entry.count());
|
||||
Date updated = tuple.get(entry.updated.max());
|
||||
uc = new UnreadCount(subscription.getId(), count, updated);
|
||||
}
|
||||
return uc;
|
||||
|
||||
@@ -2,24 +2,28 @@ package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryTag;
|
||||
import com.commafeed.backend.model.QFeedEntryTag;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.mysema.query.types.ConstructorExpression;
|
||||
|
||||
@Singleton
|
||||
public class FeedEntryTagDAO extends GenericDAO<FeedEntryTag> {
|
||||
|
||||
private QFeedEntryTag tag = QFeedEntryTag.feedEntryTag;
|
||||
|
||||
@Inject
|
||||
public FeedEntryTagDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
public List<String> findByUser(User user) {
|
||||
return newQuery().from(tag).where(tag.user.eq(user)).distinct().list(ConstructorExpression.create(String.class, tag.name));
|
||||
return newQuery().from(tag).where(tag.user.eq(user)).distinct().list(tag.name);
|
||||
}
|
||||
|
||||
public List<FeedEntryTag> findByEntry(User user, FeedEntry entry) {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
@@ -10,15 +15,15 @@ import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.Models;
|
||||
import com.commafeed.backend.model.QFeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mysema.query.jpa.hibernate.HibernateQuery;
|
||||
|
||||
@Singleton
|
||||
public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
||||
|
||||
private QFeedSubscription sub = QFeedSubscription.feedSubscription;
|
||||
|
||||
@Inject
|
||||
public FeedSubscriptionDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
@@ -55,26 +60,13 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
||||
}
|
||||
|
||||
public List<FeedSubscription> findByCategories(User user, List<FeedCategory> categories) {
|
||||
List<Long> categoryIds = Lists.transform(categories, new Function<FeedCategory, Long>() {
|
||||
@Override
|
||||
public Long apply(FeedCategory input) {
|
||||
return input.getId();
|
||||
}
|
||||
});
|
||||
|
||||
List<FeedSubscription> subscriptions = Lists.newArrayList();
|
||||
for (FeedSubscription sub : findAll(user)) {
|
||||
if (sub.getCategory() != null && categoryIds.contains(sub.getCategory().getId())) {
|
||||
subscriptions.add(sub);
|
||||
}
|
||||
}
|
||||
return subscriptions;
|
||||
Set<Long> categoryIds = categories.stream().map(c -> c.getId()).collect(Collectors.toSet());
|
||||
return findAll(user).stream().filter(s -> s.getCategory() != null && categoryIds.contains(s.getCategory().getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<FeedSubscription> initRelations(List<FeedSubscription> list) {
|
||||
for (FeedSubscription sub : list) {
|
||||
initRelations(sub);
|
||||
}
|
||||
list.forEach(s -> initRelations(s));
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.mysema.query.jpa.hibernate.HibernateQuery;
|
||||
|
||||
public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T> {
|
||||
|
||||
public GenericDAO(SessionFactory sessionFactory) {
|
||||
protected GenericDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
@@ -24,19 +24,7 @@ public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T>
|
||||
}
|
||||
|
||||
public void saveOrUpdate(Collection<T> models) {
|
||||
for (T model : models) {
|
||||
persist(model);
|
||||
}
|
||||
}
|
||||
|
||||
public void merge(T model) {
|
||||
currentSession().merge(model);
|
||||
}
|
||||
|
||||
public void merge(Collection<T> models) {
|
||||
for (T model : models) {
|
||||
merge(model);
|
||||
}
|
||||
models.forEach(m -> persist(m));
|
||||
}
|
||||
|
||||
public T findById(Long id) {
|
||||
@@ -50,15 +38,8 @@ public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T>
|
||||
}
|
||||
|
||||
public int delete(Collection<T> objects) {
|
||||
for (T object : objects) {
|
||||
delete(object);
|
||||
}
|
||||
objects.forEach(o -> delete(o));
|
||||
return objects.size();
|
||||
}
|
||||
|
||||
protected void setTimeout(javax.persistence.Query query, int queryTimeout) {
|
||||
if (queryTimeout > 0) {
|
||||
query.setHint("javax.persistence.query.timeout", queryTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,26 @@ import org.hibernate.SessionFactory;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.context.internal.ManagedSessionContext;
|
||||
|
||||
public abstract class UnitOfWork<T> {
|
||||
public class UnitOfWork {
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
public UnitOfWork(SessionFactory sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
@FunctionalInterface
|
||||
public static interface SessionRunner {
|
||||
public void runInSession();
|
||||
}
|
||||
|
||||
protected abstract T runInSession() throws Exception;
|
||||
@FunctionalInterface
|
||||
public static interface SessionRunnerReturningValue<T> {
|
||||
public T runInSession();
|
||||
}
|
||||
|
||||
public T run() {
|
||||
public static void run(SessionFactory sessionFactory, SessionRunner sessionRunner) {
|
||||
run(sessionFactory, () -> {
|
||||
sessionRunner.runInSession();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> T run(SessionFactory sessionFactory, SessionRunnerReturningValue<T> sessionRunner) {
|
||||
final Session session = sessionFactory.openSession();
|
||||
if (ManagedSessionContext.hasBind(sessionFactory)) {
|
||||
throw new IllegalStateException("Already in a unit of work!");
|
||||
@@ -25,11 +34,11 @@ public abstract class UnitOfWork<T> {
|
||||
ManagedSessionContext.bind(session);
|
||||
session.beginTransaction();
|
||||
try {
|
||||
t = runInSession();
|
||||
t = sessionRunner.runInSession();
|
||||
commitTransaction(session);
|
||||
} catch (Exception e) {
|
||||
rollbackTransaction(session);
|
||||
this.<RuntimeException> rethrow(e);
|
||||
UnitOfWork.<RuntimeException> rethrow(e);
|
||||
}
|
||||
} finally {
|
||||
session.close();
|
||||
@@ -38,14 +47,14 @@ public abstract class UnitOfWork<T> {
|
||||
return t;
|
||||
}
|
||||
|
||||
private void rollbackTransaction(Session session) {
|
||||
private static void rollbackTransaction(Session session) {
|
||||
final Transaction txn = session.getTransaction();
|
||||
if (txn != null && txn.isActive()) {
|
||||
txn.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
private void commitTransaction(Session session) {
|
||||
private static void commitTransaction(Session session) {
|
||||
final Transaction txn = session.getTransaction();
|
||||
if (txn != null && txn.isActive()) {
|
||||
txn.commit();
|
||||
@@ -53,7 +62,7 @@ public abstract class UnitOfWork<T> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <E extends Exception> void rethrow(Exception e) throws E {
|
||||
private static <E extends Exception> void rethrow(Exception e) throws E {
|
||||
throw (E) e;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.QUser;
|
||||
import com.commafeed.backend.model.QUserRole;
|
||||
import com.commafeed.backend.model.User;
|
||||
|
||||
@Singleton
|
||||
public class UserDAO extends GenericDAO<User> {
|
||||
|
||||
private QUser user = QUser.user;
|
||||
|
||||
@Inject
|
||||
public UserDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
public User findByName(String name) {
|
||||
return newQuery().from(user).where(user.name.equalsIgnoreCase(name)).leftJoin(user.roles, QUserRole.userRole).fetch()
|
||||
.uniqueResult(user);
|
||||
return newQuery().from(user).where(user.name.equalsIgnoreCase(name)).uniqueResult(user);
|
||||
}
|
||||
|
||||
public User findByApiKey(String key) {
|
||||
return newQuery().from(user).where(user.apiKey.equalsIgnoreCase(key)).leftJoin(user.roles, QUserRole.userRole).fetch()
|
||||
.uniqueResult(user);
|
||||
return newQuery().from(user).where(user.apiKey.equalsIgnoreCase(key)).uniqueResult(user);
|
||||
}
|
||||
|
||||
public User findByEmail(String email) {
|
||||
return newQuery().from(user).where(user.email.equalsIgnoreCase(email)).leftJoin(user.roles, QUserRole.userRole).fetch()
|
||||
.uniqueResult(user);
|
||||
return newQuery().from(user).where(user.email.equalsIgnoreCase(email)).uniqueResult(user);
|
||||
}
|
||||
|
||||
public long count() {
|
||||
|
||||
@@ -2,6 +2,10 @@ package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
@@ -9,12 +13,13 @@ import com.commafeed.backend.model.QUserRole;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserRole;
|
||||
import com.commafeed.backend.model.UserRole.Role;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
@Singleton
|
||||
public class UserRoleDAO extends GenericDAO<UserRole> {
|
||||
|
||||
private QUserRole role = QUserRole.userRole;
|
||||
|
||||
@Inject
|
||||
public UserRoleDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
@@ -28,10 +33,6 @@ public class UserRoleDAO extends GenericDAO<UserRole> {
|
||||
}
|
||||
|
||||
public Set<Role> findRoles(User user) {
|
||||
Set<Role> list = Sets.newHashSet();
|
||||
for (UserRole role : findAll(user)) {
|
||||
list.add(role.getRole());
|
||||
}
|
||||
return list;
|
||||
return findAll(user).stream().map(r -> r.getRole()).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.QUserSettings;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserSettings;
|
||||
|
||||
@Singleton
|
||||
public class UserSettingsDAO extends GenericDAO<UserSettings> {
|
||||
|
||||
private QUserSettings settings = QUserSettings.userSettings;
|
||||
|
||||
@Inject
|
||||
public UserSettingsDAO(SessionFactory sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractFaviconFetcher {
|
||||
|
||||
private static List<String> ICON_MIMETYPE_BLACKLIST = Arrays.asList("application/xml", "text/html");
|
||||
private static long MIN_ICON_LENGTH = 100;
|
||||
private static long MAX_ICON_LENGTH = 100000;
|
||||
|
||||
protected static int TIMEOUT = 4000;
|
||||
|
||||
public abstract Favicon fetch(Feed feed);
|
||||
|
||||
protected boolean isValidIconResponse(byte[] content, String contentType) {
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long length = content.length;
|
||||
|
||||
if (StringUtils.isNotBlank(contentType)) {
|
||||
contentType = contentType.split(";")[0];
|
||||
}
|
||||
|
||||
if (ICON_MIMETYPE_BLACKLIST.contains(contentType)) {
|
||||
log.debug("Content-Type {} is blacklisted", contentType);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length < MIN_ICON_LENGTH) {
|
||||
log.debug("Length {} below MIN_ICON_LENGTH {}", length, MIN_ICON_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length > MAX_ICON_LENGTH) {
|
||||
log.debug("Length {} greater than MAX_ICON_LENGTH {}", length, MAX_ICON_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public static class Favicon {
|
||||
private final byte[] icon;
|
||||
private final String mediaType;
|
||||
}
|
||||
}
|
||||
@@ -1,156 +1,132 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
|
||||
/**
|
||||
* Inspired/Ported from https://github.com/potatolondon/getfavicon
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class FaviconFetcher {
|
||||
|
||||
private static long MIN_ICON_LENGTH = 100;
|
||||
private static long MAX_ICON_LENGTH = 100000;
|
||||
private static int TIMEOUT = 4000;
|
||||
|
||||
protected static List<String> ICON_MIMETYPES = Arrays.asList("image/x-icon", "image/vnd.microsoft.icon", "image/ico", "image/icon",
|
||||
"text/ico", "application/ico", "image/x-ms-bmp", "image/x-bmp", "image/gif", "image/png", "image/jpeg");
|
||||
private static List<String> ICON_MIMETYPE_BLACKLIST = Arrays.asList("application/xml", "text/html");
|
||||
|
||||
private final HttpGetter getter;
|
||||
|
||||
public byte[] fetch(String url) {
|
||||
|
||||
if (url == null) {
|
||||
log.debug("url is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
int doubleSlash = url.indexOf("//");
|
||||
if (doubleSlash == -1) {
|
||||
doubleSlash = 0;
|
||||
} else {
|
||||
doubleSlash += 2;
|
||||
}
|
||||
int firstSlash = url.indexOf('/', doubleSlash);
|
||||
if (firstSlash != -1) {
|
||||
url = url.substring(0, firstSlash);
|
||||
}
|
||||
|
||||
byte[] icon = getIconAtRoot(url);
|
||||
|
||||
if (icon == null) {
|
||||
icon = getIconInPage(url);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private byte[] getIconAtRoot(String url) {
|
||||
byte[] bytes = null;
|
||||
String contentType = null;
|
||||
|
||||
try {
|
||||
url = FeedUtils.removeTrailingSlash(url) + "/favicon.ico";
|
||||
log.debug("getting root icon at {}", url);
|
||||
HttpResult result = getter.getBinary(url, TIMEOUT);
|
||||
bytes = result.getContent();
|
||||
contentType = result.getContentType();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to retrieve iconAtRoot: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (!isValidIconResponse(bytes, contentType)) {
|
||||
bytes = null;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private boolean isValidIconResponse(byte[] content, String contentType) {
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long length = content.length;
|
||||
|
||||
if (StringUtils.isNotBlank(contentType)) {
|
||||
contentType = contentType.split(";")[0];
|
||||
}
|
||||
|
||||
if (ICON_MIMETYPE_BLACKLIST.contains(contentType)) {
|
||||
log.debug("Content-Type {} is blacklisted", contentType);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length < MIN_ICON_LENGTH) {
|
||||
log.debug("Length {} below MIN_ICON_LENGTH {}", length, MIN_ICON_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length > MAX_ICON_LENGTH) {
|
||||
log.debug("Length {} greater than MAX_ICON_LENGTH {}", length, MAX_ICON_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private byte[] getIconInPage(String url) {
|
||||
|
||||
Document doc = null;
|
||||
try {
|
||||
HttpResult result = getter.getBinary(url, TIMEOUT);
|
||||
doc = Jsoup.parse(new String(result.getContent()), url);
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to retrieve page to find icon");
|
||||
return null;
|
||||
}
|
||||
|
||||
Elements icons = doc.select("link[rel~=(?i)^(shortcut|icon|shortcut icon)$]");
|
||||
|
||||
if (icons.isEmpty()) {
|
||||
log.debug("No icon found in page {}", url);
|
||||
return null;
|
||||
}
|
||||
|
||||
String href = icons.get(0).attr("abs:href");
|
||||
if (StringUtils.isBlank(href)) {
|
||||
log.debug("No icon found in page");
|
||||
return null;
|
||||
}
|
||||
|
||||
log.debug("Found unconfirmed iconInPage at {}", href);
|
||||
|
||||
byte[] bytes = null;
|
||||
String contentType = null;
|
||||
try {
|
||||
HttpResult result = getter.getBinary(href, TIMEOUT);
|
||||
bytes = result.getContent();
|
||||
contentType = result.getContentType();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to retrieve icon found in page {}", href);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isValidIconResponse(bytes, contentType)) {
|
||||
log.debug("Invalid icon found for {}", href);
|
||||
return null;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
}
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.feed.FeedUtils;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
/**
|
||||
* Inspired/Ported from https://github.com/potatolondon/getfavicon
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
|
||||
@Singleton
|
||||
public class DefaultFaviconFetcher extends AbstractFaviconFetcher {
|
||||
|
||||
private final HttpGetter getter;
|
||||
|
||||
@Override
|
||||
public Favicon fetch(Feed feed) {
|
||||
Favicon icon = fetch(feed.getLink());
|
||||
if (icon == null) {
|
||||
icon = fetch(feed.getUrl());
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
private Favicon fetch(String url) {
|
||||
if (url == null) {
|
||||
log.debug("url is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
int doubleSlash = url.indexOf("//");
|
||||
if (doubleSlash == -1) {
|
||||
doubleSlash = 0;
|
||||
} else {
|
||||
doubleSlash += 2;
|
||||
}
|
||||
int firstSlash = url.indexOf('/', doubleSlash);
|
||||
if (firstSlash != -1) {
|
||||
url = url.substring(0, firstSlash);
|
||||
}
|
||||
|
||||
Favicon icon = getIconAtRoot(url);
|
||||
|
||||
if (icon == null) {
|
||||
icon = getIconInPage(url);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private Favicon getIconAtRoot(String url) {
|
||||
byte[] bytes = null;
|
||||
String contentType = null;
|
||||
|
||||
try {
|
||||
url = FeedUtils.removeTrailingSlash(url) + "/favicon.ico";
|
||||
log.debug("getting root icon at {}", url);
|
||||
HttpResult result = getter.getBinary(url, TIMEOUT);
|
||||
bytes = result.getContent();
|
||||
contentType = result.getContentType();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to retrieve iconAtRoot for url {}: ", url);
|
||||
log.trace("Failed to retrieve iconAtRoot for url {}: ", url, e);
|
||||
}
|
||||
|
||||
if (!isValidIconResponse(bytes, contentType)) {
|
||||
return null;
|
||||
}
|
||||
return new Favicon(bytes, contentType);
|
||||
}
|
||||
|
||||
private Favicon getIconInPage(String url) {
|
||||
|
||||
Document doc = null;
|
||||
try {
|
||||
HttpResult result = getter.getBinary(url, TIMEOUT);
|
||||
doc = Jsoup.parse(new String(result.getContent()), url);
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to retrieve page to find icon");
|
||||
log.trace("Failed to retrieve page to find icon", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
Elements icons = doc.select("link[rel~=(?i)^(shortcut|icon|shortcut icon)$]");
|
||||
|
||||
if (icons.isEmpty()) {
|
||||
log.debug("No icon found in page {}", url);
|
||||
return null;
|
||||
}
|
||||
|
||||
String href = icons.get(0).attr("abs:href");
|
||||
if (StringUtils.isBlank(href)) {
|
||||
log.debug("No icon found in page");
|
||||
return null;
|
||||
}
|
||||
|
||||
log.debug("Found unconfirmed iconInPage at {}", href);
|
||||
|
||||
byte[] bytes = null;
|
||||
String contentType = null;
|
||||
try {
|
||||
HttpResult result = getter.getBinary(href, TIMEOUT);
|
||||
bytes = result.getContent();
|
||||
contentType = result.getContentType();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to retrieve icon found in page {}", href);
|
||||
log.trace("Failed to retrieve icon found in page {}", href, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isValidIconResponse(bytes, contentType)) {
|
||||
log.debug("Invalid icon found for {}", href);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Favicon(bytes, contentType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
|
||||
@Singleton
|
||||
public class FacebookFaviconFetcher extends AbstractFaviconFetcher {
|
||||
|
||||
private final HttpGetter getter;
|
||||
|
||||
@Override
|
||||
public Favicon fetch(Feed feed) {
|
||||
String url = feed.getUrl();
|
||||
|
||||
if (!url.toLowerCase().contains("www.facebook.com")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String userName = extractUserName(url);
|
||||
if (userName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String iconUrl = String.format("https://graph.facebook.com/%s/picture?type=square&height=16", userName);
|
||||
|
||||
byte[] bytes = null;
|
||||
String contentType = null;
|
||||
|
||||
try {
|
||||
log.debug("Getting Facebook user's icon, {}", url);
|
||||
|
||||
HttpResult iconResult = getter.getBinary(iconUrl, TIMEOUT);
|
||||
bytes = iconResult.getContent();
|
||||
contentType = iconResult.getContentType();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to retrieve Facebook icon", e);
|
||||
}
|
||||
|
||||
if (!isValidIconResponse(bytes, contentType)) {
|
||||
return null;
|
||||
}
|
||||
return new Favicon(bytes, contentType);
|
||||
}
|
||||
|
||||
private String extractUserName(String url) {
|
||||
URI uri = null;
|
||||
try {
|
||||
uri = new URI(url);
|
||||
} catch (URISyntaxException e) {
|
||||
log.debug("could not parse url", e);
|
||||
return null;
|
||||
}
|
||||
List<NameValuePair> params = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8.name());
|
||||
for (NameValuePair param : params) {
|
||||
if ("id".equals(param.getName())) {
|
||||
return param.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.HttpRequestInitializer;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||
import com.google.api.services.youtube.YouTube;
|
||||
import com.google.api.services.youtube.model.Channel;
|
||||
import com.google.api.services.youtube.model.ChannelListResponse;
|
||||
import com.google.api.services.youtube.model.Thumbnail;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
|
||||
@Singleton
|
||||
public class YoutubeFaviconFetcher extends AbstractFaviconFetcher {
|
||||
|
||||
private final HttpGetter getter;
|
||||
private final CommaFeedConfiguration config;
|
||||
|
||||
@Override
|
||||
public Favicon fetch(Feed feed) {
|
||||
String url = feed.getUrl();
|
||||
|
||||
if (!url.toLowerCase().contains("youtube.com/feeds/videos.xml")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String googleAuthKey = config.getApplicationSettings().getGoogleAuthKey();
|
||||
if (googleAuthKey == null) {
|
||||
log.debug("no google auth key configured");
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bytes = null;
|
||||
String contentType = null;
|
||||
try {
|
||||
List<NameValuePair> params = URLEncodedUtils.parse(url.substring(url.indexOf("?") + 1), StandardCharsets.UTF_8);
|
||||
Optional<NameValuePair> userId = params.stream().filter(nvp -> nvp.getName().equalsIgnoreCase("user")).findFirst();
|
||||
Optional<NameValuePair> channelId = params.stream().filter(nvp -> nvp.getName().equalsIgnoreCase("channel")).findFirst();
|
||||
System.out.println(userId.isPresent());
|
||||
if (!userId.isPresent() && !channelId.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
YouTube youtube = new YouTube.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(),
|
||||
new HttpRequestInitializer() {
|
||||
@Override
|
||||
public void initialize(HttpRequest request) throws IOException {
|
||||
}
|
||||
}).setApplicationName("CommaFeed").build();
|
||||
|
||||
YouTube.Channels.List list = youtube.channels().list("snippet");
|
||||
list.setKey(googleAuthKey);
|
||||
if (userId.isPresent()) {
|
||||
list.setForUsername(userId.get().getValue());
|
||||
} else {
|
||||
list.setId(channelId.get().getValue());
|
||||
}
|
||||
|
||||
log.debug("contacting youtube api");
|
||||
ChannelListResponse response = list.execute();
|
||||
if (response.getItems().isEmpty()) {
|
||||
log.debug("youtube api returned no items");
|
||||
return null;
|
||||
}
|
||||
|
||||
Channel channel = response.getItems().get(0);
|
||||
Thumbnail thumbnail = channel.getSnippet().getThumbnails().getDefault();
|
||||
|
||||
log.debug("fetching favicon");
|
||||
HttpResult iconResult = getter.getBinary(thumbnail.getUrl(), TIMEOUT);
|
||||
bytes = iconResult.getContent();
|
||||
contentType = iconResult.getContentType();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to retrieve YouTube icon", e);
|
||||
}
|
||||
|
||||
if (!isValidIconResponse(bytes, contentType)) {
|
||||
return null;
|
||||
}
|
||||
return new Favicon(bytes, contentType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This code is copied and simplified from GWT
|
||||
* https://github.com/google-web-toolkit/gwt/blob/master/user/src/com/google/gwt/i18n/shared/BidiUtils.java Released under Apache 2.0
|
||||
* license, credit of it goes to Google and please use GWT wherever possible instead of this
|
||||
*/
|
||||
class EstimateDirection {
|
||||
private static final float RTL_DETECTION_THRESHOLD = 0.40f;
|
||||
|
||||
private static final String LTR_CHARS = "A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF"
|
||||
+ "\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF";
|
||||
private static final String RTL_CHARS = "\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC";
|
||||
|
||||
private static final Pattern WORD_SEPARATOR_RE = Pattern.compile("\\s+");
|
||||
private static final Pattern FIRST_STRONG_IS_RTL_RE = Pattern.compile("^[^" + LTR_CHARS + "]*[" + RTL_CHARS + ']');
|
||||
private static final Pattern IS_REQUIRED_LTR_RE = Pattern.compile("^http://.*");
|
||||
private static final Pattern HAS_ANY_LTR_RE = Pattern.compile("[" + LTR_CHARS + ']');
|
||||
|
||||
private static boolean startsWithRtl(String str) {
|
||||
return FIRST_STRONG_IS_RTL_RE.matcher(str).matches();
|
||||
}
|
||||
|
||||
private static boolean hasAnyLtr(String str) {
|
||||
return HAS_ANY_LTR_RE.matcher(str).matches();
|
||||
}
|
||||
|
||||
static boolean isRTL(String str) {
|
||||
int rtlCount = 0;
|
||||
int total = 0;
|
||||
String[] tokens = WORD_SEPARATOR_RE.split(str, 20); // limit splits to 20, usually enough
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
String token = tokens[i];
|
||||
if (startsWithRtl(token)) {
|
||||
rtlCount++;
|
||||
total++;
|
||||
} else if (IS_REQUIRED_LTR_RE.matcher(token).matches()) {
|
||||
// do nothing
|
||||
} else if (hasAnyLtr(token)) {
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
return total == 0 ? false : ((float) rtlCount / total > RTL_DETECTION_THRESHOLD ? true : false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* A keyword used in a search query
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class FeedEntryKeyword {
|
||||
|
||||
public static enum Mode {
|
||||
INCLUDE, EXCLUDE;
|
||||
}
|
||||
|
||||
private final String keyword;
|
||||
private final Mode mode;
|
||||
|
||||
public static List<FeedEntryKeyword> fromQueryString(String keywords) {
|
||||
List<FeedEntryKeyword> list = new ArrayList<>();
|
||||
if (keywords != null) {
|
||||
for (String keyword : StringUtils.split(keywords)) {
|
||||
boolean not = false;
|
||||
if (keyword.startsWith("-") || keyword.startsWith("!")) {
|
||||
not = true;
|
||||
keyword = keyword.substring(1);
|
||||
}
|
||||
list.add(new FeedEntryKeyword(keyword, not ? Mode.EXCLUDE : Mode.INCLUDE));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,10 @@ package com.commafeed.backend.feed;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.binary.StringUtils;
|
||||
@@ -17,10 +20,11 @@ import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.HttpGetter.NotModifiedException;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.sun.syndication.io.FeedException;
|
||||
import com.rometools.rome.io.FeedException;
|
||||
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
|
||||
@Singleton
|
||||
public class FeedFetcher {
|
||||
|
||||
private final FeedParser parser;
|
||||
@@ -37,16 +41,16 @@ public class FeedFetcher {
|
||||
byte[] content = result.getContent();
|
||||
|
||||
try {
|
||||
fetchedFeed = parser.parse(feedUrl, content);
|
||||
fetchedFeed = parser.parse(result.getUrlAfterRedirect(), content);
|
||||
} catch (FeedException e) {
|
||||
if (extractFeedUrlFromHtml) {
|
||||
String extractedUrl = extractFeedUrl(StringUtils.newStringUtf8(result.getContent()), feedUrl);
|
||||
if (org.apache.commons.lang.StringUtils.isNotBlank(extractedUrl)) {
|
||||
if (org.apache.commons.lang3.StringUtils.isNotBlank(extractedUrl)) {
|
||||
feedUrl = extractedUrl;
|
||||
|
||||
result = getter.getBinary(extractedUrl, lastModified, eTag, timeout);
|
||||
content = result.getContent();
|
||||
fetchedFeed = parser.parse(feedUrl, content);
|
||||
fetchedFeed = parser.parse(result.getUrlAfterRedirect(), content);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
@@ -73,7 +77,7 @@ public class FeedFetcher {
|
||||
|
||||
Feed feed = fetchedFeed.getFeed();
|
||||
feed.setLastModifiedHeader(result.getLastModifiedSince());
|
||||
feed.setEtagHeader(FeedUtils.truncate(result.geteTag(), 255));
|
||||
feed.setEtagHeader(FeedUtils.truncate(result.getETag(), 255));
|
||||
feed.setLastContentHash(hash);
|
||||
fetchedFeed.setFetchDuration(result.getDuration());
|
||||
fetchedFeed.setUrlAfterRedirect(result.getUrlAfterRedirect());
|
||||
@@ -89,9 +93,9 @@ public class FeedFetcher {
|
||||
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").toString();
|
||||
foundUrl = atom.get(0).attr("abs:href");
|
||||
} else if (!rss.isEmpty()) {
|
||||
foundUrl = rss.get(0).attr("abs:href").toString();
|
||||
foundUrl = rss.get(0).attr("abs:href");
|
||||
}
|
||||
}
|
||||
return foundUrl;
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.Namespace;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jdom2.Element;
|
||||
import org.jdom2.Namespace;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.sun.syndication.feed.synd.SyndContent;
|
||||
import com.sun.syndication.feed.synd.SyndEnclosure;
|
||||
import com.sun.syndication.feed.synd.SyndEntry;
|
||||
import com.sun.syndication.feed.synd.SyndFeed;
|
||||
import com.sun.syndication.feed.synd.SyndLink;
|
||||
import com.sun.syndication.feed.synd.SyndLinkImpl;
|
||||
import com.sun.syndication.io.FeedException;
|
||||
import com.sun.syndication.io.SyndFeedInput;
|
||||
import com.rometools.rome.feed.synd.SyndEnclosure;
|
||||
import com.rometools.rome.feed.synd.SyndEntry;
|
||||
import com.rometools.rome.feed.synd.SyndFeed;
|
||||
import com.rometools.rome.feed.synd.SyndLink;
|
||||
import com.rometools.rome.feed.synd.SyndLinkImpl;
|
||||
import com.rometools.rome.io.FeedException;
|
||||
import com.rometools.rome.io.SyndFeedInput;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
|
||||
@Singleton
|
||||
public class FeedParser {
|
||||
|
||||
private static final String ATOM_10_URI = "http://www.w3.org/2005/Atom";
|
||||
@@ -37,20 +41,13 @@ public class FeedParser {
|
||||
private static final Date START = new Date(86400000);
|
||||
private static final Date END = new Date(1000l * Integer.MAX_VALUE - 86400000);
|
||||
|
||||
private static final Function<SyndContent, String> CONTENT_TO_STRING = new Function<SyndContent, String>() {
|
||||
public String apply(SyndContent content) {
|
||||
return content.getValue();
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public FetchedFeed parse(String feedUrl, byte[] xml) throws FeedException {
|
||||
FetchedFeed fetchedFeed = new FetchedFeed();
|
||||
Feed feed = fetchedFeed.getFeed();
|
||||
List<FeedEntry> entries = fetchedFeed.getEntries();
|
||||
|
||||
try {
|
||||
String encoding = FeedUtils.guessEncoding(xml);
|
||||
Charset encoding = FeedUtils.guessEncoding(xml);
|
||||
String xmlString = FeedUtils.trimInvalidXmlCharacters(new String(xml, encoding));
|
||||
if (xmlString == null) {
|
||||
throw new FeedException("Input string is null for url " + feedUrl);
|
||||
@@ -80,8 +77,8 @@ public class FeedParser {
|
||||
}
|
||||
entry.setGuid(FeedUtils.truncate(guid, 2048));
|
||||
entry.setUpdated(validateDate(getEntryUpdateDate(item), true));
|
||||
entry.setUrl(FeedUtils.truncate(FeedUtils.toAbsoluteUrl(item.getLink(), feed.getLink(), feed.getUrlAfterRedirect()), 2048));
|
||||
|
||||
entry.setUrl(FeedUtils.truncate(FeedUtils.toAbsoluteUrl(item.getLink(), feed.getLink(), feedUrl), 2048));
|
||||
|
||||
// if link is empty but guid is used as url
|
||||
if (StringUtils.isBlank(entry.getUrl()) && StringUtils.startsWith(entry.getGuid(), "http")) {
|
||||
entry.setUrl(entry.getGuid());
|
||||
@@ -89,9 +86,11 @@ public class FeedParser {
|
||||
|
||||
FeedEntryContent content = new FeedEntryContent();
|
||||
content.setContent(getContent(item));
|
||||
content.setCategories(FeedUtils.truncate(
|
||||
item.getCategories().stream().map(c -> c.getName()).collect(Collectors.joining(", ")), 4096));
|
||||
content.setTitle(getTitle(item));
|
||||
content.setAuthor(StringUtils.trimToNull(item.getAuthor()));
|
||||
SyndEnclosure enclosure = (SyndEnclosure) Iterables.getFirst(item.getEnclosures(), null);
|
||||
SyndEnclosure enclosure = Iterables.getFirst(item.getEnclosures(), null);
|
||||
if (enclosure != null) {
|
||||
content.setEnclosureUrl(FeedUtils.truncate(enclosure.getUrl(), 2048));
|
||||
content.setEnclosureType(enclosure.getType());
|
||||
@@ -121,24 +120,17 @@ public class FeedParser {
|
||||
/**
|
||||
* Adds atom links for rss feeds
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void handleForeignMarkup(SyndFeed feed) {
|
||||
Object foreignMarkup = feed.getForeignMarkup();
|
||||
List<Element> foreignMarkup = feed.getForeignMarkup();
|
||||
if (foreignMarkup == null) {
|
||||
return;
|
||||
}
|
||||
if (foreignMarkup instanceof List) {
|
||||
List elements = (List) foreignMarkup;
|
||||
for (Object object : elements) {
|
||||
if (object instanceof Element) {
|
||||
Element element = (Element) object;
|
||||
if ("link".equals(element.getName()) && ATOM_10_NS.equals(element.getNamespace())) {
|
||||
SyndLink link = new SyndLinkImpl();
|
||||
link.setRel(element.getAttributeValue("rel"));
|
||||
link.setHref(element.getAttributeValue("href"));
|
||||
feed.getLinks().add(link);
|
||||
}
|
||||
}
|
||||
for (Element element : foreignMarkup) {
|
||||
if ("link".equals(element.getName()) && ATOM_10_NS.equals(element.getNamespace())) {
|
||||
SyndLink link = new SyndLinkImpl();
|
||||
link.setRel(element.getAttributeValue("rel"));
|
||||
link.setHref(element.getAttributeValue("href"));
|
||||
feed.getLinks().add(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,13 +161,12 @@ public class FeedParser {
|
||||
return date;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String getContent(SyndEntry item) {
|
||||
String content = null;
|
||||
if (item.getContents().isEmpty()) {
|
||||
content = item.getDescription() == null ? null : item.getDescription().getValue();
|
||||
} else {
|
||||
content = StringUtils.join(Collections2.transform(item.getContents(), CONTENT_TO_STRING), SystemUtils.LINE_SEPARATOR);
|
||||
content = item.getContents().stream().map(c -> c.getValue()).collect(Collectors.joining(System.lineSeparator()));
|
||||
}
|
||||
return StringUtils.trimToNull(content);
|
||||
}
|
||||
@@ -193,9 +184,8 @@ public class FeedParser {
|
||||
return StringUtils.trimToNull(title);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String findHub(SyndFeed feed) {
|
||||
for (SyndLink l : (List<SyndLink>) feed.getLinks()) {
|
||||
for (SyndLink l : feed.getLinks()) {
|
||||
if ("hub".equalsIgnoreCase(l.getRel())) {
|
||||
log.debug("found hub {} for feed {}", l.getHref(), feed.getLink());
|
||||
return l.getHref();
|
||||
@@ -204,9 +194,8 @@ public class FeedParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String findSelf(SyndFeed feed) {
|
||||
for (SyndLink l : (List<SyndLink>) feed.getLinks()) {
|
||||
for (SyndLink l : feed.getLinks()) {
|
||||
if ("self".equalsIgnoreCase(l.getRel())) {
|
||||
log.debug("found self {} for feed {}", l.getHref(), feed.getLink());
|
||||
return l.getHref();
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.codahale.metrics.Gauge;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.dao.FeedDAO;
|
||||
import com.commafeed.backend.dao.UnitOfWork;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Queues;
|
||||
|
||||
@Singleton
|
||||
public class FeedQueues {
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
private final FeedDAO feedDAO;
|
||||
private final CommaFeedConfiguration config;
|
||||
|
||||
private Queue<FeedRefreshContext> addQueue = Queues.newConcurrentLinkedQueue();
|
||||
private Queue<FeedRefreshContext> takeQueue = Queues.newConcurrentLinkedQueue();
|
||||
private Queue<Feed> giveBackQueue = Queues.newConcurrentLinkedQueue();
|
||||
private Queue<FeedRefreshContext> addQueue = new ConcurrentLinkedQueue<>();
|
||||
private Queue<FeedRefreshContext> takeQueue = new ConcurrentLinkedQueue<>();
|
||||
private Queue<Feed> giveBackQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private Meter refill;
|
||||
|
||||
@Inject
|
||||
public FeedQueues(FeedDAO feedDAO, CommaFeedConfiguration config, MetricRegistry metrics) {
|
||||
public FeedQueues(SessionFactory sessionFactory, FeedDAO feedDAO, CommaFeedConfiguration config, MetricRegistry metrics) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.config = config;
|
||||
this.feedDAO = feedDAO;
|
||||
|
||||
@@ -76,7 +83,10 @@ public class FeedQueues {
|
||||
public void add(Feed feed, boolean urgent) {
|
||||
int refreshInterval = config.getApplicationSettings().getRefreshIntervalMinutes();
|
||||
if (feed.getLastUpdated() == null || feed.getLastUpdated().before(DateUtils.addMinutes(new Date(), -1 * refreshInterval))) {
|
||||
addQueue.add(new FeedRefreshContext(feed, urgent));
|
||||
boolean alreadyQueued = addQueue.stream().anyMatch(c -> c.getFeed().getId().equals(feed.getId()));
|
||||
if (!alreadyQueued) {
|
||||
addQueue.add(new FeedRefreshContext(feed, urgent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +96,7 @@ public class FeedQueues {
|
||||
private void refill() {
|
||||
refill.mark();
|
||||
|
||||
List<FeedRefreshContext> contexts = Lists.newArrayList();
|
||||
List<FeedRefreshContext> contexts = new ArrayList<>();
|
||||
int batchSize = Math.min(100, 3 * config.getApplicationSettings().getBackgroundThreads());
|
||||
|
||||
// add feeds we got from the add() method
|
||||
@@ -98,7 +108,7 @@ public class FeedQueues {
|
||||
// add feeds that are up to refresh from the database
|
||||
int count = batchSize - contexts.size();
|
||||
if (count > 0) {
|
||||
List<Feed> feeds = feedDAO.findNextUpdatable(count, getLastLoginThreshold());
|
||||
List<Feed> feeds = UnitOfWork.run(sessionFactory, () -> feedDAO.findNextUpdatable(count, getLastLoginThreshold()));
|
||||
for (Feed feed : feeds) {
|
||||
contexts.add(new FeedRefreshContext(feed, false));
|
||||
}
|
||||
@@ -106,7 +116,7 @@ public class FeedQueues {
|
||||
|
||||
// set the disabledDate as we use it in feedDAO to decide what to refresh next. We also use a map to remove
|
||||
// duplicates.
|
||||
Map<Long, FeedRefreshContext> map = Maps.newLinkedHashMap();
|
||||
Map<Long, FeedRefreshContext> map = new LinkedHashMap<>();
|
||||
for (FeedRefreshContext context : contexts) {
|
||||
Feed feed = context.getFeed();
|
||||
feed.setDisabledUntil(DateUtils.addMinutes(new Date(), config.getApplicationSettings().getRefreshIntervalMinutes()));
|
||||
@@ -124,11 +134,8 @@ public class FeedQueues {
|
||||
}
|
||||
|
||||
// update all feeds in the database
|
||||
List<Feed> feeds = Lists.newArrayList();
|
||||
for (FeedRefreshContext context : map.values()) {
|
||||
feeds.add(context.getFeed());
|
||||
}
|
||||
feedDAO.merge(feeds);
|
||||
List<Feed> feeds = map.values().stream().map(c -> c.getFeed()).collect(Collectors.toList());
|
||||
UnitOfWork.run(sessionFactory, () -> feedDAO.saveOrUpdate(feeds));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,7 +150,7 @@ public class FeedQueues {
|
||||
}
|
||||
|
||||
private Date getLastLoginThreshold() {
|
||||
if (config.getApplicationSettings().isHeavyLoad()) {
|
||||
if (config.getApplicationSettings().getHeavyLoad()) {
|
||||
return DateUtils.addDays(new Date(), -30);
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -2,9 +2,14 @@ package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class FeedRefreshContext {
|
||||
private Feed feed;
|
||||
private List<FeedEntry> entries;
|
||||
@@ -14,29 +19,4 @@ public class FeedRefreshContext {
|
||||
this.feed = feed;
|
||||
this.urgent = isUrgent;
|
||||
}
|
||||
|
||||
public Feed getFeed() {
|
||||
return feed;
|
||||
}
|
||||
|
||||
public void setFeed(Feed feed) {
|
||||
this.feed = feed;
|
||||
}
|
||||
|
||||
public boolean isUrgent() {
|
||||
return urgent;
|
||||
}
|
||||
|
||||
public void setUrgent(boolean urgent) {
|
||||
this.urgent = urgent;
|
||||
}
|
||||
|
||||
public List<FeedEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public void setEntries(List<FeedEntry> entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,14 @@ public class FeedRefreshExecutor {
|
||||
return offerLast(r);
|
||||
}
|
||||
}
|
||||
});
|
||||
}) {
|
||||
@Override
|
||||
protected void afterExecute(Runnable r, Throwable t) {
|
||||
if (t != null) {
|
||||
log.error("thread from pool {} threw a runtime exception", poolName, t);
|
||||
}
|
||||
};
|
||||
};
|
||||
pool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
|
||||
|
||||
@@ -6,25 +6,23 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.dao.FeedDAO;
|
||||
import com.commafeed.backend.dao.UnitOfWork;
|
||||
|
||||
/**
|
||||
* Infinite loop fetching feeds from @FeedQueues and queuing them to the {@link FeedRefreshWorker} pool.
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public class FeedRefreshTaskGiver implements Managed {
|
||||
|
||||
private final SessionFactory sessionFactory;
|
||||
private final FeedQueues queues;
|
||||
private final FeedRefreshWorker worker;
|
||||
|
||||
@@ -34,9 +32,8 @@ public class FeedRefreshTaskGiver implements Managed {
|
||||
private Meter threadWaited;
|
||||
|
||||
@Inject
|
||||
public FeedRefreshTaskGiver(SessionFactory sessionFactory, FeedQueues queues, FeedDAO feedDAO, FeedRefreshWorker worker,
|
||||
CommaFeedConfiguration config, MetricRegistry metrics) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
public FeedRefreshTaskGiver(FeedQueues queues, FeedDAO feedDAO, FeedRefreshWorker worker, CommaFeedConfiguration config,
|
||||
MetricRegistry metrics) {
|
||||
this.queues = queues;
|
||||
this.worker = worker;
|
||||
|
||||
@@ -66,18 +63,11 @@ public class FeedRefreshTaskGiver implements Managed {
|
||||
public void run() {
|
||||
while (!executor.isShutdown()) {
|
||||
try {
|
||||
FeedRefreshContext context = new UnitOfWork<FeedRefreshContext>(sessionFactory) {
|
||||
@Override
|
||||
protected FeedRefreshContext runInSession() throws Exception {
|
||||
FeedRefreshContext context = queues.take();
|
||||
if (context != null) {
|
||||
feedRefreshed.mark();
|
||||
worker.updateFeed(context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}.run();
|
||||
if (context == null) {
|
||||
FeedRefreshContext context = queues.take();
|
||||
if (context != null) {
|
||||
feedRefreshed.mark();
|
||||
worker.updateFeed(context);
|
||||
} else {
|
||||
log.debug("nothing to do, sleeping for 15s");
|
||||
threadWaited.mark();
|
||||
try {
|
||||
|
||||
@@ -2,19 +2,24 @@ package com.commafeed.backend.feed;
|
||||
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.codahale.metrics.Meter;
|
||||
@@ -32,10 +37,10 @@ import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.service.FeedUpdateService;
|
||||
import com.commafeed.backend.service.PubSubService;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.Striped;
|
||||
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public class FeedRefreshUpdater implements Managed {
|
||||
|
||||
private final SessionFactory sessionFactory;
|
||||
@@ -54,6 +59,7 @@ public class FeedRefreshUpdater implements Managed {
|
||||
private Meter feedUpdated;
|
||||
private Meter entryInserted;
|
||||
|
||||
@Inject
|
||||
public FeedRefreshUpdater(SessionFactory sessionFactory, FeedUpdateService feedUpdateService, PubSubService pubSubService,
|
||||
FeedQueues queues, CommaFeedConfiguration config, MetricRegistry metrics, FeedSubscriptionDAO feedSubscriptionDAO,
|
||||
CacheService cache) {
|
||||
@@ -100,24 +106,14 @@ public class FeedRefreshUpdater implements Managed {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
new UnitOfWork<Void>(sessionFactory) {
|
||||
@Override
|
||||
protected Void runInSession() throws Exception {
|
||||
internalRun();
|
||||
return null;
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
public void internalRun() {
|
||||
boolean ok = true;
|
||||
Feed feed = context.getFeed();
|
||||
final Feed feed = context.getFeed();
|
||||
List<FeedEntry> entries = context.getEntries();
|
||||
if (entries.isEmpty()) {
|
||||
feed.setMessage("Feed has no entries");
|
||||
} else {
|
||||
List<String> lastEntries = cache.getLastEntries(feed);
|
||||
List<String> currentEntries = Lists.newArrayList();
|
||||
List<String> currentEntries = new ArrayList<>();
|
||||
|
||||
List<FeedSubscription> subscriptions = null;
|
||||
for (FeedEntry entry : entries) {
|
||||
@@ -125,7 +121,7 @@ public class FeedRefreshUpdater implements Managed {
|
||||
if (!lastEntries.contains(cacheKey)) {
|
||||
log.debug("cache miss for {}", entry.getUrl());
|
||||
if (subscriptions == null) {
|
||||
subscriptions = feedSubscriptionDAO.findByFeed(feed);
|
||||
subscriptions = UnitOfWork.run(sessionFactory, () -> feedSubscriptionDAO.findByFeed(feed));
|
||||
}
|
||||
ok &= addEntry(feed, entry, subscriptions);
|
||||
entryCacheMiss.mark();
|
||||
@@ -143,16 +139,13 @@ public class FeedRefreshUpdater implements Managed {
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(subscriptions)) {
|
||||
List<User> users = Lists.newArrayList();
|
||||
for (FeedSubscription sub : subscriptions) {
|
||||
users.add(sub.getUser());
|
||||
}
|
||||
List<User> users = subscriptions.stream().map(s -> s.getUser()).collect(Collectors.toList());
|
||||
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
|
||||
cache.invalidateUserRootCategory(users.toArray(new User[0]));
|
||||
}
|
||||
}
|
||||
|
||||
if (config.getApplicationSettings().isPubsubhubbub()) {
|
||||
if (config.getApplicationSettings().getPubsubhubbub()) {
|
||||
handlePubSub(feed);
|
||||
}
|
||||
if (!ok) {
|
||||
@@ -190,7 +183,7 @@ public class FeedRefreshUpdater implements Managed {
|
||||
locked1 = lock1.tryLock(1, TimeUnit.MINUTES);
|
||||
locked2 = lock2.tryLock(1, TimeUnit.MINUTES);
|
||||
if (locked1 && locked2) {
|
||||
boolean inserted = feedUpdateService.addEntry(feed, entry);
|
||||
boolean inserted = UnitOfWork.run(sessionFactory, () -> feedUpdateService.addEntry(feed, entry, subscriptions));
|
||||
if (inserted) {
|
||||
entryInserted.mark();
|
||||
}
|
||||
|
||||
@@ -4,12 +4,17 @@ import io.dropwizard.lifecycle.Managed;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
@@ -17,13 +22,13 @@ import com.commafeed.backend.HttpGetter.NotModifiedException;
|
||||
import com.commafeed.backend.feed.FeedRefreshExecutor.Task;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
/**
|
||||
* Calls {@link FeedFetcher} and handles its outcome
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public class FeedRefreshWorker implements Managed {
|
||||
|
||||
private final FeedRefreshUpdater feedRefreshUpdater;
|
||||
@@ -32,6 +37,7 @@ public class FeedRefreshWorker implements Managed {
|
||||
private final CommaFeedConfiguration config;
|
||||
private final FeedRefreshExecutor pool;
|
||||
|
||||
@Inject
|
||||
public FeedRefreshWorker(FeedRefreshUpdater feedRefreshUpdater, FeedFetcher fetcher, FeedQueues queues, CommaFeedConfiguration config,
|
||||
MetricRegistry metrics) {
|
||||
this.feedRefreshUpdater = feedRefreshUpdater;
|
||||
@@ -79,13 +85,18 @@ public class FeedRefreshWorker implements Managed {
|
||||
int refreshInterval = config.getApplicationSettings().getRefreshIntervalMinutes();
|
||||
Date disabledUntil = DateUtils.addMinutes(new Date(), refreshInterval);
|
||||
try {
|
||||
String url = Optional.fromNullable(feed.getUrlAfterRedirect()).or(feed.getUrl());
|
||||
String url = Optional.ofNullable(feed.getUrlAfterRedirect()).orElse(feed.getUrl());
|
||||
FetchedFeed fetchedFeed = fetcher.fetch(url, false, feed.getLastModifiedHeader(), feed.getEtagHeader(),
|
||||
feed.getLastPublishedDate(), feed.getLastContentHash());
|
||||
// stops here if NotModifiedException or any other exception is thrown
|
||||
List<FeedEntry> entries = fetchedFeed.getEntries();
|
||||
|
||||
if (config.getApplicationSettings().isHeavyLoad()) {
|
||||
Integer maxFeedCapacity = config.getApplicationSettings().getMaxFeedCapacity();
|
||||
if (maxFeedCapacity > 0) {
|
||||
entries = entries.stream().limit(maxFeedCapacity).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (config.getApplicationSettings().getHeavyLoad()) {
|
||||
disabledUntil = FeedUtils.buildDisabledUntil(fetchedFeed.getFeed().getLastEntryDate(), fetchedFeed.getFeed()
|
||||
.getAverageEntryInterval(), disabledUntil);
|
||||
}
|
||||
@@ -113,7 +124,7 @@ public class FeedRefreshWorker implements Managed {
|
||||
} catch (NotModifiedException e) {
|
||||
log.debug("Feed not modified : {} - {}", feed.getUrl(), e.getMessage());
|
||||
|
||||
if (config.getApplicationSettings().isHeavyLoad()) {
|
||||
if (config.getApplicationSettings().getHeavyLoad()) {
|
||||
disabledUntil = FeedUtils.buildDisabledUntil(feed.getLastEntryDate(), feed.getAverageEntryInterval(), disabledUntil);
|
||||
}
|
||||
feed.setErrorCount(0);
|
||||
|
||||
@@ -3,20 +3,23 @@ package com.commafeed.backend.feed;
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Document.OutputSettings;
|
||||
@@ -25,16 +28,15 @@ import org.jsoup.nodes.Entities.EscapeMode;
|
||||
import org.jsoup.safety.Cleaner;
|
||||
import org.jsoup.safety.Whitelist;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.mozilla.universalchardet.UniversalDetector;
|
||||
import org.w3c.css.sac.InputSource;
|
||||
import org.w3c.dom.css.CSSStyleDeclaration;
|
||||
|
||||
import com.commafeed.backend.feed.FeedEntryKeyword.Mode;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.frontend.model.Entry;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gwt.i18n.client.HasDirection.Direction;
|
||||
import com.google.gwt.i18n.shared.BidiUtils;
|
||||
import com.ibm.icu.text.CharsetDetector;
|
||||
import com.ibm.icu.text.CharsetMatch;
|
||||
import com.steadystate.css.parser.CSSOMParser;
|
||||
|
||||
import edu.uci.ics.crawler4j.url.URLCanonicalizer;
|
||||
@@ -99,14 +101,14 @@ public class FeedUtils {
|
||||
* feed
|
||||
*
|
||||
*/
|
||||
public static String guessEncoding(byte[] bytes) {
|
||||
public static Charset guessEncoding(byte[] bytes) {
|
||||
String extracted = extractDeclaredEncoding(bytes);
|
||||
if (StringUtils.startsWithIgnoreCase(extracted, "iso-8859-")) {
|
||||
if (StringUtils.endsWith(extracted, "1") == false) {
|
||||
return extracted;
|
||||
return Charset.forName(extracted);
|
||||
}
|
||||
} else if (StringUtils.startsWithIgnoreCase(extracted, "windows-")) {
|
||||
return extracted;
|
||||
return Charset.forName(extracted);
|
||||
}
|
||||
return detectEncoding(bytes);
|
||||
}
|
||||
@@ -114,27 +116,23 @@ public class FeedUtils {
|
||||
/**
|
||||
* Detect encoding by analyzing characters in the array
|
||||
*/
|
||||
public static String detectEncoding(byte[] bytes) {
|
||||
String DEFAULT_ENCODING = "UTF-8";
|
||||
UniversalDetector detector = new UniversalDetector(null);
|
||||
detector.handleData(bytes, 0, bytes.length);
|
||||
detector.dataEnd();
|
||||
String encoding = detector.getDetectedCharset();
|
||||
detector.reset();
|
||||
if (encoding == null) {
|
||||
encoding = DEFAULT_ENCODING;
|
||||
} else if (encoding.equalsIgnoreCase("ISO-8859-1")) {
|
||||
public static Charset detectEncoding(byte[] bytes) {
|
||||
String encoding = "UTF-8";
|
||||
|
||||
CharsetDetector detector = new CharsetDetector();
|
||||
detector.setText(bytes);
|
||||
CharsetMatch match = detector.detect();
|
||||
if (match != null) {
|
||||
encoding = match.getName();
|
||||
}
|
||||
if (encoding.equalsIgnoreCase("ISO-8859-1")) {
|
||||
encoding = "windows-1252";
|
||||
}
|
||||
return encoding;
|
||||
return Charset.forName(encoding);
|
||||
}
|
||||
|
||||
public static String replaceHtmlEntitiesWithNumericEntities(String source) {
|
||||
String result = source;
|
||||
for (String entity : HtmlEntities.NUMERIC_MAPPING.keySet()) {
|
||||
result = result.replace(entity, HtmlEntities.NUMERIC_MAPPING.get(entity));
|
||||
}
|
||||
return result;
|
||||
return StringUtils.replaceEach(source, HtmlEntities.HTML_ENTITIES, HtmlEntities.NUMERIC_ENTITIES);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,7 +182,7 @@ public class FeedUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
String pi = new String(ArrayUtils.subarray(bytes, 0, index + 1));
|
||||
String pi = new String(ArrayUtils.subarray(bytes, 0, index + 1)).replace('\'', '"');
|
||||
index = StringUtils.indexOf(pi, "encoding=\"");
|
||||
if (index == -1) {
|
||||
return null;
|
||||
@@ -229,7 +227,7 @@ public class FeedUtils {
|
||||
String rule = "";
|
||||
CSSOMParser parser = new CSSOMParser();
|
||||
try {
|
||||
List<String> rules = Lists.newArrayList();
|
||||
List<String> rules = new ArrayList<>();
|
||||
CSSStyleDeclaration decl = parser.parseStyleDeclaration(new InputSource(new StringReader(orig)));
|
||||
|
||||
for (int i = 0; i < decl.getLength(); i++) {
|
||||
@@ -254,7 +252,7 @@ public class FeedUtils {
|
||||
String rule = "";
|
||||
CSSOMParser parser = new CSSOMParser();
|
||||
try {
|
||||
List<String> rules = Lists.newArrayList();
|
||||
List<String> rules = new ArrayList<>();
|
||||
CSSStyleDeclaration decl = parser.parseStyleDeclaration(new InputSource(new StringReader(orig)));
|
||||
|
||||
for (int i = 0; i < decl.getLength(); i++) {
|
||||
@@ -291,8 +289,7 @@ public class FeedUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
Direction direction = BidiUtils.get().estimateDirection(text);
|
||||
return direction == Direction.RTL;
|
||||
return EstimateDirection.isRTL(text);
|
||||
}
|
||||
|
||||
public static String trimInvalidXmlCharacters(String xml) {
|
||||
@@ -389,13 +386,7 @@ public class FeedUtils {
|
||||
}
|
||||
|
||||
public static List<Long> getSortedTimestamps(List<FeedEntry> entries) {
|
||||
List<Long> timestamps = Lists.newArrayList();
|
||||
for (FeedEntry entry : entries) {
|
||||
timestamps.add(entry.getUpdated().getTime());
|
||||
}
|
||||
Collections.sort(timestamps);
|
||||
Collections.reverse(timestamps);
|
||||
return timestamps;
|
||||
return entries.stream().map(t -> t.getUpdated().getTime()).sorted(Collections.reverseOrder()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static String removeTrailingSlash(String url) {
|
||||
@@ -439,12 +430,8 @@ public class FeedUtils {
|
||||
}
|
||||
|
||||
public static boolean isRelative(final String url) {
|
||||
// the regex means "doesn't start with 'scheme://'"
|
||||
if ((url != null) && (url.startsWith("/") == false) && (!url.matches("^\\w+\\:\\/\\/.*")) && !(url.startsWith("#"))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// the regex means "start with 'scheme://'"
|
||||
return url.startsWith("/") || url.startsWith("#") || !url.matches("^\\w+\\:\\/\\/.*");
|
||||
}
|
||||
|
||||
public static String getFaviconUrl(FeedSubscription subscription, String publicUrl) {
|
||||
@@ -498,19 +485,20 @@ public class FeedUtils {
|
||||
return rot13(new String(Base64.decodeBase64(code)));
|
||||
}
|
||||
|
||||
public static void removeUnwantedFromSearch(List<Entry> entries, String keywords) {
|
||||
if (StringUtils.isBlank(keywords)) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static void removeUnwantedFromSearch(List<Entry> entries, List<FeedEntryKeyword> keywords) {
|
||||
Iterator<Entry> it = entries.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry entry = it.next();
|
||||
boolean keep = true;
|
||||
for (String keyword : keywords.split(" ")) {
|
||||
for (FeedEntryKeyword keyword : keywords) {
|
||||
String title = Jsoup.parse(entry.getTitle()).text();
|
||||
String content = Jsoup.parse(entry.getContent()).text();
|
||||
if (!StringUtils.containsIgnoreCase(content, keyword) && !StringUtils.containsIgnoreCase(title, keyword)) {
|
||||
boolean condition = !StringUtils.containsIgnoreCase(content, keyword.getKeyword())
|
||||
&& !StringUtils.containsIgnoreCase(title, keyword.getKeyword());
|
||||
if (keyword.getMode() == Mode.EXCLUDE) {
|
||||
condition = !condition;
|
||||
}
|
||||
if (condition) {
|
||||
keep = false;
|
||||
break;
|
||||
}
|
||||
@@ -520,5 +508,4 @@ public class FeedUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,58 +1,23 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class FetchedFeed {
|
||||
|
||||
private Feed feed = new Feed();
|
||||
private List<FeedEntry> entries = Lists.newArrayList();
|
||||
private List<FeedEntry> entries = new ArrayList<>();
|
||||
|
||||
private String title;
|
||||
private String urlAfterRedirect;
|
||||
private long fetchDuration;
|
||||
|
||||
public Feed getFeed() {
|
||||
return feed;
|
||||
}
|
||||
|
||||
public void setFeed(Feed feed) {
|
||||
this.feed = feed;
|
||||
}
|
||||
|
||||
public List<FeedEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public void setEntries(List<FeedEntry> entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public long getFetchDuration() {
|
||||
return fetchDuration;
|
||||
}
|
||||
|
||||
public void setFetchDuration(long fetchDuration) {
|
||||
this.fetchDuration = fetchDuration;
|
||||
}
|
||||
|
||||
public String getUrlAfterRedirect() {
|
||||
return urlAfterRedirect;
|
||||
}
|
||||
|
||||
public void setUrlAfterRedirect(String urlAfterRedirect) {
|
||||
this.urlAfterRedirect = urlAfterRedirect;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gwt.thirdparty.guava.common.collect.Maps;
|
||||
|
||||
public class HtmlEntities {
|
||||
public static final Map<String, String> NUMERIC_MAPPING = Collections.unmodifiableMap(loadMap());
|
||||
public static final String[] HTML_ENTITIES;
|
||||
public static final String[] NUMERIC_ENTITIES;
|
||||
|
||||
private static synchronized Map<String, String> loadMap() {
|
||||
Map<String, String> map = Maps.newLinkedHashMap();
|
||||
static {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("Á", "Á");
|
||||
map.put("á", "á");
|
||||
map.put("Â", "Â");
|
||||
@@ -260,7 +259,8 @@ public class HtmlEntities {
|
||||
map.put("ζ", "ζ");
|
||||
map.put("‍", "‍");
|
||||
map.put("‌", "‌");
|
||||
|
||||
return map;
|
||||
|
||||
HTML_ENTITIES = map.keySet().toArray(new String[map.size()]);
|
||||
NUMERIC_ENTITIES = map.values().toArray(new String[map.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
@@ -103,12 +100,6 @@ public class Feed extends AbstractModel {
|
||||
@Column(length = 40)
|
||||
private String lastContentHash;
|
||||
|
||||
@OneToMany(mappedBy = "feed", cascade = CascadeType.REMOVE)
|
||||
private Set<FeedEntry> entries;
|
||||
|
||||
@OneToMany(mappedBy = "feed")
|
||||
private Set<FeedSubscription> subscriptions;
|
||||
|
||||
/**
|
||||
* detected hub for pubsubhubbub
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,8 @@ import javax.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEEDENTRYCONTENTS")
|
||||
@SuppressWarnings("serial")
|
||||
@@ -26,6 +28,7 @@ public class FeedEntryContent extends AbstractModel {
|
||||
|
||||
@Lob
|
||||
@Column(length = Integer.MAX_VALUE)
|
||||
@Type(type = "org.hibernate.type.StringClobType")
|
||||
private String content;
|
||||
|
||||
@Column(length = 40)
|
||||
@@ -40,6 +43,9 @@ public class FeedEntryContent extends AbstractModel {
|
||||
@Column(length = 255)
|
||||
private String enclosureType;
|
||||
|
||||
@Column(length = 4096)
|
||||
private String categories;
|
||||
|
||||
@OneToMany(mappedBy = "content")
|
||||
private Set<FeedEntry> entries;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@@ -8,8 +9,6 @@ import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
@@ -18,16 +17,11 @@ import javax.persistence.Transient;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEEDENTRYSTATUSES")
|
||||
@SuppressWarnings("serial")
|
||||
@Getter
|
||||
@Setter
|
||||
@NamedQueries(@NamedQuery(
|
||||
name = "Statuses.deleteOld",
|
||||
query = "delete from FeedEntryStatus s where s.entryInserted < :date and s.starred = false"))
|
||||
public class FeedEntryStatus extends AbstractModel {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@@ -46,7 +40,7 @@ public class FeedEntryStatus extends AbstractModel {
|
||||
private boolean markable;
|
||||
|
||||
@Transient
|
||||
private List<FeedEntryTag> tags = Lists.newArrayList();
|
||||
private List<FeedEntryTag> tags = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Denormalization starts here
|
||||
|
||||
@@ -40,4 +40,7 @@ public class FeedSubscription extends AbstractModel {
|
||||
|
||||
private Integer position;
|
||||
|
||||
@Column(length = 4096)
|
||||
private String filter;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
@@ -15,10 +11,7 @@ import javax.persistence.TemporalType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cascade;
|
||||
|
||||
import com.commafeed.backend.model.UserRole.Role;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
@Entity
|
||||
@Table(name = "USERS")
|
||||
@@ -57,25 +50,16 @@ public class User extends AbstractModel {
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date recoverPasswordTokenDate;
|
||||
|
||||
@OneToMany(mappedBy = "user", cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
|
||||
@Cascade({ org.hibernate.annotations.CascadeType.PERSIST, org.hibernate.annotations.CascadeType.SAVE_UPDATE,
|
||||
org.hibernate.annotations.CascadeType.REMOVE })
|
||||
private Set<UserRole> roles = Sets.newHashSet();
|
||||
|
||||
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
|
||||
private Set<FeedSubscription> subscriptions;
|
||||
|
||||
@Column(name = "last_full_refresh")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastFullRefresh;
|
||||
|
||||
public boolean hasRole(Role role) {
|
||||
for (UserRole userRole : getRoles()) {
|
||||
if (userRole.getRole() == role) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
public boolean shouldRefreshFeedsAt(Date when) {
|
||||
return (lastFullRefresh == null || lastFullRefreshMoreThan30MinutesBefore(when));
|
||||
}
|
||||
|
||||
private boolean lastFullRefreshMoreThan30MinutesBefore(Date when) {
|
||||
return lastFullRefresh.before(DateUtils.addMinutes(when, -30));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user