forked from Archives/Athou_commafeed
Compare commits
215 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbcd79e49f | ||
|
|
4dabf47822 | ||
|
|
db258d4ecc | ||
|
|
8b237db690 | ||
|
|
416350c004 | ||
|
|
8d63377e78 | ||
|
|
377176df05 | ||
|
|
95da0078b3 | ||
|
|
6392b87afc | ||
|
|
ba04d2adfe | ||
|
|
517ce1a726 | ||
|
|
36492cbff5 | ||
|
|
4b46aa08ac | ||
|
|
1a9a80c0da | ||
|
|
32a30019a7 | ||
|
|
bb72131354 | ||
|
|
3a8d72cab4 | ||
|
|
f5f7a8e63b | ||
|
|
570c4f3a1f | ||
|
|
172164b74b | ||
|
|
49835ae234 | ||
|
|
c4f1e910f8 | ||
|
|
3a621b61c6 | ||
|
|
c28f0d6788 | ||
|
|
2db9224ffc | ||
|
|
043b1df585 | ||
|
|
0626200787 | ||
|
|
b7ee61a8df | ||
|
|
6e1cdaf50e | ||
|
|
e770f802e7 | ||
|
|
8e4cf77fcb | ||
|
|
bc3bd42ce3 | ||
|
|
f73e0ba307 | ||
|
|
5703b5e8d4 | ||
|
|
cecbb2cf72 | ||
|
|
8638e4751d | ||
|
|
3b69e3b029 | ||
|
|
dced21c8e4 | ||
|
|
dab26af294 | ||
|
|
65f118e561 | ||
|
|
67f533b9f6 | ||
|
|
93573bcdb7 | ||
|
|
2263801c55 | ||
|
|
10c34d0440 | ||
|
|
4430ef3847 | ||
|
|
8e331b908d | ||
|
|
dbc6fb58e0 | ||
|
|
db298ab684 | ||
|
|
170a6095e6 | ||
|
|
6dd1bf3281 | ||
|
|
b1500cebfd | ||
|
|
6202bdbc28 | ||
|
|
39bfb61b95 | ||
|
|
fa79524ed4 | ||
|
|
ab5b70e52b | ||
|
|
4f8cd53b83 | ||
|
|
afb6221e5e | ||
|
|
f78aedc30d | ||
|
|
80ff2c8ff7 | ||
|
|
579a77dfc9 | ||
|
|
f902d967a6 | ||
|
|
0899e0b0bf | ||
|
|
65d6f8616b | ||
|
|
5c27f0834c | ||
|
|
a5f7b56bf2 | ||
|
|
63ec92038c | ||
|
|
464ac36ddb | ||
|
|
840bc2ef7a | ||
|
|
e248504528 | ||
|
|
f4f3d9ca48 | ||
|
|
e727ee414b | ||
|
|
1e9295b386 | ||
|
|
b980cdc2c2 | ||
|
|
fbe722facd | ||
|
|
1897d8e0c0 | ||
|
|
3745a152aa | ||
|
|
a7731acb08 | ||
|
|
16dd5deed4 | ||
|
|
c9f70650a0 | ||
|
|
eaa84253df | ||
|
|
45abcd7385 | ||
|
|
8a633aa648 | ||
|
|
05e092062d | ||
|
|
e83602a05c | ||
|
|
abf8666e24 | ||
|
|
af1ccc6669 | ||
|
|
cdcbfbff68 | ||
|
|
6860940afc | ||
|
|
bfc2ee3663 | ||
|
|
b104622081 | ||
|
|
a861387bd7 | ||
|
|
b0f2260fad | ||
|
|
97f0d98ffd | ||
|
|
1ad58a029c | ||
|
|
4c27da0433 | ||
|
|
faf69b43c3 | ||
|
|
7fff561268 | ||
|
|
5e1360a65b | ||
|
|
cc92d2f546 | ||
|
|
def75a250f | ||
|
|
15cd7caf9b | ||
|
|
41a51530ef | ||
|
|
3a101941b3 | ||
|
|
0976fee4df | ||
|
|
f87da777da | ||
|
|
e1c2bf0890 | ||
|
|
b829defb30 | ||
|
|
fa8770d2a7 | ||
|
|
222c8a65af | ||
|
|
76f5b67ac4 | ||
|
|
1791d49efe | ||
|
|
64e1b5df09 | ||
|
|
e1ff077623 | ||
|
|
1361072558 | ||
|
|
5119434d21 | ||
|
|
b29540b14e | ||
|
|
e69785bb89 | ||
|
|
76465fee07 | ||
|
|
b52c459ebb | ||
|
|
1d73982545 | ||
|
|
74f6c45f36 | ||
|
|
0490b528e4 | ||
|
|
ffa1e14449 | ||
|
|
b8fe89b2f4 | ||
|
|
94b293202c | ||
|
|
7ef143a642 | ||
|
|
057f6916e9 | ||
|
|
e24e892cb3 | ||
|
|
78976b06e2 | ||
|
|
96cfcd5b2b | ||
|
|
12bda0122c | ||
|
|
4ac4e5abf2 | ||
|
|
268f0f53a8 | ||
|
|
71521f3428 | ||
|
|
6101fb2bef | ||
|
|
8f6aa0896b | ||
|
|
b8f0af5b2e | ||
|
|
32730f6c41 | ||
|
|
7caa99f8f2 | ||
|
|
4f8e2ab478 | ||
|
|
5c44f392ca | ||
|
|
174d21fd4e | ||
|
|
c2ed6d47f1 | ||
|
|
0f6f717d09 | ||
|
|
d7fb637f68 | ||
|
|
fce9086b27 | ||
|
|
97586cd2c8 | ||
|
|
b74458f0b0 | ||
|
|
7c7a0fceaf | ||
|
|
425a8880cd | ||
|
|
23fe90ec64 | ||
|
|
c01ec5d039 | ||
|
|
4f284165c2 | ||
|
|
2a62ccff11 | ||
|
|
d09cf472dd | ||
|
|
5c721ae6f5 | ||
|
|
2bb8fcdb5f | ||
|
|
6eda93098b | ||
|
|
6344f554d6 | ||
|
|
7e4c1f374c | ||
|
|
28eaab7f7d | ||
|
|
1937944f7e | ||
|
|
3b4b84fdab | ||
|
|
32325bb49c | ||
|
|
c01c1e93f9 | ||
|
|
eac096019f | ||
|
|
9f9389e846 | ||
|
|
a71317881f | ||
|
|
7092824c96 | ||
|
|
0ff998bbd7 | ||
|
|
fc318ad211 | ||
|
|
73323335cb | ||
|
|
ef57c5523d | ||
|
|
846f4a7222 | ||
|
|
05036778d6 | ||
|
|
52df661238 | ||
|
|
7957dc237e | ||
|
|
3fe419ba2f | ||
|
|
61944656b8 | ||
|
|
1cb997b66d | ||
|
|
89463808db | ||
|
|
6aca66d8cf | ||
|
|
38f8102fb3 | ||
|
|
e709499240 | ||
|
|
0b714d5e52 | ||
|
|
98e4f0c6dc | ||
|
|
d82d0af565 | ||
|
|
d8abb7039d | ||
|
|
84dc11048d | ||
|
|
bad915bbaa | ||
|
|
287dea2d36 | ||
|
|
a0b937769d | ||
|
|
6acef4a406 | ||
|
|
8b77eb9850 | ||
|
|
6f22836dcb | ||
|
|
a4347c8878 | ||
|
|
836f7eff09 | ||
|
|
c993bd472d | ||
|
|
431ab92a02 | ||
|
|
94f469a6b1 | ||
|
|
3fec1c6890 | ||
|
|
f8316911bd | ||
|
|
642d1f6be5 | ||
|
|
5a82c3a130 | ||
|
|
6a8174afac | ||
|
|
f4c86634f7 | ||
|
|
322e588a4e | ||
|
|
822dee7a13 | ||
|
|
101e179788 | ||
|
|
57abee6cf0 | ||
|
|
b615847b09 | ||
|
|
ffef87e249 | ||
|
|
ba3b8df4c9 | ||
|
|
40175d3e54 | ||
|
|
06b047cfe6 |
@@ -261,7 +261,7 @@
|
||||
<property name="external_port">${env.OPENSHIFT_JBOSSEAP_CLUSTER_PROXY_PORT}
|
||||
</property>
|
||||
<property name="bind_port">7600</property>
|
||||
<property name="bind_addr">${env.OPENSHIFT_INTERNAL_IP}</property>
|
||||
<property name="bind_addr">${env.OPENSHIFT_JBOSSEAP_IP}</property>
|
||||
</transport>
|
||||
<protocol type="TCPPING">
|
||||
<property name="timeout">3000</property>
|
||||
@@ -476,15 +476,15 @@
|
||||
|
||||
<interfaces>
|
||||
<interface name="management">
|
||||
<loopback-address value="${env.OPENSHIFT_INTERNAL_IP}" />
|
||||
<loopback-address value="${env.OPENSHIFT_JBOSSEAP_IP}" />
|
||||
</interface>
|
||||
<interface name="public">
|
||||
<loopback-address value="${env.OPENSHIFT_INTERNAL_IP}" />
|
||||
<loopback-address value="${env.OPENSHIFT_JBOSSEAP_IP}" />
|
||||
</interface>
|
||||
<interface name="unsecure">
|
||||
<!-- Used for IIOP sockets in the standarad configuration. To secure JacORB
|
||||
you need to setup SSL -->
|
||||
<loopback-address value="${env.OPENSHIFT_INTERNAL_IP}" />
|
||||
<loopback-address value="${env.OPENSHIFT_JBOSSEAP_IP}" />
|
||||
</interface>
|
||||
</interfaces>
|
||||
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
rm -rf $OPENSHIFT_JBOSSAS_LOG_DIR\*.log.*
|
||||
if [ $OPENSHIFT_JBOSSAS_LOG_DIR ]; then
|
||||
rm -rf $OPENSHIFT_JBOSSAS_LOG_DIR/*.log.*
|
||||
fi
|
||||
|
||||
if [ $OPENSHIFT_JBOSSEAP_LOG_DIR ]; then
|
||||
rm -rf $OPENSHIFT_JBOSSEAP_LOG_DIR/*.log.*
|
||||
fi
|
||||
12
README.md
12
README.md
@@ -41,7 +41,7 @@ To install maven and openjdk on Ubuntu, issue the following commands
|
||||
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.
|
||||
# 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.
|
||||
@@ -54,16 +54,16 @@ If you don't have git you can download the sources as a zip file from [here](htt
|
||||
|
||||
Now build the application
|
||||
|
||||
Embedded HSQL database:
|
||||
# Embedded HSQL database:
|
||||
mvn clean package tomee:build -Pprod
|
||||
|
||||
External MySQL database:
|
||||
# External MySQL database:
|
||||
mvn clean package tomee:build -Pprod -Pmysql
|
||||
|
||||
External PostgreSQL database:
|
||||
# External PostgreSQL database:
|
||||
mvn clean package tomee:build -Pprod -Ppgsql
|
||||
|
||||
External Microsoft SQL Server database:
|
||||
# External Microsoft SQL Server database:
|
||||
mvn clean package tomee:build -Pprod -Pmssql
|
||||
|
||||
It will generate a zip file at `target/commafeed.zip` with everything you need to run the application.
|
||||
@@ -81,7 +81,7 @@ This will generate the file `target/commafeed.war`. Copy this file to your tomee
|
||||
* The application is online at [http://localhost:8082/commafeed](http://localhost:8082/commafeed). Don't forget to set the public URL in the admin settings.
|
||||
* The default user is `admin` and the password is `admin`.
|
||||
|
||||
You can use nginix or apache as a proxy http server. Note that when using apache, the `ProxyPreserveHost on` option should be `set in your config file.
|
||||
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
|
||||
-----------------
|
||||
|
||||
@@ -1 +1 @@
|
||||
set JAVA_OPTS=-Djava.net.preferIPv4Stack=true -Xmx1024m -XX:MaxPermSize=256m -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC
|
||||
set JAVA_OPTS=-Djava.net.preferIPv4Stack=true -Xmx1024m -XX:MaxPermSize=256m -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -Djsse.enableSNIExtension=false
|
||||
@@ -1 +1 @@
|
||||
export JAVA_OPTS="-Djava.net.preferIPv4Stack=true -Xmx1024m -XX:MaxPermSize=256m -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC"
|
||||
export JAVA_OPTS="-Djava.net.preferIPv4Stack=true -Xmx1024m -XX:MaxPermSize=256m -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -Djsse.enableSNIExtension=false"
|
||||
130
pom.xml
130
pom.xml
@@ -4,7 +4,7 @@
|
||||
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.5.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<name>CommaFeed</name>
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<artifactId>tomee-maven-plugin</artifactId>
|
||||
<version>1.5.2</version>
|
||||
<configuration>
|
||||
<tomeeVersion>1.5.2</tomeeVersion>
|
||||
<tomeeVersion>1.6.0</tomeeVersion>
|
||||
<tomeeClassifier>plus</tomeeClassifier>
|
||||
<tomeeHttpPort>8082</tomeeHttpPort>
|
||||
<args>-Xmx1024m -XX:MaxPermSize=512m -XX:+CMSClassUnloadingEnabled</args>
|
||||
@@ -104,13 +104,18 @@
|
||||
<lib>org.hibernate.common:hibernate-commons-annotations:4.0.1.Final</lib>
|
||||
<lib>org.hibernate:hibernate-validator:4.3.1.Final</lib>
|
||||
<lib>org.jboss.logging:jboss-logging:3.1.3.GA</lib>
|
||||
<lib>org.javassist:javassist:3.15.0-GA</lib>
|
||||
|
||||
<lib>org.apache.openejb:openejb-bonecp:4.6.0</lib>
|
||||
<lib>com.jolbox:bonecp:0.8.0.RELEASE</lib>
|
||||
<lib>com.google.guava:guava:14.0.1</lib>
|
||||
|
||||
<lib>dom4j:dom4j:1.6.1</lib>
|
||||
<lib>antlr:antlr:2.7.7</lib>
|
||||
<lib>remove:openjpa-</lib>
|
||||
<lib>remove:hsqldb</lib>
|
||||
<lib>org.hsqldb:hsqldb:2.3.0</lib>
|
||||
<lib>mysql:mysql-connector-java:5.1.24</lib>
|
||||
<lib>mysql:mysql-connector-java:5.1.26</lib>
|
||||
<lib>postgresql:postgresql:9.1-901.jdbc4</lib>
|
||||
<lib>net.sourceforge.jtds:jtds:1.3.1</lib>
|
||||
|
||||
@@ -169,7 +174,7 @@
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>2.1.5</version>
|
||||
<version>2.1.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -189,7 +194,7 @@
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>0.12.0</version>
|
||||
<version>1.12.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -214,28 +219,28 @@
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>14.0.1</version>
|
||||
<version>16.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>1.8.3</version>
|
||||
<version>1.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.8</version>
|
||||
<version>1.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
@@ -260,7 +265,7 @@
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>1.3</version>
|
||||
<version>1.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -298,34 +303,34 @@
|
||||
<dependency>
|
||||
<groupId>com.google.gwt</groupId>
|
||||
<artifactId>gwt-servlet</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<version>2.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.cssparser</groupId>
|
||||
<artifactId>cssparser</artifactId>
|
||||
<version>0.9.9</version>
|
||||
<version>0.9.13</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.2.5</version>
|
||||
<version>4.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.7.2</version>
|
||||
<version>1.7.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<version>2.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
@@ -336,28 +341,28 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-core</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<version>6.14.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-auth-roles</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<version>6.14.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-extensions</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<version>6.14.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-cdi</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<version>6.14.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ro.isdc.wro4j</groupId>
|
||||
<artifactId>wro4j-extensions</artifactId>
|
||||
<version>1.6.3</version>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -375,12 +380,83 @@
|
||||
<dependency>
|
||||
<groupId>com.codahale.metrics</groupId>
|
||||
<artifactId>metrics-core</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.codahale.metrics</groupId>
|
||||
<artifactId>metrics-json</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>lodash</artifactId>
|
||||
<version>2.4.1-3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>jquery</artifactId>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>jquery-mousewheel</artifactId>
|
||||
<version>3.1.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>angularjs</artifactId>
|
||||
<version>1.2.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>angular-ui-router</artifactId>
|
||||
<version>0.2.8-2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>angular-ui-utils</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>ui-select2</artifactId>
|
||||
<version>0.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>angular-ui-bootstrap</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>mousetrap</artifactId>
|
||||
<version>1.4.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>momentjs</artifactId>
|
||||
<version>2.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>ng-grid</artifactId>
|
||||
<version>2.0.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>device.js</artifactId>
|
||||
<version>139f208</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>ngInfiniteScroll</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -402,7 +478,7 @@
|
||||
<plugin>
|
||||
<groupId>ro.isdc.wro4j</groupId>
|
||||
<artifactId>wro4j-maven-plugin</artifactId>
|
||||
<version>1.6.3</version>
|
||||
<version>1.7.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>js</id>
|
||||
@@ -412,7 +488,7 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<targetGroups>app</targetGroups>
|
||||
<options>indent,devel,noarg,quotmark,laxcomma,laxbreak</options>
|
||||
<options>devel,noarg,quotmark,laxcomma,laxbreak</options>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
@@ -549,7 +625,7 @@
|
||||
<plugin>
|
||||
<groupId>ro.isdc.wro4j</groupId>
|
||||
<artifactId>wro4j-maven-plugin</artifactId>
|
||||
<version>1.6.3</version>
|
||||
<version>1.7.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
||||
@@ -4,47 +4,45 @@ 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.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.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;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.HttpResponseException;
|
||||
import org.apache.http.client.config.CookieSpecs;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.params.CookiePolicy;
|
||||
import org.apache.http.client.params.HttpClientParams;
|
||||
import org.apache.http.conn.ClientConnectionManager;
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||
import org.apache.http.conn.ssl.X509HostnameVerifier;
|
||||
import org.apache.http.impl.client.DecompressingHttpClient;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
|
||||
import org.apache.http.impl.client.SystemDefaultHttpClient;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import org.apache.http.params.HttpProtocolParams;
|
||||
import org.apache.http.protocol.BasicHttpContext;
|
||||
import org.apache.http.protocol.ExecutionContext;
|
||||
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.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 org.apache.wicket.util.io.IOUtils;
|
||||
|
||||
/**
|
||||
* Smart HTTP getter: handles gzip, ssl, last modified and etag headers
|
||||
@@ -57,8 +55,32 @@ public class HttpGetter {
|
||||
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 String UTF8 = "UTF-8";
|
||||
private static final String HTTPS = "https";
|
||||
|
||||
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 SSLContext SSL_CONTEXT = null;
|
||||
static {
|
||||
@@ -70,8 +92,6 @@ public class HttpGetter {
|
||||
}
|
||||
}
|
||||
|
||||
private static final X509HostnameVerifier VERIFIER = new DefaultHostnameVerifier();
|
||||
|
||||
public HttpResult getBinary(String url, int timeout) throws ClientProtocolException, IOException, NotModifiedException {
|
||||
return getBinary(url, null, null, timeout);
|
||||
}
|
||||
@@ -95,10 +115,11 @@ public class HttpGetter {
|
||||
HttpResult result = null;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
HttpClient client = newClient(timeout);
|
||||
CloseableHttpClient client = newClient(timeout);
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
HttpGet httpget = new HttpGet(url);
|
||||
HttpContext context = new BasicHttpContext();
|
||||
HttpClientContext context = HttpClientContext.create();
|
||||
|
||||
httpget.addHeader(HttpHeaders.ACCEPT_LANGUAGE, ACCEPT_LANGUAGE);
|
||||
httpget.addHeader(HttpHeaders.PRAGMA, PRAGMA_NO_CACHE);
|
||||
@@ -112,7 +133,6 @@ public class HttpGetter {
|
||||
httpget.addHeader(HttpHeaders.IF_NONE_MATCH, eTag);
|
||||
}
|
||||
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
response = client.execute(httpget, context);
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
@@ -150,14 +170,15 @@ public class HttpGetter {
|
||||
contentType = entity.getContentType().getValue();
|
||||
}
|
||||
}
|
||||
HttpUriRequest req = (HttpUriRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
|
||||
HttpHost host = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
|
||||
HttpUriRequest req = (HttpUriRequest) context.getRequest();
|
||||
HttpHost host = context.getTargetHost();
|
||||
String urlAfterRedirect = req.getURI().isAbsolute() ? req.getURI().toString() : host.toURI() + req.getURI();
|
||||
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
result = new HttpResult(content, contentType, lastModifiedHeaderValue, eTagHeaderValue, duration, urlAfterRedirect);
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
IOUtils.closeQuietly(response);
|
||||
IOUtils.closeQuietly(client);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -205,21 +226,25 @@ public class HttpGetter {
|
||||
}
|
||||
}
|
||||
|
||||
public static HttpClient newClient(int timeout) {
|
||||
DefaultHttpClient client = new SystemDefaultHttpClient();
|
||||
public static CloseableHttpClient newClient(int timeout) {
|
||||
HttpClientBuilder builder = HttpClients.custom();
|
||||
builder.useSystemProperties();
|
||||
builder.addInterceptorFirst(REMOVE_INCORRECT_CONTENT_ENCODING);
|
||||
builder.disableAutomaticRetries();
|
||||
|
||||
SSLSocketFactory ssf = new SSLSocketFactory(SSL_CONTEXT, VERIFIER);
|
||||
ClientConnectionManager ccm = client.getConnectionManager();
|
||||
SchemeRegistry sr = ccm.getSchemeRegistry();
|
||||
sr.register(new Scheme(HTTPS, 443, ssf));
|
||||
builder.setSslcontext(SSL_CONTEXT);
|
||||
builder.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
|
||||
HttpParams params = client.getParams();
|
||||
HttpClientParams.setCookiePolicy(params, CookiePolicy.IGNORE_COOKIES);
|
||||
HttpProtocolParams.setContentCharset(params, UTF8);
|
||||
HttpConnectionParams.setConnectionTimeout(params, timeout);
|
||||
HttpConnectionParams.setSoTimeout(params, timeout);
|
||||
client.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
|
||||
return new DecompressingHttpClient(client);
|
||||
RequestConfig.Builder configBuilder = RequestConfig.custom();
|
||||
configBuilder.setCookieSpec(CookieSpecs.IGNORE_COOKIES);
|
||||
configBuilder.setSocketTimeout(timeout);
|
||||
configBuilder.setConnectTimeout(timeout);
|
||||
configBuilder.setConnectionRequestTimeout(timeout);
|
||||
builder.setDefaultRequestConfig(configBuilder.build());
|
||||
|
||||
builder.setDefaultConnectionConfig(ConnectionConfig.custom().setCharset(Consts.ISO_8859_1).build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static class NotModifiedException extends Exception {
|
||||
@@ -245,24 +270,4 @@ public class HttpGetter {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultHostnameVerifier implements X509HostnameVerifier {
|
||||
|
||||
@Override
|
||||
public void verify(String string, SSLSocket ssls) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(String string, X509Certificate xc) throws SSLException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(String string, String[] strings, String[] strings1) throws SSLException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String string, SSLSession ssls) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,13 +26,23 @@ public class ScheduledTasks {
|
||||
DatabaseCleaningService cleaner;
|
||||
|
||||
/**
|
||||
* clean old read statuses, runs every day at midnight
|
||||
* clean old read statuses
|
||||
*/
|
||||
@Schedule(hour = "0", persistent = false)
|
||||
@Schedule(hour = "*", persistent = false)
|
||||
private void cleanupOldStatuses() {
|
||||
Date threshold = applicationSettingsService.getUnreadThreshold();
|
||||
if (threshold != null) {
|
||||
cleaner.cleanStatusesOlderThan(threshold);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clean feeds without subscriptions, then clean contents without entries
|
||||
*/
|
||||
@Schedule(hour = "*", persistent = false)
|
||||
private void cleanFeedsAndContents() {
|
||||
cleaner.cleanEntriesWithoutSubscriptions();
|
||||
cleaner.cleanFeedsWithoutSubscriptions();
|
||||
cleaner.cleanContentsWithoutEntries();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.inject.Alternative;
|
||||
|
||||
import org.apache.commons.pool.impl.GenericObjectPool;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
@@ -30,7 +33,14 @@ public class RedisCacheService extends CacheService {
|
||||
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
|
||||
private JedisPool pool;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
JedisPoolConfig config = new JedisPoolConfig();
|
||||
config.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
|
||||
pool = new JedisPool(config, "localhost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getLastEntries(Feed feed) {
|
||||
|
||||
@@ -6,15 +6,12 @@ import java.util.List;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
import javax.persistence.criteria.Subquery;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -26,7 +23,6 @@ import com.commafeed.backend.model.FeedSubscription_;
|
||||
import com.commafeed.backend.model.Feed_;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.User_;
|
||||
import com.commafeed.frontend.model.FeedCount;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@@ -95,8 +91,8 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
public List<Feed> findByTopic(String topic) {
|
||||
return findByField(Feed_.pushTopicHash, DigestUtils.sha1Hex(topic));
|
||||
}
|
||||
|
||||
public int deleteWithoutSubscriptions(int max) {
|
||||
|
||||
public List<Feed> findWithoutSubscriptions(int max) {
|
||||
CriteriaQuery<Feed> query = builder.createQuery(getType());
|
||||
Root<Feed> root = query.from(getType());
|
||||
|
||||
@@ -105,54 +101,6 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
TypedQuery<Feed> q = em.createQuery(query);
|
||||
q.setMaxResults(max);
|
||||
|
||||
List<Feed> list = q.getResultList();
|
||||
int deleted = list.size();
|
||||
|
||||
delete(list);
|
||||
return deleted;
|
||||
|
||||
}
|
||||
|
||||
public static enum DuplicateMode {
|
||||
NORMALIZED_URL(Feed_.normalizedUrlHash), LAST_CONTENT(Feed_.lastContentHash), PUSH_TOPIC(Feed_.pushTopicHash);
|
||||
private SingularAttribute<Feed, String> path;
|
||||
|
||||
private DuplicateMode(SingularAttribute<Feed, String> path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public SingularAttribute<Feed, String> getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public List<FeedCount> findDuplicates(DuplicateMode mode, int offset, int limit, long minCount) {
|
||||
CriteriaQuery<String> query = builder.createQuery(String.class);
|
||||
Root<Feed> root = query.from(getType());
|
||||
|
||||
Path<String> path = root.get(mode.getPath());
|
||||
Expression<Long> count = builder.count(path);
|
||||
|
||||
query.select(path);
|
||||
|
||||
query.groupBy(path);
|
||||
query.having(builder.greaterThan(count, minCount));
|
||||
|
||||
TypedQuery<String> q = em.createQuery(query);
|
||||
limit(q, offset, limit);
|
||||
List<String> pathValues = q.getResultList();
|
||||
|
||||
List<FeedCount> result = Lists.newArrayList();
|
||||
for (String pathValue : pathValues) {
|
||||
FeedCount fc = new FeedCount(pathValue);
|
||||
for (Feed feed : findByField(mode.getPath(), pathValue)) {
|
||||
Feed f = new Feed();
|
||||
f.setId(feed.getId());
|
||||
f.setUrl(feed.getUrl());
|
||||
fc.getFeeds().add(f);
|
||||
}
|
||||
result.add(fc);
|
||||
}
|
||||
return result;
|
||||
return q.getResultList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
@@ -15,6 +16,7 @@ import com.commafeed.backend.model.FeedEntryContent_;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
@Stateless
|
||||
public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
|
||||
|
||||
public Long findExisting(String contentHash, String titleHash) {
|
||||
@@ -44,6 +46,7 @@ public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
|
||||
|
||||
List<FeedEntryContent> list = q.getResultList();
|
||||
int deleted = list.size();
|
||||
delete(list);
|
||||
return deleted;
|
||||
|
||||
}
|
||||
|
||||
@@ -6,13 +6,19 @@ import java.util.List;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.FeedSubscription_;
|
||||
import com.commafeed.backend.model.Feed_;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
@@ -36,6 +42,34 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
return Iterables.getFirst(list, null);
|
||||
}
|
||||
|
||||
public List<FeedEntry> findWithoutSubscriptions(int max) {
|
||||
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
|
||||
Root<FeedEntry> root = query.from(getType());
|
||||
|
||||
Join<FeedEntry, Feed> feedJoin = root.join(FeedEntry_.feed);
|
||||
SetJoin<Feed, FeedSubscription> subJoin = feedJoin.join(Feed_.subscriptions, JoinType.LEFT);
|
||||
query.where(builder.isNull(subJoin.get(FeedSubscription_.id)));
|
||||
TypedQuery<FeedEntry> q = em.createQuery(query);
|
||||
q.setMaxResults(max);
|
||||
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
public int delete(Feed feed, int max) {
|
||||
|
||||
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
|
||||
Root<FeedEntry> root = query.from(getType());
|
||||
|
||||
query.where(builder.equal(root.get(FeedEntry_.feed), feed));
|
||||
TypedQuery<FeedEntry> q = em.createQuery(query);
|
||||
q.setMaxResults(max);
|
||||
|
||||
List<FeedEntry> list = q.getResultList();
|
||||
int deleted = list.size();
|
||||
delete(list);
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public int delete(Date olderThan, int max) {
|
||||
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
|
||||
Root<FeedEntry> root = query.from(getType());
|
||||
|
||||
@@ -15,8 +15,9 @@ import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.criterion.Conjunction;
|
||||
import org.hibernate.criterion.Disjunction;
|
||||
import org.hibernate.criterion.MatchMode;
|
||||
import org.hibernate.criterion.Order;
|
||||
@@ -31,6 +32,8 @@ import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent_;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedEntryStatus_;
|
||||
import com.commafeed.backend.model.FeedEntryTag;
|
||||
import com.commafeed.backend.model.FeedEntryTag_;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.Models;
|
||||
@@ -40,31 +43,34 @@ import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.frontend.model.UnreadCount;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
|
||||
@Stateless
|
||||
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";
|
||||
|
||||
@Inject
|
||||
FeedEntryTagDAO feedEntryTagDAO;
|
||||
|
||||
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_DESC = new Comparator<FeedEntryStatus>() {
|
||||
@Override
|
||||
public int compare(FeedEntryStatus o1, FeedEntryStatus o2) {
|
||||
return ObjectUtils.compare(o2.getEntryUpdated(), o1.getEntryUpdated());
|
||||
CompareToBuilder builder = new CompareToBuilder();
|
||||
builder.append(o2.getEntryUpdated(), o1.getEntryUpdated());
|
||||
builder.append(o2.getId(), o1.getId());
|
||||
return builder.build();
|
||||
};
|
||||
};
|
||||
|
||||
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ASC = new Comparator<FeedEntryStatus>() {
|
||||
@Override
|
||||
public int compare(FeedEntryStatus o1, FeedEntryStatus o2) {
|
||||
return ObjectUtils.compare(o1.getEntryUpdated(), o2.getEntryUpdated());
|
||||
};
|
||||
};
|
||||
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ASC = Ordering.from(STATUS_COMPARATOR_DESC).reverse();
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
public FeedEntryStatus getStatus(FeedSubscription sub, FeedEntry entry) {
|
||||
public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) {
|
||||
|
||||
CriteriaQuery<FeedEntryStatus> query = builder.createQuery(getType());
|
||||
Root<FeedEntryStatus> root = query.from(getType());
|
||||
@@ -77,14 +83,14 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
List<FeedEntryStatus> statuses = em.createQuery(query).getResultList();
|
||||
FeedEntryStatus status = Iterables.getFirst(statuses, null);
|
||||
|
||||
return handleStatus(status, sub, entry);
|
||||
return handleStatus(user, status, sub, entry);
|
||||
}
|
||||
|
||||
private FeedEntryStatus handleStatus(FeedEntryStatus status, FeedSubscription sub, FeedEntry entry) {
|
||||
private FeedEntryStatus handleStatus(User user, FeedEntryStatus status, FeedSubscription sub, FeedEntry entry) {
|
||||
if (status == null) {
|
||||
Date unreadThreshold = applicationSettingsService.getUnreadThreshold();
|
||||
boolean read = unreadThreshold == null ? false : entry.getUpdated().before(unreadThreshold);
|
||||
status = new FeedEntryStatus(sub.getUser(), sub, entry);
|
||||
status = new FeedEntryStatus(user, sub, entry);
|
||||
status.setRead(read);
|
||||
status.setMarkable(!read);
|
||||
} else {
|
||||
@@ -93,6 +99,12 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
return status;
|
||||
}
|
||||
|
||||
private FeedEntryStatus fetchTags(User user, FeedEntryStatus status) {
|
||||
List<FeedEntryTag> tags = feedEntryTagDAO.findByEntry(user, status.getEntry());
|
||||
status.setTags(tags);
|
||||
return status;
|
||||
}
|
||||
|
||||
public List<FeedEntryStatus> findStarred(User user, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent) {
|
||||
|
||||
CriteriaQuery<FeedEntryStatus> query = builder.createQuery(getType());
|
||||
@@ -102,11 +114,12 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
|
||||
predicates.add(builder.equal(root.get(FeedEntryStatus_.user), user));
|
||||
predicates.add(builder.equal(root.get(FeedEntryStatus_.starred), true));
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
|
||||
if (newerThan != null) {
|
||||
predicates.add(builder.greaterThanOrEqualTo(root.get(FeedEntryStatus_.entryInserted), newerThan));
|
||||
}
|
||||
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
|
||||
orderStatusesBy(query, root, order);
|
||||
|
||||
@@ -115,13 +128,14 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
setTimeout(q);
|
||||
List<FeedEntryStatus> statuses = q.getResultList();
|
||||
for (FeedEntryStatus status : statuses) {
|
||||
status = handleStatus(status, status.getSubscription(), status.getEntry());
|
||||
status = handleStatus(user, status, status.getSubscription(), status.getEntry());
|
||||
status = fetchTags(user, status);
|
||||
}
|
||||
return lazyLoadContent(includeContent, statuses);
|
||||
}
|
||||
|
||||
private Criteria buildSearchCriteria(FeedSubscription sub, boolean unreadOnly, String keywords, Date newerThan, int offset, int limit,
|
||||
ReadingOrder order, Date last) {
|
||||
private Criteria buildSearchCriteria(User user, FeedSubscription sub, boolean unreadOnly, String keywords, Date newerThan, int offset, int limit,
|
||||
ReadingOrder order, Date last, String tag) {
|
||||
Criteria criteria = getSession().createCriteria(FeedEntry.class, ALIAS_ENTRY);
|
||||
|
||||
criteria.add(Restrictions.eq(FeedEntry_.feed.getName(), sub.getFeed()));
|
||||
@@ -139,7 +153,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
Criteria statusJoin = criteria.createCriteria(FeedEntry_.statuses.getName(), ALIAS_STATUS, JoinType.LEFT_OUTER_JOIN,
|
||||
Restrictions.eq(FeedEntryStatus_.subscription.getName(), sub));
|
||||
|
||||
if (unreadOnly) {
|
||||
if (unreadOnly && tag == null) {
|
||||
|
||||
Disjunction or = Restrictions.disjunction();
|
||||
or.add(Restrictions.isNull(FeedEntryStatus_.read.getName()));
|
||||
@@ -152,6 +166,13 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
}
|
||||
}
|
||||
|
||||
if (tag != null) {
|
||||
Conjunction and = Restrictions.conjunction();
|
||||
and.add(Restrictions.eq(FeedEntryTag_.user.getName(), user));
|
||||
and.add(Restrictions.eq(FeedEntryTag_.name.getName(), tag));
|
||||
criteria.createCriteria(FeedEntry_.tags.getName(), ALIAS_TAG, JoinType.INNER_JOIN, and);
|
||||
}
|
||||
|
||||
if (newerThan != null) {
|
||||
criteria.add(Restrictions.ge(FeedEntry_.inserted.getName(), newerThan));
|
||||
}
|
||||
@@ -165,13 +186,11 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
}
|
||||
|
||||
if (order != null) {
|
||||
Order o = null;
|
||||
if (order == ReadingOrder.asc) {
|
||||
o = Order.asc(FeedEntry_.updated.getName());
|
||||
criteria.addOrder(Order.asc(FeedEntry_.updated.getName())).addOrder(Order.asc(FeedEntry_.id.getName()));
|
||||
} else {
|
||||
o = Order.desc(FeedEntry_.updated.getName());
|
||||
criteria.addOrder(Order.desc(FeedEntry_.updated.getName())).addOrder(Order.desc(FeedEntry_.id.getName()));
|
||||
}
|
||||
criteria.addOrder(o);
|
||||
}
|
||||
if (offset > -1) {
|
||||
criteria.setFirstResult(offset);
|
||||
@@ -188,14 +207,14 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<FeedEntryStatus> findBySubscriptions(List<FeedSubscription> subs, boolean unreadOnly, String keywords, Date newerThan,
|
||||
int offset, int limit, ReadingOrder order, boolean includeContent, boolean onlyIds) {
|
||||
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) {
|
||||
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(sub, unreadOnly, keywords, newerThan, -1, capacity, order, last);
|
||||
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");
|
||||
@@ -237,7 +256,10 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
for (FeedEntryStatus placeholder : placeholders) {
|
||||
Long statusId = placeholder.getId();
|
||||
FeedEntry entry = em.find(FeedEntry.class, placeholder.getEntry().getId());
|
||||
statuses.add(handleStatus(statusId == null ? null : findById(statusId), placeholder.getSubscription(), entry));
|
||||
FeedEntryStatus status = handleStatus(user, statusId == null ? null : findById(statusId), placeholder.getSubscription(),
|
||||
entry);
|
||||
status = fetchTags(user, status);
|
||||
statuses.add(status);
|
||||
}
|
||||
statuses = lazyLoadContent(includeContent, statuses);
|
||||
}
|
||||
@@ -245,9 +267,9 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public UnreadCount getUnreadCount(FeedSubscription subscription) {
|
||||
public UnreadCount getUnreadCount(User user, FeedSubscription subscription) {
|
||||
UnreadCount uc = null;
|
||||
Criteria criteria = buildSearchCriteria(subscription, true, null, null, -1, -1, null, 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(FeedEntry_.updated.getName()), "updated");
|
||||
@@ -273,15 +295,15 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
}
|
||||
|
||||
private void orderStatusesBy(CriteriaQuery<?> query, Path<FeedEntryStatus> statusJoin, ReadingOrder order) {
|
||||
orderBy(query, statusJoin.get(FeedEntryStatus_.entryUpdated), order);
|
||||
orderBy(query, statusJoin.get(FeedEntryStatus_.entryUpdated), statusJoin.get(FeedEntryStatus_.id), order);
|
||||
}
|
||||
|
||||
private void orderBy(CriteriaQuery<?> query, Path<Date> date, ReadingOrder order) {
|
||||
private void orderBy(CriteriaQuery<?> query, Path<Date> date, Path<Long> id, ReadingOrder order) {
|
||||
if (order != null) {
|
||||
if (order == ReadingOrder.asc) {
|
||||
query.orderBy(builder.asc(date));
|
||||
query.orderBy(builder.asc(date), builder.asc(id));
|
||||
} else {
|
||||
query.orderBy(builder.desc(date));
|
||||
query.orderBy(builder.desc(date), builder.desc(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
src/main/java/com/commafeed/backend/dao/FeedEntryTagDAO.java
Normal file
43
src/main/java/com/commafeed/backend/dao/FeedEntryTagDAO.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryTag;
|
||||
import com.commafeed.backend.model.FeedEntryTag_;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.User_;
|
||||
|
||||
@Stateless
|
||||
public class FeedEntryTagDAO extends GenericDAO<FeedEntryTag> {
|
||||
|
||||
public List<String> findByUser(User user) {
|
||||
CriteriaQuery<String> query = builder.createQuery(String.class);
|
||||
Root<FeedEntryTag> root = query.from(getType());
|
||||
query.select(root.get(FeedEntryTag_.name));
|
||||
query.distinct(true);
|
||||
|
||||
Predicate p1 = builder.equal(root.get(FeedEntryTag_.user).get(User_.id), user.getId());
|
||||
query.where(p1);
|
||||
|
||||
return cache(em.createQuery(query)).getResultList();
|
||||
}
|
||||
|
||||
public List<FeedEntryTag> findByEntry(User user, FeedEntry entry) {
|
||||
CriteriaQuery<FeedEntryTag> query = builder.createQuery(getType());
|
||||
Root<FeedEntryTag> root = query.from(getType());
|
||||
|
||||
Predicate p1 = builder.equal(root.get(FeedEntryTag_.user).get(User_.id), user.getId());
|
||||
Predicate p2 = builder.equal(root.get(FeedEntryTag_.entry).get(FeedEntry_.id), entry.getId());
|
||||
|
||||
query.where(p1, p2);
|
||||
|
||||
return cache(em.createQuery(query)).getResultList();
|
||||
}
|
||||
}
|
||||
@@ -63,10 +63,11 @@ public abstract class GenericDAO<T extends AbstractModel> {
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(Collection<? extends AbstractModel> objects) {
|
||||
public int delete(Collection<? extends AbstractModel> objects) {
|
||||
for (AbstractModel object : objects) {
|
||||
delete(object);
|
||||
}
|
||||
return objects.size();
|
||||
}
|
||||
|
||||
public void deleteById(Long id) {
|
||||
|
||||
@@ -50,6 +50,8 @@ public class FeedFetcher {
|
||||
result = getter.getBinary(extractedUrl, lastModified, eTag, timeout);
|
||||
content = result.getContent();
|
||||
fetchedFeed = parser.parse(feedUrl, content);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
|
||||
@@ -55,6 +55,7 @@ public class FeedParser {
|
||||
if (xmlString == null) {
|
||||
throw new FeedException("Input string is null for url " + feedUrl);
|
||||
}
|
||||
xmlString = FeedUtils.replaceHtmlEntitiesWithNumericEntities(xmlString);
|
||||
InputSource source = new InputSource(new StringReader(xmlString));
|
||||
SyndFeed rss = new SyndFeedInput().build(source);
|
||||
handleForeignMarkup(rss);
|
||||
@@ -66,10 +67,6 @@ public class FeedParser {
|
||||
feed.setLink(rss.getLink());
|
||||
List<SyndEntry> items = rss.getEntries();
|
||||
|
||||
if (items.isEmpty()) {
|
||||
throw new FeedException("No items in the feed.");
|
||||
}
|
||||
|
||||
for (SyndEntry item : items) {
|
||||
FeedEntry entry = new FeedEntry();
|
||||
|
||||
@@ -82,8 +79,13 @@ public class FeedParser {
|
||||
continue;
|
||||
}
|
||||
entry.setGuid(FeedUtils.truncate(guid, 2048));
|
||||
entry.setUrl(FeedUtils.truncate(FeedUtils.toAbsoluteUrl(item.getLink(), feed.getLink()), 2048));
|
||||
entry.setUpdated(validateDate(getEntryUpdateDate(item), true));
|
||||
entry.setUrl(FeedUtils.truncate(FeedUtils.toAbsoluteUrl(item.getLink(), feed.getLink(), feed.getUrlAfterRedirect()), 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());
|
||||
}
|
||||
|
||||
FeedEntryContent content = new FeedEntryContent();
|
||||
content.setContent(getContent(item));
|
||||
|
||||
@@ -8,11 +8,11 @@ import com.commafeed.backend.model.FeedEntry;
|
||||
public class FeedRefreshContext {
|
||||
private Feed feed;
|
||||
private List<FeedEntry> entries;
|
||||
private boolean isUrgent;
|
||||
private boolean urgent;
|
||||
|
||||
public FeedRefreshContext(Feed feed, boolean isUrgent) {
|
||||
this.feed = feed;
|
||||
this.isUrgent = isUrgent;
|
||||
this.urgent = isUrgent;
|
||||
}
|
||||
|
||||
public Feed getFeed() {
|
||||
@@ -24,11 +24,11 @@ public class FeedRefreshContext {
|
||||
}
|
||||
|
||||
public boolean isUrgent() {
|
||||
return isUrgent;
|
||||
return urgent;
|
||||
}
|
||||
|
||||
public void setUrgent(boolean isUrgent) {
|
||||
this.isUrgent = isUrgent;
|
||||
public void setUrgent(boolean urgent) {
|
||||
this.urgent = urgent;
|
||||
}
|
||||
|
||||
public List<FeedEntry> getEntries() {
|
||||
|
||||
@@ -17,6 +17,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
|
||||
import com.codahale.metrics.Gauge;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.backend.dao.FeedDAO;
|
||||
@@ -66,6 +67,24 @@ public class FeedRefreshTaskGiver {
|
||||
feedRefreshed = metrics.meter(MetricRegistry.name(getClass(), "feedRefreshed"));
|
||||
threadWaited = metrics.meter(MetricRegistry.name(getClass(), "threadWaited"));
|
||||
refill = metrics.meter(MetricRegistry.name(getClass(), "refill"));
|
||||
metrics.register(MetricRegistry.name(getClass(), "addQueue"), new Gauge<Integer>() {
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return addQueue.size();
|
||||
}
|
||||
});
|
||||
metrics.register(MetricRegistry.name(getClass(), "takeQueue"), new Gauge<Integer>() {
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return takeQueue.size();
|
||||
}
|
||||
});
|
||||
metrics.register(MetricRegistry.name(getClass(), "giveBackQueue"), new Gauge<Integer>() {
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return giveBackQueue.size();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
@@ -81,17 +100,16 @@ public class FeedRefreshTaskGiver {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
// sleeping for a little while, let everything settle
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("interrupted while sleeping");
|
||||
}
|
||||
log.info("starting feed refresh task giver");
|
||||
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// sleeping for a little while, let everything settle
|
||||
Thread.sleep(60000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("interrupted while sleeping");
|
||||
}
|
||||
while (!executor.isShutdown()) {
|
||||
try {
|
||||
FeedRefreshContext context = take();
|
||||
@@ -147,22 +165,25 @@ public class FeedRefreshTaskGiver {
|
||||
*/
|
||||
private void refill() {
|
||||
refill.mark();
|
||||
int count = Math.min(100, 3 * backgroundThreads);
|
||||
|
||||
// first, get feeds that are up to refresh from the database
|
||||
List<FeedRefreshContext> contexts = Lists.newArrayList();
|
||||
if (!applicationSettingsService.get().isCrawlingPaused()) {
|
||||
List<Feed> feeds = feedDAO.findNextUpdatable(count, getLastLoginThreshold());
|
||||
for (Feed feed : feeds) {
|
||||
contexts.add(new FeedRefreshContext(feed, false));
|
||||
}
|
||||
int batchSize = Math.min(100, 3 * backgroundThreads);
|
||||
|
||||
// add feeds we got from the add() method
|
||||
int addQueueSize = addQueue.size();
|
||||
for (int i = 0; i < Math.min(batchSize, addQueueSize); i++) {
|
||||
contexts.add(addQueue.poll());
|
||||
}
|
||||
|
||||
// then, add to those the feeds we got from the add() method. We add them at the beginning of the list as they probably have a
|
||||
// higher priority
|
||||
int size = addQueue.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
contexts.add(0, addQueue.poll());
|
||||
// add feeds that are up to refresh from the database
|
||||
if (!applicationSettingsService.get().isCrawlingPaused()) {
|
||||
int count = batchSize - contexts.size();
|
||||
if (count > 0) {
|
||||
List<Feed> feeds = feedDAO.findNextUpdatable(count, getLastLoginThreshold());
|
||||
for (Feed feed : feeds) {
|
||||
contexts.add(new FeedRefreshContext(feed, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set the disabledDate to now as we use the disabledDate in feedDAO to decide what to refresh next. We also use a map to remove
|
||||
@@ -178,8 +199,8 @@ public class FeedRefreshTaskGiver {
|
||||
takeQueue.addAll(map.values());
|
||||
|
||||
// add feeds from the giveBack queue to the map, overriding duplicates
|
||||
size = giveBackQueue.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
int giveBackQueueSize = giveBackQueue.size();
|
||||
for (int i = 0; i < giveBackQueueSize; i++) {
|
||||
Feed feed = giveBackQueue.poll();
|
||||
map.put(feed.getId(), new FeedRefreshContext(feed, false));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.commafeed.backend.feeds;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -15,6 +17,7 @@ import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
|
||||
import org.apache.wicket.request.UrlUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Document.OutputSettings;
|
||||
@@ -50,6 +53,8 @@ public class FeedUtils {
|
||||
private static final List<String> ALLOWED_IMG_CSS_RULES = Arrays.asList("display", "width", "height");
|
||||
private static final char[] FORBIDDEN_CSS_RULE_CHARACTERS = new char[] { '(', ')' };
|
||||
|
||||
private static final Whitelist WHITELIST = buildWhiteList();
|
||||
|
||||
public static String truncate(String string, int length) {
|
||||
if (string != null) {
|
||||
string = string.substring(0, Math.min(length, string.length()));
|
||||
@@ -57,6 +62,39 @@ public class FeedUtils {
|
||||
return string;
|
||||
}
|
||||
|
||||
private static synchronized Whitelist buildWhiteList() {
|
||||
Whitelist whitelist = new Whitelist();
|
||||
whitelist.addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", "dt", "em", "h1",
|
||||
"h2", "h3", "h4", "h5", "h6", "i", "iframe", "img", "li", "ol", "p", "pre", "q", "small", "strike", "strong", "sub", "sup",
|
||||
"table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul");
|
||||
|
||||
whitelist.addAttributes("div", "dir");
|
||||
whitelist.addAttributes("pre", "dir");
|
||||
whitelist.addAttributes("code", "dir");
|
||||
whitelist.addAttributes("table", "dir");
|
||||
whitelist.addAttributes("p", "dir");
|
||||
whitelist.addAttributes("a", "href", "title");
|
||||
whitelist.addAttributes("blockquote", "cite");
|
||||
whitelist.addAttributes("col", "span", "width");
|
||||
whitelist.addAttributes("colgroup", "span", "width");
|
||||
whitelist.addAttributes("iframe", "src", "height", "width", "allowfullscreen", "frameborder", "style");
|
||||
whitelist.addAttributes("img", "align", "alt", "height", "src", "title", "width", "style");
|
||||
whitelist.addAttributes("ol", "start", "type");
|
||||
whitelist.addAttributes("q", "cite");
|
||||
whitelist.addAttributes("table", "border", "bordercolor", "summary", "width");
|
||||
whitelist.addAttributes("td", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "width");
|
||||
whitelist.addAttributes("th", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "scope", "width");
|
||||
whitelist.addAttributes("ul", "type");
|
||||
|
||||
whitelist.addProtocols("a", "href", "ftp", "http", "https", "mailto");
|
||||
whitelist.addProtocols("blockquote", "cite", "http", "https");
|
||||
whitelist.addProtocols("img", "src", "http", "https");
|
||||
whitelist.addProtocols("q", "cite", "http", "https");
|
||||
|
||||
whitelist.addEnforcedAttribute("a", "target", "_blank");
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect feed encoding by using the declared encoding in the xml processing instruction and by detecting the characters used in the
|
||||
* feed
|
||||
@@ -91,6 +129,14 @@ public class FeedUtils {
|
||||
}
|
||||
return 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the url. The resulting url is not meant to be fetched but rather used as a mean to identify a feed and avoid duplicates
|
||||
@@ -152,38 +198,9 @@ public class FeedUtils {
|
||||
public static String handleContent(String content, String baseUri, boolean keepTextOnly) {
|
||||
if (StringUtils.isNotBlank(content)) {
|
||||
baseUri = StringUtils.trimToEmpty(baseUri);
|
||||
Whitelist whitelist = new Whitelist();
|
||||
whitelist.addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", "dt", "em",
|
||||
"h1", "h2", "h3", "h4", "h5", "h6", "i", "iframe", "img", "li", "ol", "p", "pre", "q", "small", "strike", "strong",
|
||||
"sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul");
|
||||
|
||||
whitelist.addAttributes("div", "dir");
|
||||
whitelist.addAttributes("pre", "dir");
|
||||
whitelist.addAttributes("code", "dir");
|
||||
whitelist.addAttributes("table", "dir");
|
||||
whitelist.addAttributes("p", "dir");
|
||||
whitelist.addAttributes("a", "href", "title");
|
||||
whitelist.addAttributes("blockquote", "cite");
|
||||
whitelist.addAttributes("col", "span", "width");
|
||||
whitelist.addAttributes("colgroup", "span", "width");
|
||||
whitelist.addAttributes("iframe", "src", "height", "width", "allowfullscreen", "frameborder", "style");
|
||||
whitelist.addAttributes("img", "align", "alt", "height", "src", "title", "width", "style");
|
||||
whitelist.addAttributes("ol", "start", "type");
|
||||
whitelist.addAttributes("q", "cite");
|
||||
whitelist.addAttributes("table", "border", "bordercolor", "summary", "width");
|
||||
whitelist.addAttributes("td", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "width");
|
||||
whitelist.addAttributes("th", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "scope", "width");
|
||||
whitelist.addAttributes("ul", "type");
|
||||
|
||||
whitelist.addProtocols("a", "href", "ftp", "http", "https", "mailto");
|
||||
whitelist.addProtocols("blockquote", "cite", "http", "https");
|
||||
whitelist.addProtocols("img", "src", "http", "https");
|
||||
whitelist.addProtocols("q", "cite", "http", "https");
|
||||
|
||||
whitelist.addEnforcedAttribute("a", "target", "_blank");
|
||||
|
||||
Document dirty = Jsoup.parseBodyFragment(content, baseUri);
|
||||
Cleaner cleaner = new Cleaner(whitelist);
|
||||
Cleaner cleaner = new Cleaner(WHITELIST);
|
||||
Document clean = cleaner.clean(dirty);
|
||||
|
||||
for (Element e : clean.select("iframe[style]")) {
|
||||
@@ -389,17 +406,37 @@ public class FeedUtils {
|
||||
return url;
|
||||
}
|
||||
|
||||
public static String toAbsoluteUrl(String url, String baseUrl) {
|
||||
/**
|
||||
*
|
||||
* @param url
|
||||
* the url of the entry
|
||||
* @param feedLink
|
||||
* the url of the feed as described in the feed
|
||||
* @param feedUrl
|
||||
* the url of the feed that we used to fetch the feed
|
||||
* @return an absolute url pointing to the entry
|
||||
*/
|
||||
public static String toAbsoluteUrl(String url, String feedLink, String feedUrl) {
|
||||
url = StringUtils.trimToNull(StringUtils.normalizeSpace(url));
|
||||
if (baseUrl == null || url == null || url.startsWith("http")) {
|
||||
if (url == null || url.startsWith("http")) {
|
||||
return url;
|
||||
}
|
||||
|
||||
if (url.startsWith("/") == false) {
|
||||
url = "/" + url;
|
||||
String baseUrl = (feedLink == null || UrlUtils.isRelative(feedLink)) ? feedUrl : feedLink;
|
||||
|
||||
if (baseUrl == null) {
|
||||
return url;
|
||||
}
|
||||
|
||||
return baseUrl + url;
|
||||
String result = null;
|
||||
try {
|
||||
result = new URL(new URL(baseUrl), url).toString();
|
||||
} catch (MalformedURLException e) {
|
||||
log.debug("could not parse url : " + e.getMessage(), e);
|
||||
result = url;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getFaviconUrl(FeedSubscription subscription, String publicUrl) {
|
||||
|
||||
266
src/main/java/com/commafeed/backend/feeds/HtmlEntities.java
Normal file
266
src/main/java/com/commafeed/backend/feeds/HtmlEntities.java
Normal file
@@ -0,0 +1,266 @@
|
||||
package com.commafeed.backend.feeds;
|
||||
|
||||
import java.util.Collections;
|
||||
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());
|
||||
|
||||
private static synchronized Map<String, String> loadMap() {
|
||||
Map<String, String> map = Maps.newLinkedHashMap();
|
||||
map.put("Á", "Á");
|
||||
map.put("á", "á");
|
||||
map.put("Â", "Â");
|
||||
map.put("â", "â");
|
||||
map.put("´", "´");
|
||||
map.put("Æ", "Æ");
|
||||
map.put("æ", "æ");
|
||||
map.put("À", "À");
|
||||
map.put("à", "à");
|
||||
map.put("ℵ", "ℵ");
|
||||
map.put("Α", "Α");
|
||||
map.put("α", "α");
|
||||
map.put("&", "&");
|
||||
map.put("∧", "∧");
|
||||
map.put("∠", "∠");
|
||||
map.put("Å", "Å");
|
||||
map.put("å", "å");
|
||||
map.put("≈", "≈");
|
||||
map.put("Ã", "Ã");
|
||||
map.put("ã", "ã");
|
||||
map.put("Ä", "Ä");
|
||||
map.put("ä", "ä");
|
||||
map.put("„", "„");
|
||||
map.put("Β", "Β");
|
||||
map.put("β", "β");
|
||||
map.put("¦", "¦");
|
||||
map.put("•", "•");
|
||||
map.put("∩", "∩");
|
||||
map.put("Ç", "Ç");
|
||||
map.put("ç", "ç");
|
||||
map.put("¸", "¸");
|
||||
map.put("¢", "¢");
|
||||
map.put("Χ", "Χ");
|
||||
map.put("χ", "χ");
|
||||
map.put("ˆ", "ˆ");
|
||||
map.put("♣", "♣");
|
||||
map.put("≅", "≅");
|
||||
map.put("©", "©");
|
||||
map.put("↵", "↵");
|
||||
map.put("∪", "∪");
|
||||
map.put("¤", "¤");
|
||||
map.put("†", "†");
|
||||
map.put("‡", "‡");
|
||||
map.put("↓", "↓");
|
||||
map.put("⇓", "⇓");
|
||||
map.put("°", "°");
|
||||
map.put("Δ", "Δ");
|
||||
map.put("δ", "δ");
|
||||
map.put("♦", "♦");
|
||||
map.put("÷", "÷");
|
||||
map.put("É", "É");
|
||||
map.put("é", "é");
|
||||
map.put("Ê", "Ê");
|
||||
map.put("ê", "ê");
|
||||
map.put("È", "È");
|
||||
map.put("è", "è");
|
||||
map.put("∅", "∅");
|
||||
map.put(" ", " ");
|
||||
map.put(" ", " ");
|
||||
map.put("Ε", "Ε");
|
||||
map.put("ε", "ε");
|
||||
map.put("≡", "≡");
|
||||
map.put("Η", "Η");
|
||||
map.put("η", "η");
|
||||
map.put("Ð", "Ð");
|
||||
map.put("ð", "ð");
|
||||
map.put("Ë", "Ë");
|
||||
map.put("ë", "ë");
|
||||
map.put("€", "€");
|
||||
map.put("∃", "∃");
|
||||
map.put("ƒ", "ƒ");
|
||||
map.put("∀", "∀");
|
||||
map.put("½", "½");
|
||||
map.put("¼", "¼");
|
||||
map.put("¾", "¾");
|
||||
map.put("⁄", "⁄");
|
||||
map.put("Γ", "Γ");
|
||||
map.put("γ", "γ");
|
||||
map.put("≥", "≥");
|
||||
map.put("↔", "↔");
|
||||
map.put("⇔", "⇔");
|
||||
map.put("♥", "♥");
|
||||
map.put("…", "…");
|
||||
map.put("Í", "Í");
|
||||
map.put("í", "í");
|
||||
map.put("Î", "Î");
|
||||
map.put("î", "î");
|
||||
map.put("¡", "¡");
|
||||
map.put("Ì", "Ì");
|
||||
map.put("ì", "ì");
|
||||
map.put("ℑ", "ℑ");
|
||||
map.put("∞", "∞");
|
||||
map.put("∫", "∫");
|
||||
map.put("Ι", "Ι");
|
||||
map.put("ι", "ι");
|
||||
map.put("¿", "¿");
|
||||
map.put("∈", "∈");
|
||||
map.put("Ï", "Ï");
|
||||
map.put("ï", "ï");
|
||||
map.put("Κ", "Κ");
|
||||
map.put("κ", "κ");
|
||||
map.put("Λ", "Λ");
|
||||
map.put("λ", "λ");
|
||||
map.put("⟨", "〈");
|
||||
map.put("«", "«");
|
||||
map.put("←", "←");
|
||||
map.put("⇐", "⇐");
|
||||
map.put("⌈", "⌈");
|
||||
map.put("“", "“");
|
||||
map.put("≤", "≤");
|
||||
map.put("⌊", "⌊");
|
||||
map.put("∗", "∗");
|
||||
map.put("◊", "◊");
|
||||
map.put("‎", "‎");
|
||||
map.put("‹", "‹");
|
||||
map.put("‘", "‘");
|
||||
map.put("¯", "¯");
|
||||
map.put("—", "—");
|
||||
map.put("µ", "µ");
|
||||
map.put("·", "·");
|
||||
map.put("−", "−");
|
||||
map.put("Μ", "Μ");
|
||||
map.put("μ", "μ");
|
||||
map.put("∇", "∇");
|
||||
map.put(" ", " ");
|
||||
map.put("–", "–");
|
||||
map.put("≠", "≠");
|
||||
map.put("∋", "∋");
|
||||
map.put("¬", "¬");
|
||||
map.put("∉", "∉");
|
||||
map.put("⊄", "⊄");
|
||||
map.put("Ñ", "Ñ");
|
||||
map.put("ñ", "ñ");
|
||||
map.put("Ν", "Ν");
|
||||
map.put("ν", "ν");
|
||||
map.put("Ó", "Ó");
|
||||
map.put("ó", "ó");
|
||||
map.put("Ô", "Ô");
|
||||
map.put("ô", "ô");
|
||||
map.put("Œ", "Œ");
|
||||
map.put("œ", "œ");
|
||||
map.put("Ò", "Ò");
|
||||
map.put("ò", "ò");
|
||||
map.put("‾", "‾");
|
||||
map.put("Ω", "Ω");
|
||||
map.put("ω", "ω");
|
||||
map.put("Ο", "Ο");
|
||||
map.put("ο", "ο");
|
||||
map.put("⊕", "⊕");
|
||||
map.put("∨", "∨");
|
||||
map.put("ª", "ª");
|
||||
map.put("º", "º");
|
||||
map.put("Ø", "Ø");
|
||||
map.put("ø", "ø");
|
||||
map.put("Õ", "Õ");
|
||||
map.put("õ", "õ");
|
||||
map.put("⊗", "⊗");
|
||||
map.put("Ö", "Ö");
|
||||
map.put("ö", "ö");
|
||||
map.put("¶", "¶");
|
||||
map.put("∂", "∂");
|
||||
map.put("‰", "‰");
|
||||
map.put("⊥", "⊥");
|
||||
map.put("Φ", "Φ");
|
||||
map.put("φ", "φ");
|
||||
map.put("Π", "Π");
|
||||
map.put("π", "π");
|
||||
map.put("ϖ", "ϖ");
|
||||
map.put("±", "±");
|
||||
map.put("£", "£");
|
||||
map.put("′", "′");
|
||||
map.put("″", "″");
|
||||
map.put("∏", "∏");
|
||||
map.put("∝", "∝");
|
||||
map.put("Ψ", "Ψ");
|
||||
map.put("ψ", "ψ");
|
||||
map.put(""", """);
|
||||
map.put("√", "√");
|
||||
map.put("⟩", "〉");
|
||||
map.put("»", "»");
|
||||
map.put("→", "→");
|
||||
map.put("⇒", "⇒");
|
||||
map.put("⌉", "⌉");
|
||||
map.put("”", "”");
|
||||
map.put("ℜ", "ℜ");
|
||||
map.put("®", "®");
|
||||
map.put("⌋", "⌋");
|
||||
map.put("Ρ", "Ρ");
|
||||
map.put("ρ", "ρ");
|
||||
map.put("‏", "‏");
|
||||
map.put("›", "›");
|
||||
map.put("’", "’");
|
||||
map.put("‚", "‚");
|
||||
map.put("Š", "Š");
|
||||
map.put("š", "š");
|
||||
map.put("⋅", "⋅");
|
||||
map.put("§", "§");
|
||||
map.put("­", "­");
|
||||
map.put("Σ", "Σ");
|
||||
map.put("σ", "σ");
|
||||
map.put("ς", "ς");
|
||||
map.put("∼", "∼");
|
||||
map.put("♠", "♠");
|
||||
map.put("⊂", "⊂");
|
||||
map.put("⊆", "⊆");
|
||||
map.put("∑", "∑");
|
||||
map.put("¹", "¹");
|
||||
map.put("²", "²");
|
||||
map.put("³", "³");
|
||||
map.put("⊃", "⊃");
|
||||
map.put("⊇", "⊇");
|
||||
map.put("ß", "ß");
|
||||
map.put("Τ", "Τ");
|
||||
map.put("τ", "τ");
|
||||
map.put("∴", "∴");
|
||||
map.put("Θ", "Θ");
|
||||
map.put("θ", "θ");
|
||||
map.put("ϑ", "ϑ");
|
||||
map.put(" ", " ");
|
||||
map.put("Þ", "Þ");
|
||||
map.put("þ", "þ");
|
||||
map.put("˜", "˜");
|
||||
map.put("×", "×");
|
||||
map.put("™", "™");
|
||||
map.put("Ú", "Ú");
|
||||
map.put("ú", "ú");
|
||||
map.put("↑", "↑");
|
||||
map.put("⇑", "⇑");
|
||||
map.put("Û", "Û");
|
||||
map.put("û", "û");
|
||||
map.put("Ù", "Ù");
|
||||
map.put("ù", "ù");
|
||||
map.put("¨", "¨");
|
||||
map.put("ϒ", "ϒ");
|
||||
map.put("Υ", "Υ");
|
||||
map.put("υ", "υ");
|
||||
map.put("Ü", "Ü");
|
||||
map.put("ü", "ü");
|
||||
map.put("℘", "℘");
|
||||
map.put("Ξ", "Ξ");
|
||||
map.put("ξ", "ξ");
|
||||
map.put("Ý", "Ý");
|
||||
map.put("ý", "ý");
|
||||
map.put("¥", "¥");
|
||||
map.put("ÿ", "ÿ");
|
||||
map.put("Ÿ", "Ÿ");
|
||||
map.put("Ζ", "Ζ");
|
||||
map.put("ζ", "ζ");
|
||||
map.put("‍", "‍");
|
||||
map.put("‌", "‌");
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -55,5 +55,8 @@ public class FeedEntry extends AbstractModel {
|
||||
|
||||
@OneToMany(mappedBy = "entry", cascade = CascadeType.REMOVE)
|
||||
private Set<FeedEntryStatus> statuses;
|
||||
|
||||
@OneToMany(mappedBy = "entry", cascade = CascadeType.REMOVE)
|
||||
private Set<FeedEntryTag> tags;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
@@ -8,6 +9,8 @@ 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;
|
||||
@@ -19,6 +22,8 @@ import lombok.Setter;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEEDENTRYSTATUSES")
|
||||
@SuppressWarnings("serial")
|
||||
@@ -26,6 +31,7 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@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)
|
||||
@@ -42,6 +48,9 @@ public class FeedEntryStatus extends AbstractModel {
|
||||
|
||||
@Transient
|
||||
private boolean markable;
|
||||
|
||||
@Transient
|
||||
private List<FeedEntryTag> tags = Lists.newArrayList();
|
||||
|
||||
/**
|
||||
* Denormalization starts here
|
||||
|
||||
46
src/main/java/com/commafeed/backend/model/FeedEntryTag.java
Normal file
46
src/main/java/com/commafeed/backend/model/FeedEntryTag.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEEDENTRYTAGS")
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class FeedEntryTag extends AbstractModel {
|
||||
|
||||
@JoinColumn(name = "user_id")
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private User user;
|
||||
|
||||
@JoinColumn(name = "entry_id")
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private FeedEntry entry;
|
||||
|
||||
@Column(name = "name", length = 40)
|
||||
private String name;
|
||||
|
||||
public FeedEntryTag() {
|
||||
}
|
||||
|
||||
public FeedEntryTag(User user, FeedEntry entry, String name) {
|
||||
this.name = name;
|
||||
this.entry = entry;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public class User extends AbstractModel {
|
||||
|
||||
@Column(length = 32, nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
|
||||
@Column(length = 255, unique = true)
|
||||
private String email;
|
||||
|
||||
@@ -66,4 +66,8 @@ public class User extends AbstractModel {
|
||||
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
|
||||
private Set<FeedSubscription> subscriptions;
|
||||
|
||||
@Column(name = "last_full_refresh")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastFullRefresh;
|
||||
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ public class UserSettings extends AbstractModel {
|
||||
|
||||
private boolean showRead;
|
||||
private boolean scrollMarks;
|
||||
private boolean socialButtons;
|
||||
|
||||
@Column(length = 32)
|
||||
private String theme;
|
||||
@@ -68,4 +67,18 @@ public class UserSettings extends AbstractModel {
|
||||
@Column(length = Integer.MAX_VALUE)
|
||||
private String customCss;
|
||||
|
||||
@Column(name = "scroll_speed")
|
||||
private int scrollSpeed;
|
||||
|
||||
private boolean email;
|
||||
private boolean gmail;
|
||||
private boolean facebook;
|
||||
private boolean twitter;
|
||||
private boolean googleplus;
|
||||
private boolean tumblr;
|
||||
private boolean pocket;
|
||||
private boolean instapaper;
|
||||
private boolean buffer;
|
||||
private boolean readability;
|
||||
|
||||
}
|
||||
|
||||
@@ -34,9 +34,14 @@ public class OPMLExporter {
|
||||
List<FeedCategory> categories = feedCategoryDAO.findAll(user);
|
||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findAll(user);
|
||||
|
||||
// export root categories
|
||||
for (FeedCategory cat : categories) {
|
||||
opml.getOutlines().add(buildCategoryOutline(cat, subscriptions));
|
||||
if (cat.getParent() == null) {
|
||||
opml.getOutlines().add(buildCategoryOutline(cat, subscriptions));
|
||||
}
|
||||
}
|
||||
|
||||
// export root subscriptions
|
||||
for (FeedSubscription sub : subscriptions) {
|
||||
if (sub.getCategory() == null) {
|
||||
opml.getOutlines().add(buildSubscriptionOutline(sub));
|
||||
|
||||
@@ -9,13 +9,14 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.wicket.util.io.IOUtils;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.feeds.FeedRefreshTaskGiver;
|
||||
@@ -67,10 +68,11 @@ public class SubscriptionHandler {
|
||||
post.setHeader(HttpHeaders.USER_AGENT, "CommaFeed");
|
||||
post.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
HttpClient client = HttpGetter.newClient(20000);
|
||||
CloseableHttpClient client = HttpGetter.newClient(20000);
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
post.setEntity(new UrlEncodedFormEntity(nvp));
|
||||
HttpResponse response = client.execute(post);
|
||||
response = client.execute(post);
|
||||
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
if (code != 204 && code != 202 && code != 200) {
|
||||
@@ -90,7 +92,8 @@ public class SubscriptionHandler {
|
||||
} catch (Exception e) {
|
||||
log.error("Could not subscribe to {} for {} : " + e.getMessage(), hub, topic);
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
IOUtils.closeQuietly(response);
|
||||
IOUtils.closeQuietly(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
|
||||
@@ -26,6 +27,8 @@ import com.commafeed.backend.model.FeedSubscription;
|
||||
@Slf4j
|
||||
public class DatabaseCleaningService {
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
@Inject
|
||||
FeedDAO feedDAO;
|
||||
|
||||
@@ -43,13 +46,28 @@ public class DatabaseCleaningService {
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
public long cleanEntriesWithoutSubscriptions() {
|
||||
log.info("cleaning entries without subscriptions");
|
||||
long total = 0;
|
||||
int deleted = 0;
|
||||
do {
|
||||
List<FeedEntry> entries = feedEntryDAO.findWithoutSubscriptions(BATCH_SIZE);
|
||||
deleted = feedEntryDAO.delete(entries);
|
||||
total += deleted;
|
||||
log.info("removed {} entries without subscriptions", total);
|
||||
} while (deleted != 0);
|
||||
log.info("cleanup done: {} entries without subscriptions deleted", total);
|
||||
return total;
|
||||
}
|
||||
|
||||
public long cleanFeedsWithoutSubscriptions() {
|
||||
|
||||
log.info("cleaning feeds without subscriptions");
|
||||
long total = 0;
|
||||
int deleted = -1;
|
||||
int deleted = 0;
|
||||
do {
|
||||
deleted = feedDAO.deleteWithoutSubscriptions(10);
|
||||
List<Feed> feeds = feedDAO.findWithoutSubscriptions(BATCH_SIZE);
|
||||
deleted = feedDAO.delete(feeds);
|
||||
total += deleted;
|
||||
log.info("removed {} feeds without subscriptions", total);
|
||||
} while (deleted != 0);
|
||||
@@ -58,15 +76,15 @@ public class DatabaseCleaningService {
|
||||
}
|
||||
|
||||
public long cleanContentsWithoutEntries() {
|
||||
|
||||
log.info("cleaning contents without entries");
|
||||
long total = 0;
|
||||
int deleted = -1;
|
||||
int deleted = 0;
|
||||
do {
|
||||
deleted = feedEntryContentDAO.deleteWithoutEntries(10);
|
||||
deleted = feedEntryContentDAO.deleteWithoutEntries(BATCH_SIZE);
|
||||
total += deleted;
|
||||
log.info("removed {} feeds without subscriptions", total);
|
||||
log.info("removed {} contents without entries", total);
|
||||
} while (deleted != 0);
|
||||
log.info("cleanup done: {} feeds without subscriptions deleted", total);
|
||||
log.info("cleanup done: {} contents without entries deleted", total);
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -75,9 +93,9 @@ public class DatabaseCleaningService {
|
||||
cal.add(Calendar.MINUTE, -1 * (int) unit.toMinutes(value));
|
||||
|
||||
long total = 0;
|
||||
int deleted = -1;
|
||||
int deleted = 0;
|
||||
do {
|
||||
deleted = feedEntryDAO.delete(cal.getTime(), 100);
|
||||
deleted = feedEntryDAO.delete(cal.getTime(), BATCH_SIZE);
|
||||
total += deleted;
|
||||
log.info("removed {} entries", total);
|
||||
} while (deleted != 0);
|
||||
@@ -105,7 +123,7 @@ public class DatabaseCleaningService {
|
||||
long total = 0;
|
||||
List<FeedEntryStatus> list = Collections.emptyList();
|
||||
do {
|
||||
list = feedEntryStatusDAO.getOldStatuses(olderThan, 100);
|
||||
list = feedEntryStatusDAO.getOldStatuses(olderThan, BATCH_SIZE);
|
||||
if (!list.isEmpty()) {
|
||||
feedEntryStatusDAO.delete(list);
|
||||
total += list.size();
|
||||
|
||||
@@ -43,7 +43,7 @@ public class FeedEntryService {
|
||||
return;
|
||||
}
|
||||
|
||||
FeedEntryStatus status = feedEntryStatusDAO.getStatus(sub, entry);
|
||||
FeedEntryStatus status = feedEntryStatusDAO.getStatus(user, sub, entry);
|
||||
if (status.isMarkable()) {
|
||||
status.setRead(read);
|
||||
feedEntryStatusDAO.saveOrUpdate(status);
|
||||
@@ -64,14 +64,14 @@ public class FeedEntryService {
|
||||
return;
|
||||
}
|
||||
|
||||
FeedEntryStatus status = feedEntryStatusDAO.getStatus(sub, entry);
|
||||
FeedEntryStatus status = feedEntryStatusDAO.getStatus(user, sub, entry);
|
||||
status.setStarred(starred);
|
||||
feedEntryStatusDAO.saveOrUpdate(status);
|
||||
}
|
||||
|
||||
public void markSubscriptionEntries(User user, List<FeedSubscription> subscriptions, Date olderThan) {
|
||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO
|
||||
.findBySubscriptions(subscriptions, true, null, null, -1, -1, null, false, false);
|
||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, null, null, -1, -1, null, false,
|
||||
false, null);
|
||||
markList(statuses, olderThan);
|
||||
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
|
||||
cache.invalidateUserRootCategory(user);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.commafeed.backend.services;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryTagDAO;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryTag;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@Stateless
|
||||
public class FeedEntryTagService {
|
||||
|
||||
@Inject
|
||||
FeedEntryDAO feedEntryDAO;
|
||||
|
||||
@Inject
|
||||
FeedEntryTagDAO feedEntryTagDAO;
|
||||
|
||||
public void updateTags(User user, Long entryId, List<String> tagNames) {
|
||||
FeedEntry entry = feedEntryDAO.findById(entryId);
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<FeedEntryTag> tags = feedEntryTagDAO.findByEntry(user, entry);
|
||||
Map<String, FeedEntryTag> tagMap = Maps.uniqueIndex(tags, new Function<FeedEntryTag, String>() {
|
||||
@Override
|
||||
public String apply(FeedEntryTag input) {
|
||||
return input.getName();
|
||||
}
|
||||
});
|
||||
|
||||
List<FeedEntryTag> addList = Lists.newArrayList();
|
||||
List<FeedEntryTag> removeList = Lists.newArrayList();
|
||||
|
||||
for (String tagName : tagNames) {
|
||||
FeedEntryTag tag = tagMap.get(tagName);
|
||||
if (tag == null) {
|
||||
addList.add(new FeedEntryTag(user, entry, tagName));
|
||||
}
|
||||
}
|
||||
|
||||
for (FeedEntryTag tag : tags) {
|
||||
if (!tagNames.contains(tag.getName())) {
|
||||
removeList.add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
feedEntryTagDAO.saveOrUpdate(addList);
|
||||
feedEntryTagDAO.delete(removeList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,11 +95,19 @@ public class FeedSubscriptionService {
|
||||
}
|
||||
}
|
||||
|
||||
public UnreadCount getUnreadCount(FeedSubscription sub) {
|
||||
public void refreshAll(User user) {
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||
for (FeedSubscription sub : subs) {
|
||||
Feed feed = sub.getFeed();
|
||||
taskGiver.add(feed, true);
|
||||
}
|
||||
}
|
||||
|
||||
public UnreadCount getUnreadCount(User user, FeedSubscription sub) {
|
||||
UnreadCount count = cache.getUnreadCount(sub);
|
||||
if (count == null) {
|
||||
log.debug("unread count cache miss for {}", Models.getId(sub));
|
||||
count = feedEntryStatusDAO.getUnreadCount(sub);
|
||||
count = feedEntryStatusDAO.getUnreadCount(user, sub);
|
||||
cache.setUnreadCount(sub, count);
|
||||
}
|
||||
return count;
|
||||
@@ -109,7 +117,7 @@ public class FeedSubscriptionService {
|
||||
Map<Long, UnreadCount> map = Maps.newHashMap();
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||
for (FeedSubscription sub : subs) {
|
||||
map.put(sub.getId(), getUnreadCount(sub));
|
||||
map.put(sub.getId(), getUnreadCount(user, sub));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ public class UserService {
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
@Inject
|
||||
FeedSubscriptionService feedSubscriptionService;
|
||||
|
||||
public User login(String name, String password) {
|
||||
if (name == null || password == null) {
|
||||
return null;
|
||||
@@ -52,10 +55,21 @@ public class UserService {
|
||||
if (authenticated) {
|
||||
Date lastLogin = user.getLastLogin();
|
||||
Date now = new Date();
|
||||
|
||||
boolean saveUser = false;
|
||||
// only update lastLogin field every hour in order to not
|
||||
// invalidate the cache everytime someone logs in
|
||||
if (lastLogin == null || lastLogin.before(DateUtils.addHours(now, -1))) {
|
||||
user.setLastLogin(now);
|
||||
saveUser = true;
|
||||
}
|
||||
if (applicationSettingsService.get().isHeavyLoad()
|
||||
&& (user.getLastFullRefresh() == null || user.getLastFullRefresh().before(DateUtils.addMinutes(now, -30)))) {
|
||||
user.setLastFullRefresh(now);
|
||||
saveUser = true;
|
||||
feedSubscriptionService.refreshAll(user);
|
||||
}
|
||||
if (saveUser) {
|
||||
userDAO.saveOrUpdate(user);
|
||||
}
|
||||
return user;
|
||||
|
||||
@@ -41,4 +41,7 @@ public class Entries implements Serializable {
|
||||
@ApiProperty("list of entries")
|
||||
private List<Entry> entries = Lists.newArrayList();
|
||||
|
||||
@ApiProperty("if true, the unread flag was ignored in the request, all entries are returned regardless of their read status")
|
||||
private boolean ignoredReadStatus;
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.commafeed.frontend.model;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -10,7 +11,9 @@ import com.commafeed.backend.feeds.FeedUtils;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedEntryTag;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sun.syndication.feed.synd.SyndContentImpl;
|
||||
import com.sun.syndication.feed.synd.SyndEntry;
|
||||
import com.sun.syndication.feed.synd.SyndEntryImpl;
|
||||
@@ -43,6 +46,12 @@ public class Entry implements Serializable {
|
||||
entry.setFeedLink(sub.getFeed().getLink());
|
||||
entry.setIconUrl(FeedUtils.getFaviconUrl(sub, publicUrl));
|
||||
|
||||
List<String> tags = Lists.newArrayList();
|
||||
for (FeedEntryTag tag : status.getTags()) {
|
||||
tags.add(tag.getName());
|
||||
}
|
||||
entry.setTags(tags);
|
||||
|
||||
if (content != null) {
|
||||
entry.setRtl(FeedUtils.isRTL(feedEntry));
|
||||
entry.setTitle(content.getTitle());
|
||||
@@ -125,4 +134,7 @@ public class Entry implements Serializable {
|
||||
|
||||
@ApiProperty("wether the entry is still markable (old entry statuses are discarded)")
|
||||
private boolean markable;
|
||||
|
||||
@ApiProperty("tags")
|
||||
private List<String> tags;
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@ public class Settings implements Serializable {
|
||||
@ApiProperty(value = "user wants category and feeds with no unread entries shown", required = true)
|
||||
private boolean showRead;
|
||||
|
||||
@ApiProperty(value = "user wants social buttons (facebook, twitter, ...) shown", required = true)
|
||||
private boolean socialButtons;
|
||||
|
||||
@ApiProperty(value = "In expanded view, scroll through entries mark them as read", required = true)
|
||||
private boolean scrollMarks;
|
||||
|
||||
@@ -38,5 +35,19 @@ public class Settings implements Serializable {
|
||||
|
||||
@ApiProperty(value = "user's custom css for the website")
|
||||
private String customCss;
|
||||
|
||||
@ApiProperty(value = "user's preferred scroll speed when navigating between entries")
|
||||
private int scrollSpeed;
|
||||
|
||||
private boolean email;
|
||||
private boolean gmail;
|
||||
private boolean facebook;
|
||||
private boolean twitter;
|
||||
private boolean googleplus;
|
||||
private boolean tumblr;
|
||||
private boolean pocket;
|
||||
private boolean instapaper;
|
||||
private boolean buffer;
|
||||
private boolean readability;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@ApiClass("Tag Request")
|
||||
@Data
|
||||
public class TagRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "entry id", required = true)
|
||||
private Long entryId;
|
||||
|
||||
@ApiProperty(value = "tags")
|
||||
private List<String> tags;
|
||||
|
||||
}
|
||||
@@ -1,45 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:wicket="http://wicket.apache.org" wicket:id="html">
|
||||
<head>
|
||||
<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" />
|
||||
<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" />
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="app-icon-114.png" />
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="app-icon-144.png" />
|
||||
<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="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
||||
<meta name="application-name" content="CommaFeed" />
|
||||
<meta name="msapplication-navbutton-color" content="#F88A14" />
|
||||
<meta name="msapplication-starturl" content="/" />
|
||||
<meta name="msapplication-square70x70logo" content="metro-icon-70.png"/>
|
||||
<meta name="msapplication-square150x150logo" content="metro-icon-150.png"/>
|
||||
<link rel="fluid-icon" href="app-icon-512.png" title="CommaFeed" />
|
||||
<link rel="logo" type="image/svg" href="app-icon.svg" />
|
||||
<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" />
|
||||
<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" />
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="app-icon-114.png" />
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="app-icon-144.png" />
|
||||
<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="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
||||
<meta name="application-name" content="CommaFeed" />
|
||||
<meta name="msapplication-navbutton-color" content="#F88A14" />
|
||||
<meta name="msapplication-starturl" content="/" />
|
||||
<meta name="msapplication-square70x70logo" content="metro-icon-70.png" />
|
||||
<meta name="msapplication-square150x150logo" content="metro-icon-150.png" />
|
||||
<link rel="fluid-icon" href="app-icon-512.png" title="CommaFeed" />
|
||||
<link rel="logo" type="image/svg" href="app-icon.svg" />
|
||||
</head>
|
||||
<body>
|
||||
<wicket:child />
|
||||
<wicket:container wicket:id="footer-container"/>
|
||||
<wicket:container wicket:id="footer-container" />
|
||||
<wicket:container wicket:id="uservoice">
|
||||
<script>(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/XYpTZZteqS4lHvgrTXeA.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})()</script>
|
||||
<script>
|
||||
(function() {
|
||||
var uv = document.createElement('script');
|
||||
uv.type = 'text/javascript';
|
||||
uv.async = true;
|
||||
uv.src = '//widget.uservoice.com/XYpTZZteqS4lHvgrTXeA.js';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(uv, s);
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
UserVoice = window.UserVoice || [];
|
||||
UserVoice.push(['showTab', 'classic_widget', {
|
||||
mode: 'full',
|
||||
default_mode: 'feedback',
|
||||
primary_color: '#000',
|
||||
link_color: '#007dbf',
|
||||
forum_id: 204509,
|
||||
support_tab_name: 'Contact',
|
||||
feedback_tab_name: 'Feedback',
|
||||
tab_label: 'Feedback',
|
||||
tab_color: '#7e72db',
|
||||
tab_position: 'bottom-right',
|
||||
tab_inverted: false
|
||||
mode : 'full',
|
||||
default_mode : 'feedback',
|
||||
primary_color : '#000',
|
||||
link_color : '#007dbf',
|
||||
forum_id : 204509,
|
||||
support_tab_name : 'Contact',
|
||||
feedback_tab_name : 'Feedback',
|
||||
tab_label : 'Feedback',
|
||||
tab_color : '#7e72db',
|
||||
tab_position : 'bottom-right',
|
||||
tab_inverted : false
|
||||
}]);
|
||||
</script>
|
||||
</wicket:container>
|
||||
|
||||
@@ -54,13 +54,13 @@ public class NextUnreadRedirectPage extends WebPage {
|
||||
List<FeedEntryStatus> statuses = null;
|
||||
if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) {
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||
statuses = feedEntryStatusDAO.findBySubscriptions(subs, true, null, null, 0, 1, order, true, false);
|
||||
statuses = feedEntryStatusDAO.findBySubscriptions(user, subs, true, null, null, 0, 1, order, true, false, null);
|
||||
} else {
|
||||
FeedCategory category = feedCategoryDAO.findById(user, Long.valueOf(categoryId));
|
||||
if (category != null) {
|
||||
List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user, category);
|
||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user, children);
|
||||
statuses = feedEntryStatusDAO.findBySubscriptions(subscriptions, true, null, null, 0, 1, order, true, false);
|
||||
statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, null, null, 0, 1, order, true, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,19 @@
|
||||
<div class="text-center">
|
||||
<img src="images/logo_2.png" />
|
||||
<div wicket:id="feedback"></div>
|
||||
<form wicket:id="form">
|
||||
New Password:
|
||||
<input type="password" wicket:id="password" />
|
||||
<br />
|
||||
Confirm:
|
||||
<input type="password" wicket:id="confirm" />
|
||||
<br />
|
||||
<input type="submit" class="btn btn-primary" value="Submit" />
|
||||
<input type="button" class="btn" wicket:id="cancel" value="Home page" />
|
||||
<form wicket:id="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label>New Password</label>
|
||||
<input type="password" wicket:id="password" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Confirm</label>
|
||||
<input type="password" wicket:id="confirm" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" value="Submit" />
|
||||
<input type="button" class="btn btn-default" wicket:id="cancel" value="Home page" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
<body>
|
||||
<wicket:extend>
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<div class="col-xs-6 col-xs-offset-3 text-center">
|
||||
<img src="images/logo_2.png" />
|
||||
<div wicket:id="feedback"></div>
|
||||
<form wicket:id="form">
|
||||
Email:
|
||||
<input type="email" wicket:id="email" />
|
||||
<br />
|
||||
<input type="submit" class="btn btn-primary" value="Submit" />
|
||||
<input type="button" class="btn" wicket:id="cancel" value="Cancel" />
|
||||
<form wicket:id="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label>Email</label>
|
||||
<input type="email" wicket:id="email" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" value="Submit" />
|
||||
<input type="button" class="btn btn-default" wicket:id="cancel" value="Cancel" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<body>
|
||||
<wicket:extend>
|
||||
<div class="welcome">
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid header">
|
||||
<div class="container">
|
||||
<div class="row header">
|
||||
<div class="pull-left">
|
||||
<a wicket:id="logo-link">
|
||||
<img src="images/logo_2.png"></img>
|
||||
@@ -23,14 +23,14 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="well" id="login-panel">
|
||||
<h3>Login</h3>
|
||||
<span wicket:id="login"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span6" wicket:enclosure="register">
|
||||
<div class="col-md-6" wicket:enclosure="register">
|
||||
<div class="well" id="register-panel">
|
||||
<h3>Register</h3>
|
||||
<span wicket:id="register"></span>
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<hr />
|
||||
<div class="footer">
|
||||
<div class="row-fluid">
|
||||
<div class="row">
|
||||
<span>
|
||||
©
|
||||
<a href="http://www.commafeed.com" target="_blank">CommaFeed</a>
|
||||
|
||||
@@ -4,24 +4,20 @@
|
||||
<wicket:panel>
|
||||
<span wicket:id="feedback"></span>
|
||||
<form wicket:id="signInForm">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="username">User Name</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="username" wicket:id="username" class="input-block-level"></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username">User Name</label>
|
||||
<input type="text" id="username" wicket:id="username" class="form-control"></input>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="password">Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" id="password" wicket:id="password" class="input-block-level"></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" wicket:id="password" class="form-control"></input>
|
||||
</div>
|
||||
<p class="help-block" wicket:id="rememberMeRow">
|
||||
<label class="checkbox">
|
||||
<div wicket:id="rememberMeRow">
|
||||
<label>
|
||||
<input wicket:id="rememberMe" type="checkbox" />
|
||||
Remember me
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" class="btn btn-primary" value="Log in" />
|
||||
<a wicket:id="recover" class="pull-right">Forgot password?</a>
|
||||
|
||||
@@ -4,23 +4,17 @@
|
||||
<wicket:panel>
|
||||
<div wicket:id="feedback"></div>
|
||||
<form wicket:id="form" autocomplete="off">
|
||||
<div class="control-group">
|
||||
<label class="control-label">User Name</label>
|
||||
<div class="controls">
|
||||
<input type="text" wicket:id="name" class="input-block-level"></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>User Name</label>
|
||||
<input type="text" wicket:id="name" class="form-control"></input>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" wicket:id="password" class="input-block-level"></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password</label>
|
||||
<input type="password" wicket:id="password" class="form-control"></input>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Email address (used for password recovery only)</label>
|
||||
<div class="controls">
|
||||
<input type="email" wicket:id="email" class="input-block-level"></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Email address (used for password recovery only)</label>
|
||||
<input type="email" wicket:id="email" class="form-control"></input>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" class="btn btn-primary" value="Register" />
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.commafeed.frontend.resources;
|
||||
|
||||
import ro.isdc.wro.model.resource.processor.impl.css.CssUrlRewritingProcessor;
|
||||
|
||||
public class CustomCssUrlRewritingProcessor extends CssUrlRewritingProcessor {
|
||||
|
||||
/**
|
||||
* ignore webjar image replacements since they won't be available at runtime anyway
|
||||
*/
|
||||
@Override
|
||||
protected String replaceImageUrl(String cssUri, String imageUrl) {
|
||||
if (cssUri.startsWith("webjar:")) {
|
||||
return imageUrl;
|
||||
}
|
||||
return super.replaceImageUrl(cssUri, imageUrl);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public class WroAdditionalProvider implements ProcessorProvider {
|
||||
map.put("sassOnlyProcessor", new SassOnlyProcessor());
|
||||
map.put("sassImport", new SassImportProcessor());
|
||||
map.put("timestamp", new TimestampProcessor());
|
||||
map.put("cssUrlRewriting", new CustomCssUrlRewritingProcessor());
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ public class WroManagerFactory extends ConfigurableWroManagerFactory {
|
||||
map.put("sassOnlyProcessor", new SassOnlyProcessor());
|
||||
map.put("sassImport", new SassImportProcessor());
|
||||
map.put("timestamp", new TimestampProcessor());
|
||||
map.put("cssUrlRewriting", new CustomCssUrlRewritingProcessor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -146,4 +146,5 @@ public abstract class AbstractREST {
|
||||
boolean authorized = roles.hasAnyRole(new Roles(requiredRole.name()));
|
||||
return authorized;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package com.commafeed.frontend.rest.resources;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
@@ -19,14 +15,12 @@ import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.backend.dao.FeedDAO;
|
||||
import com.commafeed.backend.dao.FeedDAO.DuplicateMode;
|
||||
import com.commafeed.backend.dao.UserDAO;
|
||||
import com.commafeed.backend.dao.UserRoleDAO;
|
||||
import com.commafeed.backend.feeds.FeedRefreshTaskGiver;
|
||||
import com.commafeed.backend.feeds.FeedRefreshUpdater;
|
||||
import com.commafeed.backend.feeds.FeedRefreshWorker;
|
||||
import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserRole;
|
||||
import com.commafeed.backend.model.UserRole.Role;
|
||||
@@ -37,13 +31,10 @@ import com.commafeed.backend.services.PasswordEncryptionService;
|
||||
import com.commafeed.backend.services.UserService;
|
||||
import com.commafeed.backend.startup.StartupBean;
|
||||
import com.commafeed.frontend.SecurityCheck;
|
||||
import com.commafeed.frontend.model.FeedCount;
|
||||
import com.commafeed.frontend.model.UserModel;
|
||||
import com.commafeed.frontend.model.request.FeedMergeRequest;
|
||||
import com.commafeed.frontend.model.request.IDRequest;
|
||||
import com.commafeed.frontend.rest.PrettyPrint;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.wordnik.swagger.annotations.Api;
|
||||
@@ -233,9 +224,18 @@ public class AdminREST extends AbstractREST {
|
||||
return Response.ok(metrics).build();
|
||||
}
|
||||
|
||||
@Path("/cleanup/entries")
|
||||
@GET
|
||||
@ApiOperation(value = "Entries cleanup", notes = "Delete entries without subscriptions")
|
||||
public Response cleanupEntries() {
|
||||
Map<String, Long> map = Maps.newHashMap();
|
||||
map.put("entries_without_subscriptions", cleaner.cleanEntriesWithoutSubscriptions());
|
||||
return Response.ok(map).build();
|
||||
}
|
||||
|
||||
@Path("/cleanup/feeds")
|
||||
@GET
|
||||
@ApiOperation(value = "Feeds cleanup", notes = "Delete feeds without subscriptions and entries without feeds")
|
||||
@ApiOperation(value = "Feeds cleanup", notes = "Delete feeds without subscriptions")
|
||||
public Response cleanupFeeds() {
|
||||
Map<String, Long> map = Maps.newHashMap();
|
||||
map.put("feeds_without_subscriptions", cleaner.cleanFeedsWithoutSubscriptions());
|
||||
@@ -251,44 +251,4 @@ public class AdminREST extends AbstractREST {
|
||||
return Response.ok(map).build();
|
||||
}
|
||||
|
||||
@Path("/cleanup/entries")
|
||||
@GET
|
||||
@ApiOperation(value = "Entries cleanup", notes = "Delete entries older than given date")
|
||||
public Response cleanupEntries(@QueryParam("days") @DefaultValue("30") int days) {
|
||||
Map<String, Long> map = Maps.newHashMap();
|
||||
map.put("old_entries", cleaner.cleanEntriesOlderThan(days, TimeUnit.DAYS));
|
||||
return Response.ok(map).build();
|
||||
}
|
||||
|
||||
@Path("/cleanup/findDuplicateFeeds")
|
||||
@GET
|
||||
@ApiOperation(value = "Find duplicate feeds")
|
||||
public Response findDuplicateFeeds(@QueryParam("mode") DuplicateMode mode, @QueryParam("page") int page,
|
||||
@QueryParam("limit") int limit, @QueryParam("minCount") long minCount) {
|
||||
List<FeedCount> list = feedDAO.findDuplicates(mode, limit * page, limit, minCount);
|
||||
return Response.ok(list).build();
|
||||
}
|
||||
|
||||
@Path("/cleanup/merge")
|
||||
@POST
|
||||
@ApiOperation(value = "Merge feeds", notes = "Merge feeds together")
|
||||
public Response mergeFeeds(@ApiParam(required = true) FeedMergeRequest request) {
|
||||
Feed into = feedDAO.findById(request.getIntoFeedId());
|
||||
if (into == null) {
|
||||
return Response.status(Status.BAD_REQUEST).entity("'into feed' not found").build();
|
||||
}
|
||||
|
||||
List<Feed> feeds = Lists.newArrayList();
|
||||
for (Long feedId : request.getFeedIds()) {
|
||||
Feed feed = feedDAO.findById(feedId);
|
||||
feeds.add(feed);
|
||||
}
|
||||
|
||||
if (feeds.isEmpty()) {
|
||||
return Response.status(Status.BAD_REQUEST).entity("'from feeds' empty").build();
|
||||
}
|
||||
|
||||
cleaner.mergeFeeds(into, feeds);
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ import javax.ws.rs.core.Response.Status;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
@@ -106,7 +106,8 @@ public class CategoryREST extends AbstractREST {
|
||||
@ApiParam(
|
||||
value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords,
|
||||
@ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds,
|
||||
@ApiParam(value = "comma-separated list of excluded subscription ids") @QueryParam("excludedSubscriptionIds") String excludedSubscriptionIds) {
|
||||
@ApiParam(value = "comma-separated list of excluded subscription ids") @QueryParam("excludedSubscriptionIds") String excludedSubscriptionIds,
|
||||
@ApiParam(value = "keep only entries tagged with this tag") @QueryParam("tag") String tag) {
|
||||
|
||||
Preconditions.checkNotNull(readType);
|
||||
|
||||
@@ -135,11 +136,11 @@ public class CategoryREST extends AbstractREST {
|
||||
}
|
||||
|
||||
if (ALL.equals(id)) {
|
||||
entries.setName("All");
|
||||
entries.setName(ObjectUtils.defaultIfNull(tag, "All"));
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(getUser());
|
||||
removeExcludedSubscriptions(subs, excludedIds);
|
||||
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(subs, unreadOnly, keywords, newerThanDate, offset,
|
||||
limit + 1, order, true, onlyIds);
|
||||
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(getUser(), subs, unreadOnly, keywords, newerThanDate,
|
||||
offset, limit + 1, order, true, onlyIds, tag);
|
||||
|
||||
for (FeedEntryStatus status : list) {
|
||||
entries.getEntries().add(
|
||||
@@ -161,8 +162,8 @@ public class CategoryREST extends AbstractREST {
|
||||
List<FeedCategory> categories = feedCategoryDAO.findAllChildrenCategories(getUser(), parent);
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findByCategories(getUser(), categories);
|
||||
removeExcludedSubscriptions(subs, excludedIds);
|
||||
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(subs, unreadOnly, keywords, newerThanDate, offset,
|
||||
limit + 1, order, true, onlyIds);
|
||||
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(getUser(), subs, unreadOnly, keywords, newerThanDate,
|
||||
offset, limit + 1, order, true, onlyIds, tag);
|
||||
|
||||
for (FeedEntryStatus status : list) {
|
||||
entries.getEntries().add(
|
||||
@@ -182,6 +183,7 @@ public class CategoryREST extends AbstractREST {
|
||||
}
|
||||
|
||||
entries.setTimestamp(System.currentTimeMillis());
|
||||
entries.setIgnoredReadStatus(STARRED.equals(id) || keywords != null || tag != null);
|
||||
FeedUtils.removeUnwantedFromSearch(entries.getEntries(), keywords);
|
||||
return Response.ok(entries).build();
|
||||
}
|
||||
@@ -192,16 +194,20 @@ public class CategoryREST extends AbstractREST {
|
||||
@Produces(MediaType.APPLICATION_XML)
|
||||
@SecurityCheck(value = Role.USER, apiKeyAllowed = true)
|
||||
public Response getCategoryEntriesAsFeed(
|
||||
@ApiParam(value = "id of the category, 'all' or 'starred'", required = true) @QueryParam("id") String id) {
|
||||
@ApiParam(value = "id of the category, 'all' or 'starred'", required = true) @QueryParam("id") String id,
|
||||
@ApiParam(value = "all entries or only unread ones", allowableValues = "all,unread", required = true) @DefaultValue("all") @QueryParam("readType") ReadingMode readType,
|
||||
@ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan,
|
||||
@ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset,
|
||||
@ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit,
|
||||
@ApiParam(value = "date ordering", allowableValues = "asc,desc") @QueryParam("order") @DefaultValue("desc") ReadingOrder order,
|
||||
@ApiParam(
|
||||
value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords,
|
||||
@ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds,
|
||||
@ApiParam(value = "comma-separated list of excluded subscription ids") @QueryParam("excludedSubscriptionIds") String excludedSubscriptionIds,
|
||||
@ApiParam(value = "keep only entries tagged with this tag") @QueryParam("tag") String tag) {
|
||||
|
||||
Preconditions.checkNotNull(id);
|
||||
|
||||
ReadingMode readType = ReadingMode.all;
|
||||
ReadingOrder order = ReadingOrder.desc;
|
||||
int offset = 0;
|
||||
int limit = 20;
|
||||
|
||||
Response response = getCategoryEntries(id, readType, null, offset, limit, order, null, false, null);
|
||||
Response response = getCategoryEntries(id, readType, newerThan, offset, limit, order, keywords, onlyIds, excludedSubscriptionIds,
|
||||
tag);
|
||||
if (response.getStatus() != Status.OK.getStatusCode()) {
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
package com.commafeed.frontend.rest.resources;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryTagDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.backend.services.FeedEntryService;
|
||||
import com.commafeed.backend.services.FeedEntryTagService;
|
||||
import com.commafeed.frontend.model.request.MarkRequest;
|
||||
import com.commafeed.frontend.model.request.MultipleMarkRequest;
|
||||
import com.commafeed.frontend.model.request.StarRequest;
|
||||
import com.commafeed.frontend.model.request.TagRequest;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.wordnik.swagger.annotations.Api;
|
||||
import com.wordnik.swagger.annotations.ApiOperation;
|
||||
@@ -30,6 +36,12 @@ public class EntryREST extends AbstractREST {
|
||||
@Inject
|
||||
FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
|
||||
@Inject
|
||||
FeedEntryTagDAO feedEntryTagDAO;
|
||||
|
||||
@Inject
|
||||
FeedEntryTagService feedEntryTagService;
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
@@ -71,4 +83,24 @@ public class EntryREST extends AbstractREST {
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@Path("/tags")
|
||||
@GET
|
||||
@ApiOperation(value = "Get list of tags for the user", notes = "Get list of tags for the user")
|
||||
public Response getTags() {
|
||||
List<String> tags = feedEntryTagDAO.findByUser(getUser());
|
||||
return Response.ok(tags).build();
|
||||
}
|
||||
|
||||
@Path("/tag")
|
||||
@POST
|
||||
@ApiOperation(value = "Mark a feed entry", notes = "Mark a feed entry as read/unread")
|
||||
public Response tagFeedEntry(@ApiParam(value = "Tag Request", required = true) TagRequest req) {
|
||||
Preconditions.checkNotNull(req);
|
||||
Preconditions.checkNotNull(req.getEntryId());
|
||||
|
||||
feedEntryTagService.updateTags(getUser(), req.getEntryId(), req.getTags());
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ import com.commafeed.frontend.model.request.IDRequest;
|
||||
import com.commafeed.frontend.model.request.MarkRequest;
|
||||
import com.commafeed.frontend.model.request.SubscribeRequest;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sun.syndication.feed.opml.Opml;
|
||||
import com.sun.syndication.feed.synd.SyndEntry;
|
||||
@@ -167,8 +168,8 @@ public class FeedREST extends AbstractREST {
|
||||
entries.setErrorCount(subscription.getFeed().getErrorCount());
|
||||
entries.setFeedLink(subscription.getFeed().getLink());
|
||||
|
||||
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(Arrays.asList(subscription), unreadOnly, keywords,
|
||||
newerThanDate, offset, limit + 1, order, true, onlyIds);
|
||||
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(getUser(), Arrays.asList(subscription), unreadOnly,
|
||||
keywords, newerThanDate, offset, limit + 1, order, true, onlyIds, null);
|
||||
|
||||
for (FeedEntryStatus status : list) {
|
||||
entries.getEntries().add(
|
||||
@@ -186,6 +187,7 @@ public class FeedREST extends AbstractREST {
|
||||
}
|
||||
|
||||
entries.setTimestamp(System.currentTimeMillis());
|
||||
entries.setIgnoredReadStatus(keywords != null);
|
||||
FeedUtils.removeUnwantedFromSearch(entries.getEntries(), keywords);
|
||||
return Response.ok(entries).build();
|
||||
}
|
||||
@@ -195,16 +197,18 @@ public class FeedREST extends AbstractREST {
|
||||
@ApiOperation(value = "Get feed entries as a feed", notes = "Get a feed of feed entries")
|
||||
@Produces(MediaType.APPLICATION_XML)
|
||||
@SecurityCheck(value = Role.USER, apiKeyAllowed = true)
|
||||
public Response getFeedEntriesAsFeed(@ApiParam(value = "id of the feed", required = true) @QueryParam("id") String id) {
|
||||
public Response getFeedEntriesAsFeed(
|
||||
@ApiParam(value = "id of the feed", required = true) @QueryParam("id") String id,
|
||||
@ApiParam(value = "all entries or only unread ones", allowableValues = "all,unread", required = true) @DefaultValue("all") @QueryParam("readType") ReadingMode readType,
|
||||
@ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan,
|
||||
@ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset,
|
||||
@ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit,
|
||||
@ApiParam(value = "date ordering", allowableValues = "asc,desc") @QueryParam("order") @DefaultValue("desc") ReadingOrder order,
|
||||
@ApiParam(
|
||||
value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords,
|
||||
@ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds) {
|
||||
|
||||
Preconditions.checkNotNull(id);
|
||||
|
||||
ReadingMode readType = ReadingMode.all;
|
||||
ReadingOrder order = ReadingOrder.desc;
|
||||
int offset = 0;
|
||||
int limit = 20;
|
||||
|
||||
Response response = getFeedEntries(id, readType, null, offset, limit, order, null, false);
|
||||
Response response = getFeedEntries(id, readType, newerThan, offset, limit, order, keywords, onlyIds);
|
||||
if (response.getStatus() != Status.OK.getStatusCode()) {
|
||||
return response;
|
||||
}
|
||||
@@ -262,7 +266,8 @@ public class FeedREST extends AbstractREST {
|
||||
try {
|
||||
info = fetchFeedInternal(req.getUrl());
|
||||
} catch (Exception e) {
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(Throwables.getStackTraceAsString(Throwables.getRootCause(e)))
|
||||
.build();
|
||||
}
|
||||
return Response.ok(info).build();
|
||||
}
|
||||
@@ -271,11 +276,7 @@ public class FeedREST extends AbstractREST {
|
||||
@GET
|
||||
@ApiOperation(value = "Queue all feeds of the user for refresh", notes = "Manually add all feeds of the user to the refresh queue")
|
||||
public Response queueAllForRefresh() {
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(getUser());
|
||||
for (FeedSubscription sub : subs) {
|
||||
Feed feed = sub.getFeed();
|
||||
taskGiver.add(feed, true);
|
||||
}
|
||||
feedSubscriptionService.refreshAll(getUser());
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@@ -374,8 +375,10 @@ public class FeedREST extends AbstractREST {
|
||||
try {
|
||||
url = fetchFeedInternal(url).getUrl();
|
||||
|
||||
FeedCategory category = CategoryREST.ALL.equals(req.getCategoryId()) ? null : feedCategoryDAO.findById(Long.valueOf(req
|
||||
.getCategoryId()));
|
||||
FeedCategory category = null;
|
||||
if (req.getCategoryId() != null && !CategoryREST.ALL.equals(req.getCategoryId())) {
|
||||
category = feedCategoryDAO.findById(Long.valueOf(req.getCategoryId()));
|
||||
}
|
||||
FeedInfo info = fetchFeedInternal(url);
|
||||
feedSubscriptionService.subscribe(getUser(), info.getUrl(), req.getTitle(), category);
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -74,20 +74,44 @@ public class UserREST extends AbstractREST {
|
||||
s.setReadingOrder(settings.getReadingOrder().name());
|
||||
s.setViewMode(settings.getViewMode().name());
|
||||
s.setShowRead(settings.isShowRead());
|
||||
s.setSocialButtons(settings.isSocialButtons());
|
||||
|
||||
s.setEmail(settings.isEmail());
|
||||
s.setGmail(settings.isGmail());
|
||||
s.setFacebook(settings.isFacebook());
|
||||
s.setTwitter(settings.isTwitter());
|
||||
s.setGoogleplus(settings.isGoogleplus());
|
||||
s.setTumblr(settings.isTumblr());
|
||||
s.setPocket(settings.isPocket());
|
||||
s.setInstapaper(settings.isInstapaper());
|
||||
s.setBuffer(settings.isBuffer());
|
||||
s.setReadability(settings.isReadability());
|
||||
|
||||
s.setScrollMarks(settings.isScrollMarks());
|
||||
s.setTheme(settings.getTheme());
|
||||
s.setCustomCss(settings.getCustomCss());
|
||||
s.setLanguage(settings.getLanguage());
|
||||
s.setScrollSpeed(settings.getScrollSpeed());
|
||||
} else {
|
||||
s.setReadingMode(ReadingMode.unread.name());
|
||||
s.setReadingOrder(ReadingOrder.desc.name());
|
||||
s.setViewMode(ViewMode.title.name());
|
||||
s.setShowRead(true);
|
||||
s.setTheme("default");
|
||||
s.setSocialButtons(true);
|
||||
|
||||
s.setEmail(true);
|
||||
s.setGmail(true);
|
||||
s.setFacebook(true);
|
||||
s.setTwitter(true);
|
||||
s.setGoogleplus(true);
|
||||
s.setTumblr(true);
|
||||
s.setPocket(true);
|
||||
s.setInstapaper(true);
|
||||
s.setBuffer(true);
|
||||
s.setReadability(true);
|
||||
|
||||
s.setScrollMarks(true);
|
||||
s.setLanguage("en");
|
||||
s.setScrollSpeed(400);
|
||||
}
|
||||
return Response.ok(s).build();
|
||||
}
|
||||
@@ -114,8 +138,20 @@ public class UserREST extends AbstractREST {
|
||||
s.setScrollMarks(settings.isScrollMarks());
|
||||
s.setTheme(settings.getTheme());
|
||||
s.setCustomCss(settings.getCustomCss());
|
||||
s.setSocialButtons(settings.isSocialButtons());
|
||||
s.setLanguage(settings.getLanguage());
|
||||
s.setScrollSpeed(settings.getScrollSpeed());
|
||||
|
||||
s.setEmail(settings.isEmail());
|
||||
s.setGmail(settings.isGmail());
|
||||
s.setFacebook(settings.isFacebook());
|
||||
s.setTwitter(settings.isTwitter());
|
||||
s.setGoogleplus(settings.isGoogleplus());
|
||||
s.setTumblr(settings.isTumblr());
|
||||
s.setPocket(settings.isPocket());
|
||||
s.setInstapaper(settings.isInstapaper());
|
||||
s.setBuffer(settings.isBuffer());
|
||||
s.setReadability(settings.isReadability());
|
||||
|
||||
userSettingsDAO.saveOrUpdate(s);
|
||||
return Response.ok().build();
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package liquibase.integration.cdi;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.enterprise.event.Observes;
|
||||
import javax.enterprise.inject.Produces;
|
||||
import javax.enterprise.inject.spi.AfterBeanDiscovery;
|
||||
import javax.enterprise.inject.spi.AfterDeploymentValidation;
|
||||
import javax.enterprise.inject.spi.BeanManager;
|
||||
import javax.enterprise.inject.spi.Extension;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import liquibase.integration.cdi.annotations.LiquibaseType;
|
||||
import liquibase.resource.ResourceAccessor;
|
||||
|
||||
/**
|
||||
* temporary fix until https://liquibase.jira.com/browse/CORE-1325 is fixed
|
||||
*/
|
||||
public class CDIBootstrap implements Extension {
|
||||
|
||||
void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager bm) {
|
||||
}
|
||||
|
||||
void afterDeploymentValidation(@Observes AfterDeploymentValidation event, BeanManager manager) {
|
||||
}
|
||||
|
||||
@Produces
|
||||
@LiquibaseType
|
||||
public CDILiquibaseConfig createConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Produces
|
||||
@LiquibaseType
|
||||
public DataSource createDataSource() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Produces
|
||||
@LiquibaseType
|
||||
public ResourceAccessor create() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<entity-mappings version="2.0"
|
||||
xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://java.sun.com/xml/ns/persistence/orm
|
||||
http://java.sun.com/xml/ns/persistence/orm_2_0.xsd">
|
||||
|
||||
<named-query name="Statuses.deleteOld">
|
||||
<query>delete from FeedEntryStatus s where s.entryInserted < :date and s.starred = false</query>
|
||||
</named-query>
|
||||
|
||||
</entity-mappings>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.0"
|
||||
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://java.sun.com/xml/ns/persistence
|
||||
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
|
||||
@@ -9,6 +8,7 @@
|
||||
<jta-data-source>${jpa.datasource.name}</jta-data-source>
|
||||
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="openejb.jpa.auto-scan" value="true" />
|
||||
|
||||
<property name="format_sql" value="true" />
|
||||
<property name="use_sql_comments" value="true" />
|
||||
@@ -22,26 +22,17 @@
|
||||
|
||||
<property name="hibernate.generate_statistics" value="true" />
|
||||
|
||||
<property name="hibernate.cache.use_second_level_cache"
|
||||
value="${jpa.cache}" />
|
||||
<property name="hibernate.cache.use_second_level_cache" value="${jpa.cache}" />
|
||||
<property name="hibernate.cache.use_query_cache" value="${jpa.cache}" />
|
||||
|
||||
<property name="hibernate.cache.region.factory_class"
|
||||
value="org.hibernate.cache.infinispan.InfinispanRegionFactory" />
|
||||
<property name="hibernate.cache.infinispan.statistics"
|
||||
value="true" />
|
||||
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.infinispan.InfinispanRegionFactory" />
|
||||
<property name="hibernate.cache.infinispan.statistics" value="true" />
|
||||
|
||||
<property name="hibernate.cache.infinispan.entity.eviction.strategy"
|
||||
value="LRU" />
|
||||
<property
|
||||
name="hibernate.cache.infinispan.entity.eviction.wake_up_interval"
|
||||
value="2000" />
|
||||
<property name="hibernate.cache.infinispan.entity.eviction.max_entries"
|
||||
value="10000" />
|
||||
<property name="hibernate.cache.infinispan.entity.expiration.lifespan"
|
||||
value="60000" />
|
||||
<property name="hibernate.cache.infinispan.entity.expiration.max_idle"
|
||||
value="30000" />
|
||||
<property name="hibernate.cache.infinispan.entity.eviction.strategy" value="LRU" />
|
||||
<property name="hibernate.cache.infinispan.entity.eviction.wake_up_interval" value="2000" />
|
||||
<property name="hibernate.cache.infinispan.entity.eviction.max_entries" value="10000" />
|
||||
<property name="hibernate.cache.infinispan.entity.expiration.lifespan" value="60000" />
|
||||
<property name="hibernate.cache.infinispan.entity.expiration.max_idle" value="30000" />
|
||||
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
@@ -357,6 +357,7 @@
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="athou" id="status-cleanup">
|
||||
<validCheckSum>7:cf40ae235c2d4086c5fa6ac64102c6a9</validCheckSum>
|
||||
<delete tableName="FEEDENTRYSTATUSES">
|
||||
<where>read_status = false and starred = false</where>
|
||||
</delete>
|
||||
|
||||
51
src/main/resources/changelogs/db.changelog-1.4.xml
Normal file
51
src/main/resources/changelogs/db.changelog-1.4.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd">
|
||||
|
||||
<changeSet author="athou" id="scroll-speed">
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="scroll_speed" type="BIGINT" />
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
<changeSet author="athou" id="set-default-scroll-speed">
|
||||
<update tableName="USERSETTINGS">
|
||||
<column name="scroll_speed" valueNumeric="400" />
|
||||
</update>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="athou" id="create-tags-table">
|
||||
<validCheckSum>7:fdd37bdee09c8fbbcbcd867b05decaae</validCheckSum>
|
||||
<createTable tableName="FEEDENTRYTAGS">
|
||||
<column name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" />
|
||||
</column>
|
||||
<column name="entry_id" type="BIGINT">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="user_id" type="BIGINT">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="name" type="VARCHAR(40)">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<addForeignKeyConstraint constraintName="fk_entry_id" baseTableName="FEEDENTRYTAGS" baseColumnNames="entry_id"
|
||||
referencedTableName="FEEDENTRIES" referencedColumnNames="id" />
|
||||
<addForeignKeyConstraint constraintName="fk_user_id" baseTableName="FEEDENTRYTAGS" baseColumnNames="user_id"
|
||||
referencedTableName="USERS" referencedColumnNames="id" />
|
||||
|
||||
<createIndex tableName="FEEDENTRYTAGS" indexName="user_entry_name_index">
|
||||
<column name="user_id" />
|
||||
<column name="entry_id" />
|
||||
<column name="name" />
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="athou" id="add-full-refresh-timestamp">
|
||||
<addColumn tableName="USERS">
|
||||
<column name="last_full_refresh" type="DATETIME" />
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
53
src/main/resources/changelogs/db.changelog-1.5.xml
Normal file
53
src/main/resources/changelogs/db.changelog-1.5.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
|
||||
<changeSet id="add-detailed-social-options" author="athou">
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="email" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="gmail" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="facebook" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="twitter" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="googleplus" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="tumblr" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="pocket" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="instapaper" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="buffer" type="BIT"></column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="readability" type="BIT"></column>
|
||||
</addColumn>
|
||||
|
||||
<dropColumn tableName="USERSETTINGS" columnName="socialButtons" />
|
||||
|
||||
<update tableName="USERSETTINGS">
|
||||
<column name="email" valueBoolean="true"></column>
|
||||
<column name="gmail" valueBoolean="true"></column>
|
||||
<column name="facebook" valueBoolean="true"></column>
|
||||
<column name="twitter" valueBoolean="true"></column>
|
||||
<column name="googleplus" valueBoolean="true"></column>
|
||||
<column name="tumblr" valueBoolean="true"></column>
|
||||
<column name="pocket" valueBoolean="true"></column>
|
||||
<column name="instapaper" valueBoolean="true"></column>
|
||||
<column name="buffer" valueBoolean="true"></column>
|
||||
<column name="readability" valueBoolean="true"></column>
|
||||
</update>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -7,5 +7,7 @@
|
||||
<include file="changelogs/db.changelog-1.1.xml" />
|
||||
<include file="changelogs/db.changelog-1.2.xml" />
|
||||
<include file="changelogs/db.changelog-1.3.xml" />
|
||||
<include file="changelogs/db.changelog-1.4.xml" />
|
||||
<include file="changelogs/db.changelog-1.5.xml" />
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -6,6 +6,7 @@ global.download=تحميل
|
||||
global.link=رابط
|
||||
global.bookmark=مرجعية
|
||||
global.close=أغلق
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=اشترك
|
||||
tree.import=استورد
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=الترتيب حسب التاريخ تصاعدي / ت
|
||||
toolbar.titles_only=العناوين فقط
|
||||
toolbar.expanded_view=عرض موسع
|
||||
toolbar.mark_all_as_read=اعتبر الكل مقروء
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=العناصر الأقدم من يوم
|
||||
toolbar.mark_all_older_week=العناصر الأقدم من أسبوع
|
||||
toolbar.mark_all_older_two_weeks=العناصر الأقدم من أسبوعين
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Show feeds and categories with no unread entries
|
||||
settings.general.social_buttons=Show social sharing buttons
|
||||
settings.general.scroll_marks=In expanded view, scrolling through entries mark them as read
|
||||
settings.appearance=Appearance
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Theme
|
||||
settings.submit_your_theme=Submit your theme
|
||||
settings.custom_css=Custom CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Queued for refresh
|
||||
details.feed_url=Feed URL
|
||||
details.generate_api_key_first=Generate an API key in your profile first.
|
||||
details.unsubscribe=Unsubscribe
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Category details
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Parent category
|
||||
|
||||
profile.user_name=User name
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Generate new API key
|
||||
profile.generate_new_api_key_info=Changing password will generate a new API key
|
||||
profile.opml_export=OPML export
|
||||
profile.delete_account=Delete account
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Keyboard shortcuts
|
||||
|
||||
156
src/main/resources/i18n/ca.properties
Normal file
156
src/main/resources/i18n/ca.properties
Normal file
@@ -0,0 +1,156 @@
|
||||
global.save=Desa
|
||||
global.cancel=Cancel·la
|
||||
global.delete=Esborra
|
||||
global.required=Requerit
|
||||
global.download=Descarrega
|
||||
global.link=Enllaç
|
||||
global.bookmark=Adreça d'interès
|
||||
global.close=Tancar
|
||||
global.tags=Etiquetes
|
||||
|
||||
tree.subscribe=Subscriure
|
||||
tree.import=Importa
|
||||
tree.new_category=Nova categoria
|
||||
tree.all=Tot
|
||||
tree.starred=Destacats
|
||||
|
||||
subscribe.feed_url=URL del canal
|
||||
subscribe.feed_name=Nom del canal
|
||||
subscribe.category=Categoria
|
||||
|
||||
import.google_reader_prefix=Importaré els canals del teu
|
||||
import.google_reader_suffix= compte.
|
||||
import.google_download=O be, carrega el teu fitxer subscriptions.xml.
|
||||
import.google_download_link=Descarrega'l d'aquí.
|
||||
import.xml_file=Fitxer OPML
|
||||
|
||||
new_category.name=Nom
|
||||
new_category.parent=Arrel
|
||||
|
||||
toolbar.unread=Per llegir
|
||||
toolbar.all=Tots
|
||||
toolbar.previous_entry=Entrada prèvia
|
||||
toolbar.next_entry=Entrada següent
|
||||
toolbar.refresh=Actualitzar
|
||||
toolbar.refresh_all=Força l'actualització de tots els canals
|
||||
toolbar.sort_by_asc_desc=Ordenar per data asc/desc
|
||||
toolbar.titles_only=Només títols
|
||||
toolbar.expanded_view=Vista ampliada
|
||||
toolbar.mark_all_as_read=Marcar tots llegits
|
||||
toolbar.mark_all_older_12_hours=Ítems més vells de 12 hores
|
||||
toolbar.mark_all_older_day=Ítems més vells d'un dia
|
||||
toolbar.mark_all_older_week=Ítems més vells d'una setmana
|
||||
toolbar.mark_all_older_two_weeks=Ítems més vells de dues setmanes
|
||||
toolbar.settings=Configuració
|
||||
toolbar.profile=Perfil
|
||||
toolbar.admin=Admin
|
||||
toolbar.about=Quant a
|
||||
toolbar.logout=Desconnecta't
|
||||
toolbar.donate=Donació
|
||||
|
||||
view.entry_source=de
|
||||
view.entry_author=per
|
||||
view.error_while_loading_feed=Error carregant el canal
|
||||
view.keep_unread=Conserva com a no llegit
|
||||
view.no_unread_items=no té ítems sense llegir.
|
||||
view.mark_up_to_here=Marcar com a llegit fins aquí
|
||||
view.search_for=cercant:
|
||||
view.no_search_results=No hi ha coincidències per les paraules clau sol·licitades
|
||||
|
||||
feedsearch.hint=Introdueix una subscripció...
|
||||
feedsearch.help=Utilitza la tecla de retorn per seleccionar i les tecles de cursor per navegar.
|
||||
feedsearch.result_prefix=Les teves subscripcions:
|
||||
|
||||
settings.general=General
|
||||
settings.general.language=Idioma
|
||||
settings.general.language.contribute=Contribueix amb traduccions
|
||||
settings.general.show_unread=Mostrar canals i categories amb entrades sense llegir
|
||||
settings.general.social_buttons=Mostrar botons per compartir en xarxes socials
|
||||
settings.general.scroll_marks=A la vista ampliada si et desplaces per les entrades les marques com a llegides
|
||||
settings.appearance=Aparença
|
||||
settings.scroll_speed=Velocitat de desplaçament quan navegues entre entrades (en mil·lisegons)
|
||||
settings.scroll_speed.help=Fixa a 0 per desactivar
|
||||
settings.theme=Tema
|
||||
settings.submit_your_theme=Envia un tema
|
||||
settings.custom_css=CSS personalitzat
|
||||
|
||||
details.feed_details=Detalls del canal
|
||||
details.url=URL
|
||||
details.website=Lloc web
|
||||
details.name=Nom
|
||||
details.category=Categoria
|
||||
details.position=Posició
|
||||
details.last_refresh=Darrera actualització
|
||||
details.message=Darrer missatge d'actualització
|
||||
details.next_refresh=Propera actualització
|
||||
details.queued_for_refresh=A la cua d'actualització
|
||||
details.feed_url=URL del canal
|
||||
details.generate_api_key_first=Abans cal que generis una clau API en el teu perfil.
|
||||
details.unsubscribe=Cancel·la la subscripció
|
||||
details.unsubscribe_confirmation=Segur que vols cancel·lar la subscripció del canal?
|
||||
details.delete_category_confirmation=Segur que vols esborrar la categoria?
|
||||
details.category_details=Detalls de la categoria
|
||||
details.tag_details=Detalls de l'etiqueta
|
||||
details.parent_category=Categoria arrel
|
||||
|
||||
profile.user_name=Nom d'usuari
|
||||
profile.email=Adreça electrònica
|
||||
profile.change_password=Canvia la contrasenya
|
||||
profile.confirm_password=Confirma la contrasenya
|
||||
profile.minimum_6_chars=Mínim de 6 caracters
|
||||
profile.passwords_do_not_match=Les contrasenyes no coincideixen
|
||||
profile.api_key=Clau API
|
||||
profile.api_key_not_generated=Encara no s'ha generat
|
||||
profile.generate_new_api_key=Genera una nova clau API
|
||||
profile.generate_new_api_key_info=El canvi de contrasenya generarà una nova clau API
|
||||
profile.opml_export=Exporta OPML
|
||||
profile.delete_account=Esborra el compte
|
||||
profile.delete_account_confirmation=Vols esborrar el teu compte? No ho podràs desfer!
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Dreceres de teclat
|
||||
about.version=Versió de CommaFeed
|
||||
about.line1_prefix=CommaFeed és un projecte de codi font obert. El codi font és hostatjat a
|
||||
about.line1_suffix=.
|
||||
about.line2_prefix=Si trobes un problema, si us plau informa'n a la pàgina de problemes del
|
||||
about.line2_suffix=\ projecte.
|
||||
about.line3=Si t'agrada el projecte, pensa en fer un donatiu per recolzar el desenvolupador i per ajudar amb les despeses de l'hostatge del lloc web.
|
||||
about.line4=I pels que preferiu bitcoin, aquí teniu l'adreça
|
||||
about.goodies=Afegitons
|
||||
about.goodies.android_app=App Android
|
||||
about.goodies.subscribe_url=URL de subscripció
|
||||
about.goodies.chrome_extension=Extensió del Chrome
|
||||
about.goodies.firefox_extension=Extensió del Firefox
|
||||
about.goodies.opera_extension=Extensió de l'Opera
|
||||
about.goodies.subscribe_bookmarklet=Afegeix bookmarklet de subscripció (clica)
|
||||
about.goodies.subscribe_bookmarklet_asc=Primer els vells
|
||||
about.goodies.subscribe_bookmarklet_desc=Primer els nous
|
||||
about.goodies.next_unread_bookmarklet=Bookmarklet del proper ítem sense llegir (arrosega a la barra d'adreces d'interès)
|
||||
about.translation=Traducció
|
||||
about.translation.message=Necessitem la teva ajuda per traduir CommaFeed.
|
||||
about.translation.link=Informació per contribuir amb traduccions.
|
||||
about.announcements=Anuncis
|
||||
about.rest_api.line1=CommaFeed funciona amb JAX-RS i AngularJS. Per tant, té disponible una API REST.
|
||||
about.rest_api.link_to_documentation=Enllaç a la documentació.
|
||||
|
||||
about.shortcuts.mouse_middleclick=Clic amb el botó del mig
|
||||
about.shortcuts.open_next_entry=obrir entrada següent
|
||||
about.shortcuts.open_previous_entry=obrir entrada prèvia
|
||||
about.shortcuts.spacebar=espai/majúscula+espai
|
||||
about.shortcuts.move_page_down_up=mou la pàgina avall/amunt
|
||||
about.shortcuts.focus_next_entry=fixa el focus en l'entrada següent entrada sense obrir-la
|
||||
about.shortcuts.focus_previous_entry=fixa el focus en l'entrada prèvia sense obrir-la
|
||||
about.shortcuts.open_next_feed=obrir canal o categoria següent
|
||||
about.shortcuts.open_previous_feed=obrir canal o categoria prèvia
|
||||
about.shortcuts.open_close_current_entry=obre/tanca entrada actual
|
||||
about.shortcuts.open_current_entry_in_new_window=obrir entrada actual en una finestra nova
|
||||
about.shortcuts.open_current_entry_in_new_window_background=obrir entrada actual en una finestra nova en segon pla
|
||||
about.shortcuts.star_unstar=destacar/treure destacat a l'entrada actual
|
||||
about.shortcuts.mark_current_entry=marcar com a llegida/no llegida l'entrada actual
|
||||
about.shortcuts.mark_all_as_read=marcar totes les entrades com a llegides
|
||||
about.shortcuts.open_in_new_tab_mark_as_read=obrir entrada en una pestanya nova i marcar com a llegida
|
||||
about.shortcuts.fullscreen=commutar el mode de pantalla completa
|
||||
about.shortcuts.font_size=incrementar/reduir la mida de la font de l'entrada actual
|
||||
about.shortcuts.go_to_all=anar a la vista de Tot
|
||||
about.shortcuts.go_to_starred=anar a la vista de Destacats
|
||||
about.shortcuts.feed_search=navegar a una subscripció introduint-ne el nom
|
||||
@@ -6,6 +6,7 @@ global.download = Stáhnout
|
||||
global.link = Odkaz
|
||||
global.bookmark = Záložky
|
||||
global.close = Zavřít
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe = Nový odběr
|
||||
tree.import = Importovat
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc = Seřadit podle nejnovějšího/nejstaršího
|
||||
toolbar.titles_only = Zobrazit jenom titulky
|
||||
toolbar.expanded_view = Rozšířený náhled
|
||||
toolbar.mark_all_as_read = Označit vše jako přečtené
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day = Položky starší než den
|
||||
toolbar.mark_all_older_week = Položky starší než týden
|
||||
toolbar.mark_all_older_two_weeks = Položky starší než dva týdny
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread = Zobrazit položky a kategorie z přečtenými pol
|
||||
settings.general.social_buttons = Zobrazit možnosti sdílení
|
||||
settings.general.scroll_marks = Skrolování v rozšířeném náhledu označí položky jako přečtené
|
||||
settings.appearance = Vzhled
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme = Motiv
|
||||
settings.submit_your_theme = Nahrát vlastní motiv
|
||||
settings.custom_css = Vlastní motiv (CSS)
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh = Ve frontě na obnovu
|
||||
details.feed_url = URL RSS zdroje
|
||||
details.generate_api_key_first = Vygenerujte si API klíč na stránce vašeho profilu.
|
||||
details.unsubscribe = Odhlásit odběr.
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details = Detail kategorie
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category = Hlavní kategorie
|
||||
|
||||
profile.user_name = Uživatelské jméno
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key = Vygenerovat nový API klíč
|
||||
profile.generate_new_api_key_info = Změnou hesla vygenerujete nový API klíč
|
||||
profile.opml_export = exportovat do formátu OPML
|
||||
profile.delete_account = Odstranit účet
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api = REST API
|
||||
about.keyboard_shortcuts = Klávesové zkratky
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Lawrlwytho
|
||||
global.link=Dolen
|
||||
global.bookmark=Nod tudalen
|
||||
global.close=Cau
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Tanysgrifio
|
||||
tree.import=Mewnforio
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Trefnu yn ôl dyddiad
|
||||
toolbar.titles_only=Teitlau yn unig
|
||||
toolbar.expanded_view=Golwg estynedig
|
||||
toolbar.mark_all_as_read=Nodi'r cyfan fel wedi ei ddarllen
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Eitemau hyn na diwrnod
|
||||
toolbar.mark_all_older_week=Eitemau hyn nag wythnos
|
||||
toolbar.mark_all_older_two_weeks=Eitemau hyn na phythefnos
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Dangos ffrydiau a chategoriau gyda dim eitemau heb
|
||||
settings.general.social_buttons=Dangos botymau rhannu
|
||||
settings.general.scroll_marks=Marcio eitemau fel wedi eu darllen wrth sgrolio drwyddynt yn y golwg estynedig ###### Defnyddio gystrawen debyg i'r ddau uwch.
|
||||
settings.appearance=Golwg
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Thema
|
||||
settings.submit_your_theme=Cyflwyna dy thema
|
||||
settings.custom_css=CSS wedi'i addasu
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Ciwiwyd i'w adnewyddu
|
||||
details.feed_url=URL Ffrwd
|
||||
details.generate_api_key_first=Rhaid creu allwedd API yn dy broffil yn gyntaf.
|
||||
details.unsubscribe=Dad-danysgrifio
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Manylion categori
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Categori rhiant
|
||||
|
||||
profile.user_name=Enw defnyddiwr
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Creu allwedd API newydd
|
||||
profile.generate_new_api_key_info=Mae newid cyfrinair yn creu allwedd API newydd
|
||||
profile.opml_export=Allforio OPML
|
||||
profile.delete_account=Dileu cyfrif
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Llwybr byr bysellfwrdd
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Hent
|
||||
global.link=Link
|
||||
global.bookmark=Bogmærke
|
||||
global.close=Luk
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Abonner
|
||||
tree.import=Importer
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sorter efter dato ny/gammel
|
||||
toolbar.titles_only=Kun titler
|
||||
toolbar.expanded_view=Udvidet visning
|
||||
toolbar.mark_all_as_read=Marker alle som læst
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Artikler ældere end én dag
|
||||
toolbar.mark_all_older_week=Artikler ældere end én uge
|
||||
toolbar.mark_all_older_two_weeks=Artikler ældere end to uger
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Vis abonnomenter og kategorier med læste artikler
|
||||
settings.general.social_buttons=Vis delingsknapper
|
||||
settings.general.scroll_marks=I udvidet visning, marker artikler som læste når der rulles forbi dem
|
||||
settings.appearance=Udseende
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Tema
|
||||
settings.submit_your_theme=Indsend dit tema
|
||||
settings.custom_css=Brugerdefineret CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=I kø til opdatering
|
||||
details.feed_url=URL for abonnement
|
||||
details.generate_api_key_first=Generer en API nøgle i din profil først.
|
||||
details.unsubscribe=Afmeld abonnement
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Kategori detaljer
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Overordnet kategori
|
||||
|
||||
profile.user_name=Brugernavn
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Generer ny API nøgle
|
||||
profile.generate_new_api_key_info=Ændring af adgangskode vil generere en ny API nøgle
|
||||
profile.opml_export=OPML eksport
|
||||
profile.delete_account=Slet konto
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Tastaturgenveje
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Herunterladen
|
||||
global.link=Link
|
||||
global.bookmark=Lesezeichen
|
||||
global.close=Schließen
|
||||
global.tags=Tags
|
||||
|
||||
tree.subscribe=Abonnieren
|
||||
tree.import=Importieren
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Nach Datum sortieren (auf-/absteigend)
|
||||
toolbar.titles_only=Nur Überschriften
|
||||
toolbar.expanded_view=Ausgedehnte Ansicht
|
||||
toolbar.mark_all_as_read=Alle Artikel als gelesen markieren
|
||||
toolbar.mark_all_older_12_hours=Artikel älter als 12 Stunden
|
||||
toolbar.mark_all_older_day=Artikel älter als ein Tag
|
||||
toolbar.mark_all_older_week=Artikel älter als eine Woche
|
||||
toolbar.mark_all_older_two_weeks=Artikel älter als zwei Wochen
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Zeige Feeds und Kategorien mit ungelesenen Einträg
|
||||
settings.general.social_buttons=Zeige Buttons zum Teilen von Inhalten über soziale Netzwerke
|
||||
settings.general.scroll_marks=In der ausgedehnten Ansicht werden Artikel beim Scrollen als gelesen markiert
|
||||
settings.appearance=Aussehen
|
||||
settings.scroll_speed=Geschwindigkeit beim scrollen zwischen Einträgen (in Millisekunden)
|
||||
settings.scroll_speed.help=setze auf 0 zum deaktivieren
|
||||
settings.theme=Theme
|
||||
settings.submit_your_theme=Füg dein Theme hinzu
|
||||
settings.custom_css=Eigenes CSS
|
||||
@@ -77,13 +81,16 @@ details.name=Name
|
||||
details.category=Kategorie
|
||||
details.position=Position
|
||||
details.last_refresh=Letzte Aktualisierung
|
||||
details.message=Last refresh message ####### Needs translation
|
||||
details.message=Nachricht der letzten Aktualisierung
|
||||
details.next_refresh=Nächste Aktualisierung
|
||||
details.queued_for_refresh=Wartet auf Aktualisierung
|
||||
details.feed_url=Feed Adresse
|
||||
details.generate_api_key_first=Generiere zuerst einen API Schlüssel in deinem Profil.
|
||||
details.unsubscribe=Kündigen
|
||||
details.unsubscribe_confirmation=Bist du sicher das du diesen Feed kündigen möchtest?
|
||||
details.delete_category_confirmation=Bist du sicher das du diese Kategorie löschen möchtest?
|
||||
details.category_details=Kategoriedetails
|
||||
details.tag_details=Tag Details
|
||||
details.parent_category=Übergeordnete Kategorie
|
||||
|
||||
profile.user_name=Benutzername
|
||||
@@ -97,7 +104,8 @@ profile.api_key_not_generated=Noch nicht generiert
|
||||
profile.generate_new_api_key=Generiere einen neuen API key
|
||||
profile.generate_new_api_key_info=Das Ändern des Passwortes erzeugt einen neuen API Schlüssel
|
||||
profile.opml_export=OPML exportieren
|
||||
profile.delete_account=Lösche den Account
|
||||
profile.delete_account=Account löschen
|
||||
profile.delete_account_confirmation=Deinen Account löschen? Es gibt kein Zurück!
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Tastatur Kurzbefehle
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Download
|
||||
global.link=Link
|
||||
global.bookmark=Bookmark
|
||||
global.close=Close
|
||||
global.tags=Tags
|
||||
|
||||
tree.subscribe=Subscribe
|
||||
tree.import=Import
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sort by date asc/desc
|
||||
toolbar.titles_only=Titles only
|
||||
toolbar.expanded_view=Expanded view
|
||||
toolbar.mark_all_as_read=Mark all as read
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours
|
||||
toolbar.mark_all_older_day=Items older than a day
|
||||
toolbar.mark_all_older_week=Items older than a week
|
||||
toolbar.mark_all_older_two_weeks=Items older than two weeks
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Show feeds and categories with no unread entries
|
||||
settings.general.social_buttons=Show social sharing buttons
|
||||
settings.general.scroll_marks=In expanded view, scrolling through entries mark them as read
|
||||
settings.appearance=Appearance
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds)
|
||||
settings.scroll_speed.help=set to 0 to disable
|
||||
settings.theme=Theme
|
||||
settings.submit_your_theme=Submit your theme
|
||||
settings.custom_css=Custom CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Queued for refresh
|
||||
details.feed_url=Feed URL
|
||||
details.generate_api_key_first=Generate an API key in your profile first.
|
||||
details.unsubscribe=Unsubscribe
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed?
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category?
|
||||
details.category_details=Category details
|
||||
details.tag_details=Tag details
|
||||
details.parent_category=Parent category
|
||||
|
||||
profile.user_name=User name
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Generate new API key
|
||||
profile.generate_new_api_key_info=Changing password will generate a new API key
|
||||
profile.opml_export=OPML export
|
||||
profile.delete_account=Delete account
|
||||
profile.delete_account_confirmation=Delete your account? There's no turning back!
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Keyboard shortcuts
|
||||
@@ -145,4 +153,4 @@ about.shortcuts.fullscreen=toggle full screen mode
|
||||
about.shortcuts.font_size=increase/decrease font size of the current entry
|
||||
about.shortcuts.go_to_all=go to the All view
|
||||
about.shortcuts.go_to_starred=go to the Starred view
|
||||
about.shortcuts.feed_search=navigate to a subscription by entering the subscription name
|
||||
about.shortcuts.feed_search=navigate to a subscription by entering the subscription name
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Descargar
|
||||
global.link=Enlace
|
||||
global.bookmark=Marcador
|
||||
global.close=Close ####### Needs translation
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Subscribir
|
||||
tree.import=Importar
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Ordenar por fecha asc/desc
|
||||
toolbar.titles_only=Sólo Títulos
|
||||
toolbar.expanded_view=Vista Expandida
|
||||
toolbar.mark_all_as_read=Marcar todos como leído
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Artículos anteriores a un día
|
||||
toolbar.mark_all_older_week=Artículos más de una semana
|
||||
toolbar.mark_all_older_two_weeks=Artículos más de does semanas
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Mostrar canales y categorías sin entradas no leíd
|
||||
settings.general.social_buttons=Mostrar botones de compartir de redes sociales.
|
||||
settings.general.scroll_marks=En vista expandida, el desplazamiento por las entradas las marca como leídas
|
||||
settings.appearance=Appearance ####### Needs translation
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Theme ####### Needs translation
|
||||
settings.submit_your_theme=Submit your theme ####### Needs translation
|
||||
settings.custom_css=CSS Personalizado
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Queued for refresh ####### Needs translation
|
||||
details.feed_url=URL del Canal
|
||||
details.generate_api_key_first=Genera una llave API en tu perfil primero.
|
||||
details.unsubscribe=Terminar subscripción
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Detalles de la categoría
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Categoría principal
|
||||
|
||||
profile.user_name=Nombre de usuario
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Generar nueva llave API
|
||||
profile.generate_new_api_key_info=Al cambiar la contraseña se generará una nueva llave API
|
||||
profile.opml_export=Exportación de OPML
|
||||
profile.delete_account=Eliminar cuenta
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Atajos de teclado
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=بارگیری
|
||||
global.link=پیوند
|
||||
global.bookmark=بوکمارک
|
||||
global.close=بستن
|
||||
global.tags=برجسپها
|
||||
|
||||
tree.subscribe=مشترک شوید
|
||||
tree.import=درونریزی
|
||||
@@ -31,11 +32,12 @@ toolbar.all=همه
|
||||
toolbar.previous_entry=مطلب قبلی
|
||||
toolbar.next_entry=مطلب بعدی
|
||||
toolbar.refresh=تازهسازی
|
||||
toolbar.refresh_all=Force refresh all my feeds ####### Needs translation
|
||||
toolbar.refresh_all=مجبورکردن تازهسازی همهٔ خوراکها
|
||||
toolbar.sort_by_asc_desc=مرتبکردن بر اساس تاریخ بهصورت صعودی/نزولی
|
||||
toolbar.titles_only=فقط عنوانها
|
||||
toolbar.expanded_view=نمای گسترشیافته
|
||||
toolbar.mark_all_as_read=علامتگذاری تمامی مطالب بهعنوان خواندهشده
|
||||
toolbar.mark_all_older_12_hours=مطالب قدیمیتر از ۱۲ ساعت
|
||||
toolbar.mark_all_older_day=مطالب قدیمیتر از یک روز
|
||||
toolbar.mark_all_older_week=مطالب قدیمیتر از یک هفته
|
||||
toolbar.mark_all_older_two_weeks=مطالب قدیمی تر از چند هفته قیل
|
||||
@@ -52,8 +54,8 @@ view.error_while_loading_feed=متأسفانه، هنگام بارگیری ای
|
||||
view.keep_unread=خواندهنشده نگهدار
|
||||
view.no_unread_items=هیچ مطلب خواندهنشدهای ندارد.
|
||||
view.mark_up_to_here=تا اینجا را خواندهشده در نظر بگیر
|
||||
view.search_for=searching for: ####### Needs translation
|
||||
view.no_search_results=No match found for the requested keywords ####### Needs translation
|
||||
view.search_for=جستجو برای:
|
||||
view.no_search_results=هیج نتیجهای برای کلیدواژههای درخواستی یافت نشد
|
||||
|
||||
feedsearch.hint=نوشتن بر روی یک اشتراک...
|
||||
feedsearch.help=دکمهٔ بازگشت برای انتخاب و دکمههای جهتدار را برای ناوبری استفاده کن.
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=تنها خوراکها و دستههای ر
|
||||
settings.general.social_buttons=نشاندادن دکمههای اشتراکگذاری در شبکههای اجتماعی
|
||||
settings.general.scroll_marks=در نمای گسترشیافته، لغزیدن بر روی مطالب بهعنوان نشانهگذاری بهعنوان خواندهشده در نظر گرفتهشوند.
|
||||
settings.appearance=ظاهر
|
||||
settings.scroll_speed=سرعت لغزش هنگام گشتن بین مدخلها (به میلیثانیه)
|
||||
settings.scroll_speed.help=قراردادن به ۰ برای غیرفعالکردن
|
||||
settings.theme=پوسته
|
||||
settings.submit_your_theme=پوستهٔ خود را ارسالکنید
|
||||
settings.custom_css=سیاساس شخصیسازیشده
|
||||
@@ -77,13 +81,16 @@ details.name=نام
|
||||
details.category=دسته
|
||||
details.position=موقعیت
|
||||
details.last_refresh=آخرین بروزرسانی
|
||||
details.message=Last refresh message ####### Needs translation
|
||||
details.message=پیام آخرین تازهسازی
|
||||
details.next_refresh=بروزرسانی بعدی
|
||||
details.queued_for_refresh=منتظر برای بروزرسانی
|
||||
details.feed_url=نشانی خوراک
|
||||
details.generate_api_key_first=ابتدا یک کلید API در نمایهٔ خود ایجاد کنید.
|
||||
details.unsubscribe=لغو اشتراک
|
||||
details.unsubscribe_confirmation=مطمئنید میخواهید از این این لغو اشتراک کنید؟
|
||||
details.delete_category_confirmation=مطمئنید میخواهید این رده را حذف کنید؟
|
||||
details.category_details=جزئیات دسته
|
||||
details.tag_details=جزئیات برچسپ
|
||||
details.parent_category=ردهٔ پدر
|
||||
|
||||
profile.user_name=نام کاربری
|
||||
@@ -98,10 +105,11 @@ profile.generate_new_api_key=ایجاد کلید جدید API
|
||||
profile.generate_new_api_key_info=تغییر گذرواژه کلید API بهوجود خواهد آورد.
|
||||
profile.opml_export=خارجسازی OPML
|
||||
profile.delete_account=حذف حساب کاربری
|
||||
profile.delete_account_confirmation=حذف حسابتان؟ بازگشتی وجود ندارد!
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=کلیدهای میانبر
|
||||
about.version=CommaFeed version ####### Needs translation
|
||||
about.version=نسخهٔ کامافید
|
||||
about.line1_prefix=کامافید یک پروژه متنباز است. مخازن آن در
|
||||
about.line1_suffix=میزبانی میشود.
|
||||
about.line2_prefix=اگر شما به مسئلهای برخورده اید، لطفاً آن را در صفحه مسائل گزارش دهید
|
||||
@@ -143,7 +151,7 @@ about.shortcuts.mark_all_as_read=علامتگذاری تمامی مطالب
|
||||
about.shortcuts.open_in_new_tab_mark_as_read=بازکردن مطلب در سربرگ جدید و علامتگذاری آن بهعنوان خواندهشده
|
||||
about.shortcuts.fullscreen=فعال/غیرفعالکردن حالت تمام صفحه
|
||||
about.shortcuts.font_size=افزایش/کاهش اندازهٔ قلم مدخل فعلی
|
||||
about.shortcuts.go_to_all=go to the All view ####### Needs translation
|
||||
about.shortcuts.go_to_starred=go to the Starred view ####### Needs translation
|
||||
about.shortcuts.go_to_all=رفتن به نمای همه
|
||||
about.shortcuts.go_to_starred=رفتن به نمای ستاره دادهشدهها
|
||||
about.shortcuts.feed_search=ناوبری به یک اشتراک با نوشتن نام اشتراک
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Lataa
|
||||
global.link=Linkki
|
||||
global.bookmark=Kirjanmerkki
|
||||
global.close=Sulje
|
||||
global.tags=Tagit
|
||||
|
||||
tree.subscribe=Tilaa syöte
|
||||
tree.import=Tuo
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Järjestä päivämäärän mukaan nousevasti/laskevast
|
||||
toolbar.titles_only=Näytä vain otsikot
|
||||
toolbar.expanded_view=Laajennettu näkymä
|
||||
toolbar.mark_all_as_read=Merkitse kaikki luetuiksi
|
||||
toolbar.mark_all_older_12_hours=12 tuntia vanhemmat otsikot
|
||||
toolbar.mark_all_older_day=Päivää vanhemmat otsikot
|
||||
toolbar.mark_all_older_week=Viikkoa vanhemmat otsikot
|
||||
toolbar.mark_all_older_two_weeks=Kahta viikkoa vanhemmat otsikot
|
||||
@@ -52,8 +54,8 @@ view.error_while_loading_feed=Virhe tilausta ladattaessa
|
||||
view.keep_unread=Pidä lukemattomana
|
||||
view.no_unread_items=ei sisällä lukemattomia otsikoita.
|
||||
view.mark_up_to_here=Merkitse luetuksi tähän asti
|
||||
view.search_for=searching for: ####### Needs translation
|
||||
view.no_search_results=No match found for the requested keywords ####### Needs translation
|
||||
view.search_for=Etsi sanoilla:
|
||||
view.no_search_results=Ei tuloksia annetuilla hakusanoilla.
|
||||
|
||||
feedsearch.hint=Kirjoita syötteen nimi...
|
||||
feedsearch.help=Siirry syötteiden välillä nuolinäppäimillä ja valitse syöte enterillä.
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Näytä syötteet ja kansiot, joissa ei ole lukemat
|
||||
settings.general.social_buttons=Näytä jakonapit
|
||||
settings.general.scroll_marks=Laajennetussa näkymässä otsikoiden selaaminen merkitsee ne luetuiksi
|
||||
settings.appearance=Ulkonäkö
|
||||
settings.scroll_speed=Vieritysnopeus otsikoiden välillä navigoidessa (millisekunneissa)
|
||||
settings.scroll_speed.help=Aseta 0 poistaaksesi vieritys käytöstä.
|
||||
settings.theme=Teema
|
||||
settings.submit_your_theme=Lähetä oma teemasi
|
||||
settings.custom_css=Oma CSS
|
||||
@@ -77,13 +81,16 @@ details.name=Nimi
|
||||
details.category=Kansio
|
||||
details.position=Paikka
|
||||
details.last_refresh=Viimeisin päivitys
|
||||
details.message=Last refresh message ####### Needs translation
|
||||
details.message=Viimeisimmän päivityksen viesti
|
||||
details.next_refresh=Seuraava päivitys
|
||||
details.queued_for_refresh=Jonossa päivitettäväksi
|
||||
details.feed_url=Syötteen osoite
|
||||
details.generate_api_key_first=Luo API-avain profiilissasi.
|
||||
details.unsubscribe=Peruuta tilaus
|
||||
details.unsubscribe_confirmation=Haluatko varmasti lopettaa tämän syötteen tilauksen?
|
||||
details.delete_category_confirmation=Haluatko varmasti poistaa tämän kansion?
|
||||
details.category_details=Kansion tiedot
|
||||
details.tag_details=Tagin tiedot
|
||||
details.parent_category=Yläkansio
|
||||
|
||||
profile.user_name=Käyttäjänimi
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Luo uusi API-avain
|
||||
profile.generate_new_api_key_info=Salasanan vaihtaminen luo uuden API-avaimen
|
||||
profile.opml_export=OPML vienti
|
||||
profile.delete_account=Poista tunnus
|
||||
profile.delete_account_confirmation=Haluatko varmasti poistaa tunnuksesi? Tätä ei voi perua!
|
||||
|
||||
about.rest_api=REST-API
|
||||
about.keyboard_shortcuts=Näppäinoikotiet
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Télécharger
|
||||
global.link=Lien
|
||||
global.bookmark=Favoris
|
||||
global.close=Fermer
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=S'abonner
|
||||
tree.import=Importer
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Trier par date croissante/décroissante
|
||||
toolbar.titles_only=Titres uniquement
|
||||
toolbar.expanded_view=Vue étendue
|
||||
toolbar.mark_all_as_read=Tout marquer comme lu
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Articles de plus d'un jour
|
||||
toolbar.mark_all_older_week=Articles de plus d'une semaine
|
||||
toolbar.mark_all_older_two_weeks=Articles de plus d'un mois
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Afficher les flux et les catégories pour lesquels
|
||||
settings.general.social_buttons=Afficher les boutons de partage sur réseaux sociaux
|
||||
settings.general.scroll_marks=En mode de lecture étendu, marquer comme lu les éléments lorsque la fenêtre descend.
|
||||
settings.appearance=Apparence
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Thème
|
||||
settings.submit_your_theme=Soumettez votre thème.
|
||||
settings.custom_css=CSS personnelle
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=En file d'attente
|
||||
details.feed_url=URL du flux
|
||||
details.generate_api_key_first=Générez une clé API dans votre profil d'abord.
|
||||
details.unsubscribe=Se désabonner
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Détails de la catégorie
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Catégorie parente
|
||||
|
||||
profile.user_name=Nom
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Générer une nouvelle clé API
|
||||
profile.generate_new_api_key_info=Changer de mot de passe va générer une nouvelle clé API
|
||||
profile.opml_export=Export du fichier OPML
|
||||
profile.delete_account=Effacer le compte
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=API REST
|
||||
about.keyboard_shortcuts=Raccourcis clavier
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Descargar
|
||||
global.link=Ligazón
|
||||
global.bookmark=Marcador
|
||||
global.close=Pechar
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Subscribir
|
||||
tree.import=Importar
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Ordenar por data asc/desc
|
||||
toolbar.titles_only=Só títulos
|
||||
toolbar.expanded_view=Vista expandida
|
||||
toolbar.mark_all_as_read=Marcar todos como lidos
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Artigos anteriores a un día
|
||||
toolbar.mark_all_older_week=Artigos de máis de unha semana
|
||||
toolbar.mark_all_older_two_weeks=Artigos de máis de dúas semanas
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Mostrar fontes e categorías sen entradas non lidas
|
||||
settings.general.social_buttons=Mostrar botóns de compartir en redes sociais.
|
||||
settings.general.scroll_marks=En vista expandida, o desplazamento polas entradas márcaas como lidas.
|
||||
settings.appearance=Aspecto
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Decorado
|
||||
settings.submit_your_theme=Envíe o seu decorado
|
||||
settings.custom_css=CSS Personalizado
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=En cola para actualizar
|
||||
details.feed_url=URL da fonte
|
||||
details.generate_api_key_first=Antes debes xerar unha chave API no teu perfil.
|
||||
details.unsubscribe=Rematar suscripción
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Detalles da categoría
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Categoría principal
|
||||
|
||||
profile.user_name=Nome de usuario
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Xerar nova chave da API
|
||||
profile.generate_new_api_key_info=Ao cambiar o contrasinal xerarase unha nova chave API
|
||||
profile.opml_export=Exportación de OPML
|
||||
profile.delete_account=Eliminar conta
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Atallos de teclado
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=جیرأکش
|
||||
global.link=خال
|
||||
global.bookmark=بوکمارک
|
||||
global.close=دَوَستن
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=مشترک ببید
|
||||
tree.import=درینأدأن
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=تاریخˇ سر دچئن
|
||||
toolbar.titles_only=خالی تیتران
|
||||
toolbar.expanded_view=واشاده نما
|
||||
toolbar.mark_all_as_read=همهته مطالبه چاکون بخانده
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=یک روز پیشترˇ مطالب
|
||||
toolbar.mark_all_older_week=یک هفته پیشترˇ مطالب
|
||||
toolbar.mark_all_older_two_weeks=چن هفته پیشترˇ مطالب
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=تنها خوراکها و دستههای ر
|
||||
settings.general.social_buttons=نشاندادن دکمههای اشتراکگذاری در شبکههای اجتماعی
|
||||
settings.general.scroll_marks=در نمای گسترشیافته، لغزیدن بر روی مطالب بهعنوان نشانهگذاری بهعنوان خواندهشده در نظر گرفتهشوند.
|
||||
settings.appearance=ظاهر
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=پوسته
|
||||
settings.submit_your_theme=شیمه پوستهٰ اوسه کونید
|
||||
settings.custom_css=سیاساس شخصیسازیشده
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=منتظر برای بروزرسانی
|
||||
details.feed_url=نشانی خوراک
|
||||
details.generate_api_key_first=ابتدا یک کلید API در نمایهٔ خود ایجاد کنید.
|
||||
details.unsubscribe=لغو اشتراک
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=جرگه جزئیات
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=پئرˇ جرگه
|
||||
|
||||
profile.user_name=کاربری نام
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=تازه کلید چاگودن API
|
||||
profile.generate_new_api_key_info=رمزه عوضأگودن API کلیده چاکونه.
|
||||
profile.opml_export=برینأدأن OPML
|
||||
profile.delete_account=کاربری حسابه پاکودن
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=وئر زئنˇ کلیدان
|
||||
|
||||
@@ -1,149 +1,157 @@
|
||||
global.save=Mentés
|
||||
global.cancel=Mégsem
|
||||
global.delete=Törlés
|
||||
global.required=Szükséges
|
||||
global.download=Letöltés
|
||||
global.link=Link
|
||||
global.bookmark=Könyvjelző
|
||||
global.close=Bezár
|
||||
|
||||
tree.subscribe=Feliratkozás
|
||||
tree.import=Importálás
|
||||
tree.new_category=Új kategória
|
||||
tree.all=Összes
|
||||
tree.starred=Csillagozott
|
||||
|
||||
subscribe.feed_url=Hírcsatorna URL
|
||||
subscribe.feed_name=Hírcsatorna neve
|
||||
subscribe.category=Kategória
|
||||
|
||||
import.google_reader_prefix=Engedd meg, hogy importáljuk a hírcsatornáidat a
|
||||
import.google_reader_suffix= fiókjából.
|
||||
import.google_download=Alternatívaként, feltöltheti a subscriptions.xml fájlt.
|
||||
import.google_download_link=Letöltheti innen.
|
||||
import.xml_file=OPML Fájl
|
||||
|
||||
new_category.name=Név
|
||||
new_category.parent=Szülő
|
||||
|
||||
toolbar.unread=Olvasatlan
|
||||
toolbar.all=Összes
|
||||
toolbar.previous_entry=Előző elem
|
||||
toolbar.next_entry=Következő elem
|
||||
toolbar.refresh=Frissítés
|
||||
toolbar.refresh_all=Force refresh all my feeds ####### Needs translation
|
||||
toolbar.sort_by_asc_desc=Rendezés időrend szerint
|
||||
toolbar.titles_only=Csak cím
|
||||
toolbar.expanded_view=Részletes nézet
|
||||
toolbar.mark_all_as_read=Az összes megjelölése olvasottként
|
||||
toolbar.mark_all_older_day=Régebbiek, mint egy nap
|
||||
toolbar.mark_all_older_week=Régebbiek, mint egy hét
|
||||
toolbar.mark_all_older_two_weeks=Régebbiek, mint két hét
|
||||
toolbar.settings=Beállítások
|
||||
toolbar.profile=Profil
|
||||
toolbar.admin=Admin
|
||||
toolbar.about=Névjegy
|
||||
toolbar.logout=Kilépés
|
||||
toolbar.donate=Anyagi támogatás
|
||||
|
||||
view.entry_source=from ####### Needs translation
|
||||
view.entry_author=by ####### Needs translation
|
||||
view.error_while_loading_feed=Hiba történt ennek a hírcsatornának a betöltésekor
|
||||
view.keep_unread=Megtartása olvasatlanként
|
||||
view.no_unread_items=nincsen olvasatlan eleme.
|
||||
view.mark_up_to_here=Mark as read up to here ####### Needs translation
|
||||
view.search_for=searching for: ####### Needs translation
|
||||
view.no_search_results=No match found for the requested keywords ####### Needs translation
|
||||
|
||||
feedsearch.hint=Keressen a hírcsatornák között...
|
||||
feedsearch.help=Használja a nyíl billentyűket a navigáláshoz, az enter-t a kiválasztáshoz.
|
||||
feedsearch.result_prefix=Az ön feliratkozásai:
|
||||
|
||||
settings.general=Általános
|
||||
settings.general.language=Nyelv
|
||||
settings.general.language.contribute=Segítsen a fordításban
|
||||
settings.general.show_unread=Mutassa azokat a hírcsatornákat és kategóriákat amelyekben nincsen olvasatlan bejegyzés
|
||||
settings.general.social_buttons=Mutassa a közösségi oldalak megosztás gombjait
|
||||
settings.general.scroll_marks=Kiterjesztett nézetben, görgetéssel olvasottként jelöli meg a bejegyzést
|
||||
settings.appearance=Megjelenés
|
||||
settings.theme=Téma
|
||||
settings.submit_your_theme=Küldje el a témáját
|
||||
settings.custom_css=Saját CSS
|
||||
|
||||
details.feed_details=Hírcsatorna részletei
|
||||
details.url=URL
|
||||
details.website=Website ####### Needs translation
|
||||
details.name=Név
|
||||
details.category=Kategória
|
||||
details.position=Position ####### Needs translation
|
||||
details.last_refresh=Utolsó frissítés
|
||||
details.message=Last refresh message ####### Needs translation
|
||||
details.next_refresh=Következő frissítés
|
||||
details.queued_for_refresh=Frissítésre vár
|
||||
details.feed_url=Hírcsatorna URL
|
||||
details.generate_api_key_first=A profiljában először egy API kulcsot kell generálnia.
|
||||
details.unsubscribe=Leiratkozás
|
||||
details.category_details=Kategória részletei
|
||||
details.parent_category=Szülő kategória
|
||||
|
||||
profile.user_name=Felhasználói név
|
||||
profile.email=E-mail
|
||||
profile.change_password=Jelszó megváltoztatás
|
||||
profile.confirm_password=Jelszó megerősítése
|
||||
profile.minimum_6_chars=Legalább 8 karakter
|
||||
profile.passwords_do_not_match=A jelszavak nem egyeznek
|
||||
profile.api_key=API kulcs
|
||||
profile.api_key_not_generated=Még nincsen generálva
|
||||
profile.generate_new_api_key=Új API kulcs generálása
|
||||
profile.generate_new_api_key_info=A jelszó megváltoztatása új API kulcsot generál
|
||||
profile.opml_export=OPML exportálása
|
||||
profile.delete_account=Fiók törlése
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Gyorsbillentyűk
|
||||
about.version=CommaFeed version ####### Needs translation
|
||||
about.line1_prefix=A CommaFeed egy nyílt forrású projekt. A forrás megtalálható a
|
||||
about.line1_suffix=oldalán.
|
||||
about.line2_prefix=Ha hibába ütközik, kérjük jelentse azt a
|
||||
about.line2_suffix=projekt oldalán.
|
||||
about.line3=Ha tetszik önnek ez a szolgáltatás, akkor kérjük támogassa a fejlesztőket és, hogy fentarthassák a weboldalt.
|
||||
about.line4=Akik jobban szeretnék az oldalt bitcon-nal támogatni, itt a cím
|
||||
about.goodies=Hasznos dolgok
|
||||
about.goodies.android_app=Android app ####### Needs translation
|
||||
about.goodies.subscribe_url=Feliratkozás az URL-re
|
||||
about.goodies.chrome_extension=Chrome bővítmény
|
||||
about.goodies.firefox_extension=Firefox kiterjesztés
|
||||
about.goodies.opera_extension=Opera kiterjesztés
|
||||
about.goodies.subscribe_bookmarklet=Feliratkozás bookmarklet hozzáadása (klikkeléssel)
|
||||
about.goodies.subscribe_bookmarklet_asc=Oldest first ####### Needs translation
|
||||
about.goodies.subscribe_bookmarklet_desc=Newest first ####### Needs translation
|
||||
about.goodies.next_unread_bookmarklet=Következő olvasatlan elem bookmarklet (húzza fel a könyvjelzősávba)
|
||||
about.translation=Fordítás
|
||||
about.translation.message=Segítségét kérjük a CommaFeed fordításához.
|
||||
about.translation.link=Nézze meg, hogyan tud segíteni ebben.
|
||||
about.announcements=Bejelentések
|
||||
about.rest_api.line1=A CommaFeed a JAX-RS-re és az AngularJS-re épül. Ezért a RESTA API elérhető.
|
||||
about.rest_api.link_to_documentation=Link a dokumentációhoz.
|
||||
|
||||
about.shortcuts.mouse_middleclick=középső egérgomb
|
||||
about.shortcuts.open_next_entry=következő hír megnyitása
|
||||
about.shortcuts.open_previous_entry=előző hír megnyitása
|
||||
about.shortcuts.spacebar=space/shift+space ####### Needs translation
|
||||
about.shortcuts.move_page_down_up=moves the page down/up ####### Needs translation
|
||||
about.shortcuts.focus_next_entry=megnyitás nélkül fókuszál a övetkező elemre
|
||||
about.shortcuts.focus_previous_entry=megnyitás nélkül fókuszál az előző elemre
|
||||
about.shortcuts.open_next_feed=a következő hírcsatorna vagy kategória megnyitása
|
||||
about.shortcuts.open_previous_feed=az előző hírcsatorna vagy kategória megnyitása
|
||||
about.shortcuts.open_close_current_entry=a jelenlegi elem megnyitása/bezárása
|
||||
about.shortcuts.open_current_entry_in_new_window=a jelenlegi elem megnyitása új ablakban
|
||||
about.shortcuts.open_current_entry_in_new_window_background=a jelenlegi elem megnyitása a háttérben, új ablakban
|
||||
about.shortcuts.star_unstar=hírelem csillagozása
|
||||
about.shortcuts.mark_current_entry=elem megjelölése olvasottként
|
||||
about.shortcuts.mark_all_as_read=az összes elem megjelölése olvasottként
|
||||
about.shortcuts.open_in_new_tab_mark_as_read=elem megnyitása új fülön és megjelölése olvasottként
|
||||
about.shortcuts.fullscreen=toggle full screen mode ####### Needs translation
|
||||
about.shortcuts.font_size=increase/decrease font size of the current entry ####### Needs translation
|
||||
about.shortcuts.go_to_all=go to the All view ####### Needs translation
|
||||
about.shortcuts.go_to_starred=go to the Starred view ####### Needs translation
|
||||
about.shortcuts.feed_search=név szerinti keresés a hírcsatornák között
|
||||
|
||||
global.save=Mentés
|
||||
global.cancel=Mégsem
|
||||
global.delete=Törlés
|
||||
global.required=Szükséges
|
||||
global.download=Letöltés
|
||||
global.link=Link
|
||||
global.bookmark=Könyvjelző
|
||||
global.close=Bezár
|
||||
global.tags=Címkék
|
||||
|
||||
tree.subscribe=Feliratkozás
|
||||
tree.import=Importálás
|
||||
tree.new_category=Új kategória
|
||||
tree.all=Összes
|
||||
tree.starred=Csillagozott
|
||||
|
||||
subscribe.feed_url=Hírcsatorna URL
|
||||
subscribe.feed_name=Hírcsatorna neve
|
||||
subscribe.category=Kategória
|
||||
|
||||
import.google_reader_prefix=Engedd meg, hogy importáljuk a hírcsatornáidat a
|
||||
import.google_reader_suffix= fiókjából.
|
||||
import.google_download=Alternatívaként, feltöltheti a subscriptions.xml fájlt.
|
||||
import.google_download_link=Letöltheti innen.
|
||||
import.xml_file=OPML Fájl
|
||||
|
||||
new_category.name=Név
|
||||
new_category.parent=Szülő
|
||||
|
||||
toolbar.unread=Olvasatlan
|
||||
toolbar.all=Összes
|
||||
toolbar.previous_entry=Előző elem
|
||||
toolbar.next_entry=Következő elem
|
||||
toolbar.refresh=Frissítés
|
||||
toolbar.refresh_all=Force refresh all my feeds ####### Needs translation
|
||||
toolbar.sort_by_asc_desc=Rendezés időrend szerint
|
||||
toolbar.titles_only=Csak cím
|
||||
toolbar.expanded_view=Részletes nézet
|
||||
toolbar.mark_all_as_read=Az összes megjelölése olvasottként
|
||||
toolbar.mark_all_older_12_hours=Régebbiek 12 óránál
|
||||
toolbar.mark_all_older_day=Régebbiek, mint egy nap
|
||||
toolbar.mark_all_older_week=Régebbiek, mint egy hét
|
||||
toolbar.mark_all_older_two_weeks=Régebbiek, mint két hét
|
||||
toolbar.settings=Beállítások
|
||||
toolbar.profile=Profil
|
||||
toolbar.admin=Admin
|
||||
toolbar.about=Névjegy
|
||||
toolbar.logout=Kilépés
|
||||
toolbar.donate=Anyagi támogatás
|
||||
|
||||
view.entry_source=forrás
|
||||
view.entry_author=szerző
|
||||
view.error_while_loading_feed=Hiba történt ennek a hírcsatornának a betöltésekor
|
||||
view.keep_unread=Megtartása olvasatlanként
|
||||
view.no_unread_items=nincsen olvasatlan eleme.
|
||||
view.mark_up_to_here=Megjelölés olvasottnak eddig
|
||||
view.search_for=keresés erre:
|
||||
view.no_search_results=Nem található semmi erre a keresett szóra
|
||||
|
||||
feedsearch.hint=Keressen a hírcsatornák között...
|
||||
feedsearch.help=Használja a nyíl billentyűket a navigáláshoz, az enter-t a kiválasztáshoz.
|
||||
feedsearch.result_prefix=Az ön feliratkozásai:
|
||||
|
||||
settings.general=Általános
|
||||
settings.general.language=Nyelv
|
||||
settings.general.language.contribute=Segítsen a fordításban
|
||||
settings.general.show_unread=Mutassa azokat a hírcsatornákat és kategóriákat amelyekben nincsen olvasatlan bejegyzés
|
||||
settings.general.social_buttons=Mutassa a közösségi oldalak megosztás gombjait
|
||||
settings.general.scroll_marks=Kiterjesztett nézetben, görgetéssel olvasottként jelöli meg a bejegyzést
|
||||
settings.appearance=Megjelenés
|
||||
settings.scroll_speed=A görgetés sebessége, amikor a cikkek között navigál (miliszekundumban)
|
||||
settings.scroll_speed.help=Írjon be 0-át a letiltáshoz
|
||||
settings.theme=Téma
|
||||
settings.submit_your_theme=Küldje el a témáját
|
||||
settings.custom_css=Saját CSS
|
||||
|
||||
details.feed_details=Hírcsatorna részletei
|
||||
details.url=URL
|
||||
details.website=Weboldal
|
||||
details.name=Név
|
||||
details.category=Kategória
|
||||
details.position=Pozició
|
||||
details.last_refresh=Utolsó frissítés
|
||||
details.message=Utolsó frissítési üzenet
|
||||
details.next_refresh=Következő frissítés
|
||||
details.queued_for_refresh=Frissítésre vár
|
||||
details.feed_url=Hírcsatorna URL
|
||||
details.generate_api_key_first=A profiljában először egy API kulcsot kell generálnia.
|
||||
details.unsubscribe=Leiratkozás
|
||||
details.unsubscribe_confirmation=Biztos, hogy le akar iratkozni errről a csatornáról?
|
||||
details.delete_category_confirmation=Biztos, hog törölni akarja ezt a kategóriát?
|
||||
details.category_details=Kategória részletei
|
||||
details.tag_details=Címke részletei
|
||||
details.parent_category=Szülő kategória
|
||||
|
||||
profile.user_name=Felhasználói név
|
||||
profile.email=E-mail
|
||||
profile.change_password=Jelszó megváltoztatás
|
||||
profile.confirm_password=Jelszó megerősítése
|
||||
profile.minimum_6_chars=Legalább 8 karakter
|
||||
profile.passwords_do_not_match=A jelszavak nem egyeznek
|
||||
profile.api_key=API kulcs
|
||||
profile.api_key_not_generated=Még nincsen generálva
|
||||
profile.generate_new_api_key=Új API kulcs generálása
|
||||
profile.generate_new_api_key_info=A jelszó megváltoztatása új API kulcsot generál
|
||||
profile.opml_export=OPML exportálása
|
||||
profile.delete_account=Fiók törlése
|
||||
profile.delete_account_confirmation=Törli a fiókját? Innen már nincs visszatérés!
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Gyorsbillentyűk
|
||||
about.version=CommaFeed verzió
|
||||
about.line1_prefix=A CommaFeed egy nyílt forrású projekt. A forrás megtalálható a
|
||||
about.line1_suffix=oldalán.
|
||||
about.line2_prefix=Ha hibába ütközik, kérjük jelentse azt a
|
||||
about.line2_suffix=projekt oldalán.
|
||||
about.line3=Ha tetszik önnek ez a szolgáltatás, akkor kérjük támogassa a fejlesztőket és, hogy fentarthassák a weboldalt.
|
||||
about.line4=Akik jobban szeretnék az oldalt bitcon-nal támogatni, itt a cím
|
||||
about.goodies=Hasznos dolgok
|
||||
about.goodies.android_app=Android alkalmazás
|
||||
about.goodies.subscribe_url=Feliratkozás az URL-re
|
||||
about.goodies.chrome_extension=Chrome bővítmény
|
||||
about.goodies.firefox_extension=Firefox kiterjesztés
|
||||
about.goodies.opera_extension=Opera kiterjesztés
|
||||
about.goodies.subscribe_bookmarklet=Feliratkozás bookmarklet hozzáadása (klikkeléssel)
|
||||
about.goodies.subscribe_bookmarklet_asc=Régebbiek először
|
||||
about.goodies.subscribe_bookmarklet_desc=Újak először
|
||||
about.goodies.next_unread_bookmarklet=Következő olvasatlan elem bookmarklet (húzza fel a könyvjelzősávba)
|
||||
about.translation=Fordítás
|
||||
about.translation.message=Segítségét kérjük a CommaFeed fordításához.
|
||||
about.translation.link=Nézze meg, hogyan tud segíteni ebben.
|
||||
about.announcements=Bejelentések
|
||||
about.rest_api.line1=A CommaFeed a JAX-RS-re és az AngularJS-re épül. Ezért a RESTA API elérhető.
|
||||
about.rest_api.link_to_documentation=Link a dokumentációhoz.
|
||||
|
||||
about.shortcuts.mouse_middleclick=középső egérgomb
|
||||
about.shortcuts.open_next_entry=következő hír megnyitása
|
||||
about.shortcuts.open_previous_entry=előző hír megnyitása
|
||||
about.shortcuts.spacebar=szóköz/shift+szóköz
|
||||
about.shortcuts.move_page_down_up=fel/le lépkedhet az oldalon
|
||||
about.shortcuts.focus_next_entry=megnyitás nélkül fókuszál a övetkező elemre
|
||||
about.shortcuts.focus_previous_entry=megnyitás nélkül fókuszál az előző elemre
|
||||
about.shortcuts.open_next_feed=a következő hírcsatorna vagy kategória megnyitása
|
||||
about.shortcuts.open_previous_feed=az előző hírcsatorna vagy kategória megnyitása
|
||||
about.shortcuts.open_close_current_entry=a jelenlegi elem megnyitása/bezárása
|
||||
about.shortcuts.open_current_entry_in_new_window=a jelenlegi elem megnyitása új ablakban
|
||||
about.shortcuts.open_current_entry_in_new_window_background=a jelenlegi elem megnyitása a háttérben, új ablakban
|
||||
about.shortcuts.star_unstar=hírelem csillagozása
|
||||
about.shortcuts.mark_current_entry=elem megjelölése olvasottként
|
||||
about.shortcuts.mark_all_as_read=az összes elem megjelölése olvasottként
|
||||
about.shortcuts.open_in_new_tab_mark_as_read=elem megnyitása új fülön és megjelölése olvasottként
|
||||
about.shortcuts.fullscreen=teljes képernyős mód bekapcsolása
|
||||
about.shortcuts.font_size=a jelenlegi elemnél növeli/csökkenti a betűméretet
|
||||
about.shortcuts.go_to_all=átkapcsol az Összes nézetre
|
||||
about.shortcuts.go_to_starred=átkapcsol a Csillagozott nézetre
|
||||
about.shortcuts.feed_search=név szerinti keresés a hírcsatornák között
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Download
|
||||
global.link=Link
|
||||
global.bookmark=Segnalibro
|
||||
global.close=Chiudi
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Iscriviti
|
||||
tree.import=Importa
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sort by date asc/desc
|
||||
toolbar.titles_only=Solo titoli
|
||||
toolbar.expanded_view=Espandi
|
||||
toolbar.mark_all_as_read=Contrassegna tutto come già letto
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Elementi più vecchi di un giorno
|
||||
toolbar.mark_all_older_week=Elementi più vecchi di una settimana
|
||||
toolbar.mark_all_older_two_weeks=Elementi più vecchi di due settimane
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Show feeds and categories with no unread entries
|
||||
settings.general.social_buttons=Visualizza i social button
|
||||
settings.general.scroll_marks=Marca come letto quando scorri
|
||||
settings.appearance=Appearance ####### Needs translation
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Tema
|
||||
settings.submit_your_theme=Sottoponi il tuo tema
|
||||
settings.custom_css=Css modificato
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=In attesa per l'aggiornamento
|
||||
details.feed_url=Feed URL
|
||||
details.generate_api_key_first=Generate an API key in your profile first.
|
||||
details.unsubscribe=Annulla l"'"iscrizione
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Dettagli categoria
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Parent category
|
||||
|
||||
profile.user_name=User name
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Genera una nuova chiave API
|
||||
profile.generate_new_api_key_info=Cambiando la password sarà generata una nuova chiave API
|
||||
profile.opml_export=Esporta OPML
|
||||
profile.delete_account=Elimina account
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Scorciatoie da tastiera
|
||||
|
||||
157
src/main/resources/i18n/ja.properties
Normal file
157
src/main/resources/i18n/ja.properties
Normal file
@@ -0,0 +1,157 @@
|
||||
global.save=保存
|
||||
global.cancel=取り消し
|
||||
global.delete=削除
|
||||
global.required=Required
|
||||
global.download=ダウンロード
|
||||
global.link=リンク
|
||||
global.bookmark=ブックマーク
|
||||
global.close=閉じる
|
||||
global.tags=タグ
|
||||
|
||||
tree.subscribe=購読
|
||||
tree.import=インポート
|
||||
tree.new_category=新しいカテゴリー
|
||||
tree.all=全て
|
||||
tree.starred=スター付
|
||||
|
||||
subscribe.feed_url=フィードURL
|
||||
subscribe.feed_name=フィード名
|
||||
subscribe.category=カテゴリー
|
||||
|
||||
import.google_reader_prefix=Googleアカウントからフィードを
|
||||
import.google_reader_suffix=インポートします。
|
||||
import.google_download=または、お持ちのsubscriptions.xmlファイルをアップロードします。
|
||||
import.google_download_link=このリンクからダウンロードして下さい。
|
||||
import.xml_file=OPMLファイル
|
||||
|
||||
new_category.name=名前
|
||||
new_category.parent=親カテゴリー
|
||||
|
||||
toolbar.unread=未読
|
||||
toolbar.all=全て
|
||||
toolbar.previous_entry=前のエントリー
|
||||
toolbar.next_entry=次のエントリー
|
||||
toolbar.refresh=更新
|
||||
toolbar.refresh_all=全てのフィードを更新
|
||||
toolbar.sort_by_asc_desc=昇順/降順にソート
|
||||
toolbar.titles_only=タイトルのみ
|
||||
toolbar.expanded_view=拡張ビュー
|
||||
toolbar.mark_all_as_read=全て既読にする
|
||||
toolbar.mark_all_older_12_hours=12時間以上前のアイテム
|
||||
toolbar.mark_all_older_day=前日より前のアイテム
|
||||
toolbar.mark_all_older_week=1週間以上前のアイテム
|
||||
toolbar.mark_all_older_two_weeks=2週間以上前のアイテム
|
||||
toolbar.settings=設定
|
||||
toolbar.profile=Profile
|
||||
toolbar.admin=管理者
|
||||
toolbar.about=About
|
||||
toolbar.logout=ログアウト
|
||||
toolbar.donate=寄付
|
||||
|
||||
view.entry_source= より
|
||||
view.entry_author= 著者
|
||||
view.error_while_loading_feed=フィード読み込み中にエラーが発生しました。
|
||||
view.keep_unread=未読として保持
|
||||
view.no_unread_items=未読アイテムはありません。
|
||||
view.mark_up_to_here=ここまで既読
|
||||
view.search_for=検索:
|
||||
view.no_search_results=検索結果はありません。
|
||||
|
||||
feedsearch.hint=購読フィードを入力...
|
||||
feedsearch.help=Enterキーで選択、矢印キーで移動します。
|
||||
feedsearch.result_prefix=見つかった購読フィード:
|
||||
|
||||
settings.general=一般
|
||||
settings.general.language=言語
|
||||
settings.general.language.contribute=翻訳に貢献する
|
||||
settings.general.show_unread=未読エントリーのないフィードとカテゴリーを表示
|
||||
settings.general.social_buttons=共有ボタンを表示
|
||||
settings.general.scroll_marks=拡張ビューではエントリーのスクロールで既読にする
|
||||
settings.appearance=外観
|
||||
settings.scroll_speed=エントリー間のスクロールスピード(ミリ秒)
|
||||
settings.scroll_speed.help=0に設定すると無効になります
|
||||
settings.theme=テーマ
|
||||
settings.submit_your_theme=テーマを登録する
|
||||
settings.custom_css=カスタムCSS
|
||||
|
||||
details.feed_details=フィードの詳細
|
||||
details.url=URL
|
||||
details.website=Webサイト
|
||||
details.name=名前
|
||||
details.category=カテゴリー
|
||||
details.position=位置
|
||||
details.last_refresh=最終更新
|
||||
details.message=最終更新メッセージ
|
||||
details.next_refresh=次回更新
|
||||
details.queued_for_refresh=更新キュー
|
||||
details.feed_url=フィードURL
|
||||
details.generate_api_key_first=最初にあなたのAPIキーを生成して下さい。
|
||||
details.unsubscribe=購読解除
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=カテゴリー詳細
|
||||
details.tag_details=タグ詳細
|
||||
details.parent_category=親カテゴリー
|
||||
|
||||
profile.user_name=ユーザ名
|
||||
profile.email=E-mail
|
||||
profile.change_password=パスワードの変更
|
||||
profile.confirm_password=変更パスワードの確認
|
||||
profile.minimum_6_chars=6文字以上
|
||||
profile.passwords_do_not_match=パスワードが一致しません
|
||||
profile.api_key=APIキー
|
||||
profile.api_key_not_generated=APIキーが生成されていません
|
||||
profile.generate_new_api_key=新しいAPIキーを生成
|
||||
profile.generate_new_api_key_info=パスワードの変更は新しいAPIキーが生成されます
|
||||
profile.opml_export=OPMLエクスポート
|
||||
profile.delete_account=アカウント削除
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=キーボードショートカット
|
||||
about.version=CommaFeedバージョン
|
||||
about.line1_prefix=CommaFeedはオープンソースプロジェクトです。ソースは
|
||||
about.line1_suffix=にホスティングされています。
|
||||
about.line2_prefix=もし問題を登録したい場合、
|
||||
about.line2_suffix=プロジェクトのissuesページに報告して下さい。
|
||||
about.line3=このプロジェクトを気に入った場合、開発者やWebサイトの運営コストをサポートするための寄付を検討して下さいね。
|
||||
about.line4=Bitcoinなら寄付できる方、アドレスはこちらです。
|
||||
about.goodies=Goodies
|
||||
about.goodies.android_app=Androidアプリ
|
||||
about.goodies.subscribe_url=購読URL
|
||||
about.goodies.chrome_extension=Chrome拡張
|
||||
about.goodies.firefox_extension=Firefox拡張
|
||||
about.goodies.opera_extension=Opera拡張
|
||||
about.goodies.subscribe_bookmarklet=購読ブックマークレットを追加(クリック)
|
||||
about.goodies.subscribe_bookmarklet_asc=古い順
|
||||
about.goodies.subscribe_bookmarklet_desc=新しい順
|
||||
about.goodies.next_unread_bookmarklet=次の未読アイテムブックマークレット(ブックマークバーへドラッグ)
|
||||
about.translation=翻訳
|
||||
about.translation.message=CommaFeedの翻訳に助けが必要です!
|
||||
about.translation.link=どうやって翻訳に貢献できるか見て下さい。
|
||||
about.announcements=Announcements
|
||||
about.rest_api.line1=CommaFeedはJAX-RSとAngularJSを使用しているので、REST APIも利用可能です。
|
||||
about.rest_api.link_to_documentation=ドキュメントへのリンク
|
||||
|
||||
about.shortcuts.mouse_middleclick=中クリック
|
||||
about.shortcuts.open_next_entry=次のエントリーを開く
|
||||
about.shortcuts.open_previous_entry=前のエントリーを開く
|
||||
about.shortcuts.spacebar=space/shift+space
|
||||
about.shortcuts.move_page_down_up=ページ移動
|
||||
about.shortcuts.focus_next_entry=次のエントリーを開かずにフォーカス移動
|
||||
about.shortcuts.focus_previous_entry=前のエントリーを開かずにフォーカス移動
|
||||
about.shortcuts.open_next_feed=次のフィード/カテゴリーを開く
|
||||
about.shortcuts.open_previous_feed=前のフィード/カテゴリーを開く
|
||||
about.shortcuts.open_close_current_entry=現在のエントリーを開く/閉じる
|
||||
about.shortcuts.open_current_entry_in_new_window=現在のエントリーを新しいウィンドウで開く
|
||||
about.shortcuts.open_current_entry_in_new_window_background=現在のエントリーを新しいバックグラウンドウィンドウで開く
|
||||
about.shortcuts.star_unstar=現在のエントリーにスターを付ける/解除する
|
||||
about.shortcuts.mark_current_entry=現在のエントリーを既読/未読にする
|
||||
about.shortcuts.mark_all_as_read=全エントリーを既読にする
|
||||
about.shortcuts.open_in_new_tab_mark_as_read=エントリーを既読にして新しいタブで開く
|
||||
about.shortcuts.fullscreen=フルスクリーントグル
|
||||
about.shortcuts.font_size=現在のエントリーのフォントサイズを大きく/小さくする
|
||||
about.shortcuts.go_to_all=All viewに変更する
|
||||
about.shortcuts.go_to_starred=スター付きviewに変更する
|
||||
about.shortcuts.feed_search=購読画面(subscription nameの入力)に移動する
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Download ####### Needs translation
|
||||
global.link=Link ####### Needs translation
|
||||
global.bookmark=Bookmark ####### Needs translation
|
||||
global.close=Close ####### Needs translation
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=구독
|
||||
tree.import=임포트
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sort by date asc/desc ####### Needs translation
|
||||
toolbar.titles_only=Titles only ####### Needs translation
|
||||
toolbar.expanded_view=Expanded view ####### Needs translation
|
||||
toolbar.mark_all_as_read=읽음표시
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Items older than a day ####### Needs translation
|
||||
toolbar.mark_all_older_week=Items older than a week ####### Needs translation
|
||||
toolbar.mark_all_older_two_weeks=Items older than two weeks ####### Needs translation
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=안읽은 항목들이 있는 피드와 카테고
|
||||
settings.general.social_buttons=소셜미디아 버튼들 보여주기
|
||||
settings.general.scroll_marks=Expanded View에서 스크롤하면 항목들을 읽음으로 저장하기
|
||||
settings.appearance=Appearance ####### Needs translation
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Theme ####### Needs translation
|
||||
settings.submit_your_theme=Submit your theme ####### Needs translation
|
||||
settings.custom_css=커스톰 CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Queued for refresh ####### Needs translation
|
||||
details.feed_url=피드 유알엘
|
||||
details.generate_api_key_first=당신의 프로필을 위해 API Key를 먼저 생성하세요.
|
||||
details.unsubscribe=주소 삭제
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=카테고리 세부
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=부모 카테고리
|
||||
|
||||
profile.user_name=사용자 이름
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=API Key 생성하기
|
||||
profile.generate_new_api_key_info=비밀번호를 변경하면 새로운 API Key가 생성됩니다.
|
||||
profile.opml_export=OPML export ####### Needs translation
|
||||
profile.delete_account=프로필삭제
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=단축기
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
ar=العربية
|
||||
ca=Català
|
||||
en=English
|
||||
es=Español
|
||||
de=Deutsch
|
||||
@@ -7,6 +8,7 @@ fr=Français
|
||||
gl=Galician
|
||||
glk=گیلکی
|
||||
hu=Magyar
|
||||
ja=日本語
|
||||
ko=한국어
|
||||
nl=Nederlands
|
||||
nb=Norsk (bokmål)
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Muat turun
|
||||
global.link=Pautan
|
||||
global.bookmark=Bookmark
|
||||
global.close=Tutup
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Langgan
|
||||
tree.import=Import
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Aturkan mengikut tarikh (baru/lama)
|
||||
toolbar.titles_only=Tajuk sahaja
|
||||
toolbar.expanded_view=Wide view
|
||||
toolbar.mark_all_as_read=Tanda kesemuanya telah dibaca
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Lebih lama daripada sehari
|
||||
toolbar.mark_all_older_week=Lebih lama daripada seminggu
|
||||
toolbar.mark_all_older_two_weeks=Lebih lama daripada dua minggu
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Tunjuk semua feed dan kategori yang telah dibaca
|
||||
settings.general.social_buttons=Tunjuk social sharing
|
||||
settings.general.scroll_marks=Dalam wide view, tanda item dibaca ketika scrolling
|
||||
settings.appearance=Rupa
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Tema
|
||||
settings.submit_your_theme=Muat naik tema anda
|
||||
settings.custom_css=Custom CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Diaturkan untuk refresh
|
||||
details.feed_url=URL Feed
|
||||
details.generate_api_key_first=Janakan API key dalam profil anda dahulu.
|
||||
details.unsubscribe=Hentikan langganan
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Butir-butir kategori
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Kategori induk
|
||||
|
||||
profile.user_name=User name
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Jana API key baru
|
||||
profile.generate_new_api_key_info=Pertukaran kata laluan akan menjanakan API key yang baru
|
||||
profile.opml_export=Export OPML
|
||||
profile.delete_account=Padam akaun
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Pintasan papan kekunci
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Last ned
|
||||
global.link=Lenke
|
||||
global.bookmark=Bokmerke
|
||||
global.close=Lukk
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Abonner
|
||||
tree.import=Importer
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sorter etter dato ny/gammel
|
||||
toolbar.titles_only=Kun titler
|
||||
toolbar.expanded_view=Utvidet visning
|
||||
toolbar.mark_all_as_read=Merk alle som lest
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Artikler eldre enn én dag
|
||||
toolbar.mark_all_older_week=Artikler eldre enn én uke
|
||||
toolbar.mark_all_older_two_weeks=Artikler eldre enn to uker
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Vis abonnementer og kategorier uten nye artikler
|
||||
settings.general.social_buttons=Vis delingsknapper
|
||||
settings.general.scroll_marks=I utvidet visning, merk artikler som leste når du blar deg forbi dem.
|
||||
settings.appearance=Utseende
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Drakt
|
||||
settings.submit_your_theme=Legg til egen drakt
|
||||
settings.custom_css=Egendefinert CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=I kø for oppdatering
|
||||
details.feed_url=URL for abonnement
|
||||
details.generate_api_key_first=Generer en API-nøkkel under profilinnstillinger først.
|
||||
details.unsubscribe=Avslutt abonnement
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Kategoridetaljer
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Overordnet kategori
|
||||
|
||||
profile.user_name=Brukernavn
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Generer ny API-nøkkel
|
||||
profile.generate_new_api_key_info=Endring av passord vil generere en ny API-nøkkel
|
||||
profile.opml_export=OPML-eksport
|
||||
profile.delete_account=Slett bruker
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Hurtigtaster
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Download
|
||||
global.link=Link
|
||||
global.bookmark=Bookmark
|
||||
global.close=Sluiten
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Abonneer
|
||||
tree.import=Importeer
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sorteer op datum opl/afl
|
||||
toolbar.titles_only=Alleen titels
|
||||
toolbar.expanded_view=Uitgebreide weergave
|
||||
toolbar.mark_all_as_read=Markeer alles als gelezen
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Artikelen ouder dan een dag
|
||||
toolbar.mark_all_older_week=Artikelen ouder dan een week
|
||||
toolbar.mark_all_older_two_weeks=Artikelen ouder dan twee weken
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Laat feeds en categorieën zonder ongelezen artikel
|
||||
settings.general.social_buttons=Laat Social Media knoppen zien
|
||||
settings.general.scroll_marks=Markeer artikelen als gelezen, wanneer je er doorheen scrollt
|
||||
settings.appearance=Uiterlijk
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Thema
|
||||
settings.submit_your_theme=Stuur thema in
|
||||
settings.custom_css=Custom CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=In wachtrij voor vernieuwing
|
||||
details.feed_url=Feed URL
|
||||
details.generate_api_key_first=Genereer eerst een API sleutel in je profiel.
|
||||
details.unsubscribe=Abonnement opzeggen
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Categorie details
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Bovenliggende categorie
|
||||
|
||||
profile.user_name=Gebruikersnaam
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Genereer nieuwe API sleutel
|
||||
profile.generate_new_api_key_info=Het veranderen van het wachtwoord genereert een nieuwe API sleutel
|
||||
profile.opml_export=OPML export
|
||||
profile.delete_account=Verwijder account
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Sneltoetsen
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Last ned
|
||||
global.link=Lenkje
|
||||
global.bookmark=Bokmerke
|
||||
global.close=Lukk
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Abonner
|
||||
tree.import=Importer
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sorter etter dato ny/gamal
|
||||
toolbar.titles_only=Berre titlar
|
||||
toolbar.expanded_view=Utvida visning
|
||||
toolbar.mark_all_as_read=Merk alle som lesne
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Artiklar eldre enn éin dag
|
||||
toolbar.mark_all_older_week=Artiklar eldre enn éi veke
|
||||
toolbar.mark_all_older_two_weeks=Artiklar eldre enn to veker
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Vis abonnement og kategoriar utan nye artiklar
|
||||
settings.general.social_buttons=Vis delingsknappar
|
||||
settings.general.scroll_marks=I utvida visning, merk artiklar som lesne når du blar deg forbi dei.
|
||||
settings.appearance=Utsjånad
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Drakt
|
||||
settings.submit_your_theme=Legg til eiga drakt
|
||||
settings.custom_css=Skreddarsydd CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=I kø for oppdatering
|
||||
details.feed_url=URL for abonnement
|
||||
details.generate_api_key_first=Generer ein API-nykel under profilinnstillingar fyrst.
|
||||
details.unsubscribe=Avslutt abonnement
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Kategoridetaljar
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Overordna kategori
|
||||
|
||||
profile.user_name=Brukarnamn
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Generer ny API-nykel
|
||||
profile.generate_new_api_key_info=Endring av passord vil generere ein ny API-nykel
|
||||
profile.opml_export=OPML-eksport
|
||||
profile.delete_account=Slett brukar
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Hurtigtastar
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Pobierz
|
||||
global.link=Odnośnik
|
||||
global.bookmark=Zakładka
|
||||
global.close=Zamknij
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Subskrybuj
|
||||
tree.import=Importuj
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sortuj od najnowszego/najstarszego
|
||||
toolbar.titles_only=Widok listy
|
||||
toolbar.expanded_view=Widok rozwinięty
|
||||
toolbar.mark_all_as_read=Oznacz wszystko jako przeczytane
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Elementy starsze niż dzień
|
||||
toolbar.mark_all_older_week=Elementy starsze niż tydzień
|
||||
toolbar.mark_all_older_two_weeks=Elementy starsze niż dwa tygodnie
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Pokaż kanały i kategorie bez nieprzeczytanych ele
|
||||
settings.general.social_buttons=Pokaż przyciski udostępniania
|
||||
settings.general.scroll_marks=W widoku rozwiniętym przewijanie oznacza elementy jako przeczytane
|
||||
settings.appearance=Wygląd
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Motyw
|
||||
settings.submit_your_theme=Wyślij swój motyw
|
||||
settings.custom_css=Własny styl CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=W kolejce do odświeżenia
|
||||
details.feed_url=URL kanału
|
||||
details.generate_api_key_first=Najpierw wygeneruj klucz API w swoim profilu.
|
||||
details.unsubscribe=Cofnij subskrypcje
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Szczegóły kategorii
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Kategoria nadrzędna
|
||||
|
||||
profile.user_name=Nazwa użytkownika
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Wygeneruj nowy klucz API
|
||||
profile.generate_new_api_key_info=Zmiana hasła spowoduje wygenerowanie nowego klucza API
|
||||
profile.opml_export=Eksportuj do pliku OPML
|
||||
profile.delete_account=Usuń konto
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Skróty klawiszowe
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Download
|
||||
global.link=Link
|
||||
global.bookmark=Favorito
|
||||
global.close=Fechar
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Inscrever-se
|
||||
tree.import=Importar
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Ordenar por data cresc/decres
|
||||
toolbar.titles_only=Somente títulos
|
||||
toolbar.expanded_view=Modo Expandido
|
||||
toolbar.mark_all_as_read=Marcar tudo como lido
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Itens mais antigos que um dia
|
||||
toolbar.mark_all_older_week=Itens mais antigos que uma semana
|
||||
toolbar.mark_all_older_two_weeks=Itens mais antigos que duas semanas
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Mostrar feeds e categorias sem itens não lidos
|
||||
settings.general.social_buttons=Mostrar botões de mídias sociais
|
||||
settings.general.scroll_marks=No modo expandido, percorrer os itens marca-os como lidos
|
||||
settings.appearance=Aparência
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Tema
|
||||
settings.submit_your_theme=Envie seu tema
|
||||
settings.custom_css=CSS personalizado
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Na fila para atualizar
|
||||
details.feed_url=URL do feed
|
||||
details.generate_api_key_first=Gerar uma chave de API em seu perfil primeiro.
|
||||
details.unsubscribe=Cancelar inscrição
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Detalhes da categoria
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Categoria pai
|
||||
|
||||
profile.user_name=Nome de usuário
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Gerar nova chave de API
|
||||
profile.generate_new_api_key_info=Mudar a senha irá gerar uma nova chave de API
|
||||
profile.opml_export=Exportar OPML
|
||||
profile.delete_account=Excluir conta
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=API REST
|
||||
about.keyboard_shortcuts=Atalhos de teclado
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Скачать
|
||||
global.link=Ссылка
|
||||
global.bookmark=Закладка
|
||||
global.close=Закрыть
|
||||
global.tags=Теги
|
||||
|
||||
tree.subscribe=Подписаться
|
||||
tree.import=Импорт
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Сначала новые/старые
|
||||
toolbar.titles_only=Только заголовки
|
||||
toolbar.expanded_view=Развёрнутый вид
|
||||
toolbar.mark_all_as_read=Отметить всё как прочитанное
|
||||
toolbar.mark_all_older_12_hours=Записи старше 12-и часов
|
||||
toolbar.mark_all_older_day=Записи старше суток
|
||||
toolbar.mark_all_older_week=Записи старше недели
|
||||
toolbar.mark_all_older_two_weeks=Записи старше двух недель
|
||||
@@ -52,8 +54,8 @@ view.error_while_loading_feed=Не удалось загрузить ленту
|
||||
view.keep_unread=Оставить непрочитанным
|
||||
view.no_unread_items=нет непрочитанных записей.
|
||||
view.mark_up_to_here=Отметить прочитанным до сюда
|
||||
view.search_for=searching for: ####### Needs translation
|
||||
view.no_search_results=No match found for the requested keywords ####### Needs translation
|
||||
view.search_for=искать:
|
||||
view.no_search_results=По данному запросу ничего не найдено.
|
||||
|
||||
feedsearch.hint=Введите подписку...
|
||||
feedsearch.help=Используйте клавишу ввода для выбора и стрелки для перемещения.
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Показывать прочтённые лент
|
||||
settings.general.social_buttons=Показывать социальные кнопки
|
||||
settings.general.scroll_marks=В развёрнутом виде помечать записи как прочитанные по мере прокрутки
|
||||
settings.appearance=Вид
|
||||
settings.scroll_speed=Скорость прокрутки при навигации между записями (в миллисекундах)
|
||||
settings.scroll_speed.help=смените на 0 чтобы выключить
|
||||
settings.theme=Тема
|
||||
settings.submit_your_theme=Добавьте свою тему
|
||||
settings.custom_css=Собственная CSS
|
||||
@@ -77,13 +81,16 @@ details.name=Название
|
||||
details.category=Категория
|
||||
details.position=Позиция
|
||||
details.last_refresh=Последнее обновление
|
||||
details.message=Last refresh message ####### Needs translation
|
||||
details.message=Сообщение последнего обновления
|
||||
details.next_refresh=Следующее обновление
|
||||
details.queued_for_refresh=В очереди на обновление
|
||||
details.feed_url=Адрес ленты
|
||||
details.generate_api_key_first=Сначала сгенерируйте API-ключ в вашем профиле.
|
||||
details.unsubscribe=Отписаться
|
||||
details.unsubscribe_confirmation=Подтвердить отписку от этой ленты? Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Подтвердить удаление этой категории?
|
||||
details.category_details=Информация о категории
|
||||
details.tag_details=Детали тега
|
||||
details.parent_category=Родительская категория
|
||||
|
||||
profile.user_name=Имя пользователя
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Сгенерировать новый API-ключ
|
||||
profile.generate_new_api_key_info=После изменения пароля, API-ключ изменится
|
||||
profile.opml_export=Экспорт OPML
|
||||
profile.delete_account=Удалить аккаунт
|
||||
profile.delete_account_confirmation=Удалить ваш аккаунт? Назад пути не будет!
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Горячие клавиши
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Stiahnuť
|
||||
global.link=Link
|
||||
global.bookmark=Záložky
|
||||
global.close=Zavrieť
|
||||
global.tags=Tagy
|
||||
|
||||
tree.subscribe=Odoberať
|
||||
tree.import=Importovať
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Zoradiť podľa najnovšieho/najstaršieho
|
||||
toolbar.titles_only=Náhľad titulkov
|
||||
toolbar.expanded_view=Rozšírený náhľad
|
||||
toolbar.mark_all_as_read=Označiť všetky ako prečítané
|
||||
toolbar.mark_all_older_12_hours=Položky staršie ako 12 hodín
|
||||
toolbar.mark_all_older_day=Položky staršie ako deň
|
||||
toolbar.mark_all_older_week=Položky staršie ako týždeň
|
||||
toolbar.mark_all_older_two_weeks=Položky staršie ako dva týždne
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Zobraziť príspevky a kategórie bez neprečítan
|
||||
settings.general.social_buttons=Zobraziť možnosti zdieľania
|
||||
settings.general.scroll_marks=Scrollovanie v rozšírenom náhľade označí položky ako prečítané
|
||||
settings.appearance=Vzhľad
|
||||
settings.scroll_speed=Rýchlosť skrolovania—pohybu medzi položkami (v milisekundách)
|
||||
settings.scroll_speed.help=nastavte 0 pre deaktiváciu
|
||||
settings.theme=Motív
|
||||
settings.submit_your_theme=Nahrať vlastný motív vzhľadu
|
||||
settings.custom_css=Vlastný motív vzhľadu (CSS)
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Vo fronte
|
||||
details.feed_url=URL RSS zdroja
|
||||
details.generate_api_key_first=Vygenerujte si API kľúč vo vašom profile.
|
||||
details.unsubscribe=Zrušiť odoberanie.
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Detaily kategórie
|
||||
details.tag_details=Detaily tagu
|
||||
details.parent_category=Hlavná kategória
|
||||
|
||||
profile.user_name=Uživateľské meno
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Vygenerovať nový API kľúč
|
||||
profile.generate_new_api_key_info=Zmenou hesla vygenerujete nový API kľúč
|
||||
profile.opml_export=exportovať do formátu OPML
|
||||
profile.delete_account=Odstrániť účet
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Klávesové skratky
|
||||
@@ -146,3 +154,4 @@ about.shortcuts.font_size=zmeniť veľkosť písma pre vybranú položku
|
||||
about.shortcuts.go_to_all=zobraziť všetky položky
|
||||
about.shortcuts.go_to_starred=zobraziť obľúbené položiek
|
||||
about.shortcuts.feed_search=presun na odoberaný RSS zdroj vložením jeho názvu
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=Ladda ned
|
||||
global.link=Länka
|
||||
global.bookmark=Bokmärk
|
||||
global.close=Stäng
|
||||
global.tags=Taggar
|
||||
|
||||
tree.subscribe=Prenumerera
|
||||
tree.import=Importera
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Sortera efter datum stigande/fallande
|
||||
toolbar.titles_only=Endast titlar
|
||||
toolbar.expanded_view=Expanderad vy
|
||||
toolbar.mark_all_as_read=Markera alla som lästa
|
||||
toolbar.mark_all_older_12_hours=Poster äldre än 12 timmar
|
||||
toolbar.mark_all_older_day=Poster äldre än en dag
|
||||
toolbar.mark_all_older_week=Poster äldre än en vecka
|
||||
toolbar.mark_all_older_two_weeks=Poster äldre än två veckor
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Visa prenumerationer och kategorier utan olästa po
|
||||
settings.general.social_buttons=Visa delningsknappar
|
||||
settings.general.scroll_marks=I expanderad vy, markera poster som lästa genom att scrolla förbi dem
|
||||
settings.appearance=Utseende
|
||||
settings.scroll_speed=Scrollhastighet under navigation mellan poster (i millisekunder)
|
||||
settings.scroll_speed.help=ställ på 0 för att avaktivera
|
||||
settings.theme=Tema
|
||||
settings.submit_your_theme=Skicka in ditt tema
|
||||
settings.custom_css=Anpassad CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=I kö för uppdatering
|
||||
details.feed_url=Prenumerationens URL
|
||||
details.generate_api_key_first=Skapa en API-nyckel på din profil först.
|
||||
details.unsubscribe=Avprenumerera
|
||||
details.unsubscribe_confirmation=Är du säker på att du vill avprenumerera?
|
||||
details.delete_category_confirmation=Är du säker på att du vill ta bort denna kategori?
|
||||
details.category_details=Kategoridetaljer
|
||||
details.tag_details=Taggdetaljer
|
||||
details.parent_category=Överordnad kategori
|
||||
|
||||
profile.user_name=Användarnamn
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Skapa ny API-nyckel
|
||||
profile.generate_new_api_key_info=Lösenordsbyte skapar ny API-nyckel
|
||||
profile.opml_export=OPML-export
|
||||
profile.delete_account=Radera konto
|
||||
profile.delete_account_confirmation=Vill du ta bort ditt konto? Det försvinner för alltid!
|
||||
|
||||
about.rest_api=REST-API
|
||||
about.keyboard_shortcuts=Tangentbordsgenvägar
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=İndir
|
||||
global.link=Bağlantı
|
||||
global.bookmark=Yer imi
|
||||
global.close=Kapat
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=Abone ol
|
||||
tree.import=İçe aktar
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=Tarihe göre sırala artan/azalan
|
||||
toolbar.titles_only=Sadece başlıklar
|
||||
toolbar.expanded_view=Genişletilmiş görünüm
|
||||
toolbar.mark_all_as_read=Tümünü okundu işaretle
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=Bir günden eski yazılar
|
||||
toolbar.mark_all_older_week=Bir haftadan eski yazılar
|
||||
toolbar.mark_all_older_two_weeks=İki haftadan eski yazılar
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=Okunmamış öğesi bulunan yayın ve kategorileri
|
||||
settings.general.social_buttons=Sosyal paylaşım butonlarını göster
|
||||
settings.general.scroll_marks=Genişletilmiş görünümde götüntülenen iletileri okunmuş işaretle
|
||||
settings.appearance=Görünüm
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=Tema
|
||||
settings.submit_your_theme=Tema gönder
|
||||
settings.custom_css=Kişiselleştirilmiş CSS
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=Yenilenmek üzere kuyrukta
|
||||
details.feed_url=Yayın URL'si
|
||||
details.generate_api_key_first=Öncelikle profilinizden bir API anahtarı oluşturun.
|
||||
details.unsubscribe=Aboneliği iptal et
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=Kategori detayları
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Üst kategori
|
||||
|
||||
profile.user_name=Kullanıcı adı
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=Yeni bir API anahtarı oluştur
|
||||
profile.generate_new_api_key_info=Şifre değiştirmek API anahtarının da değiştirilmesine neden olcak.
|
||||
profile.opml_export=OPML dışa aktar
|
||||
profile.delete_account=Hesabı sil
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=REST API
|
||||
about.keyboard_shortcuts=Klavye kısayolları
|
||||
|
||||
@@ -6,6 +6,7 @@ global.download=下载
|
||||
global.link=链接
|
||||
global.bookmark=书签
|
||||
global.close=关闭
|
||||
global.tags=Tags ####### Needs translation
|
||||
|
||||
tree.subscribe=订阅
|
||||
tree.import=导入
|
||||
@@ -36,6 +37,7 @@ toolbar.sort_by_asc_desc=按日期升序/降序排序
|
||||
toolbar.titles_only=仅显示标题
|
||||
toolbar.expanded_view=显示内容
|
||||
toolbar.mark_all_as_read=标记所有为已读
|
||||
toolbar.mark_all_older_12_hours=Items older than 12 hours ####### Needs translation
|
||||
toolbar.mark_all_older_day=早于一天的条目
|
||||
toolbar.mark_all_older_week=早于一周的条目
|
||||
toolbar.mark_all_older_two_weeks=早于两周的条目
|
||||
@@ -66,6 +68,8 @@ settings.general.show_unread=显示未读的订阅和目录条目
|
||||
settings.general.social_buttons=显示分享按钮
|
||||
settings.general.scroll_marks=在扩展视图中,可滚动条目将其标记为已读
|
||||
settings.appearance=外观
|
||||
settings.scroll_speed=Scrolling speed when navigating between entries (in milliseconds) ####### Needs translation
|
||||
settings.scroll_speed.help=set to 0 to disable ####### Needs translation
|
||||
settings.theme=主题
|
||||
settings.submit_your_theme=提交你的主题
|
||||
settings.custom_css=自定义 CSS 样式
|
||||
@@ -83,7 +87,10 @@ details.queued_for_refresh=放入等待刷新的队列
|
||||
details.feed_url=订阅地址
|
||||
details.generate_api_key_first=在您的配置文件中首先生成一个 API 密钥。
|
||||
details.unsubscribe=取消订阅
|
||||
details.unsubscribe_confirmation=Are you sure you want to unsubscribe from this feed? ####### Needs translation
|
||||
details.delete_category_confirmation=Are you sure you want to delete this category? ####### Needs translation
|
||||
details.category_details=目录详情
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=上一层目录
|
||||
|
||||
profile.user_name=用户名
|
||||
@@ -98,6 +105,7 @@ profile.generate_new_api_key=生成一个新的 API 密钥
|
||||
profile.generate_new_api_key_info=修改密码将会生成一个新的的 API 密钥
|
||||
profile.opml_export=导出 OPML
|
||||
profile.delete_account=删除帐号
|
||||
profile.delete_account_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
about.rest_api=重置 API
|
||||
about.keyboard_shortcuts=快捷键
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
# tomee.jaxws.subcontext = webservices
|
||||
# tomee.jaxws.oldsubcontext = false
|
||||
|
||||
# openejb.system.apps = true
|
||||
|
||||
# openejb.servicemanager.enabled = true
|
||||
# openejb.jmx.active = false
|
||||
|
||||
# openejb.descriptors.output = false
|
||||
# openejb.strict.interface.declaration = false
|
||||
# openejb.conf.file = conf/tomee.xml
|
||||
@@ -31,7 +31,6 @@
|
||||
# org.apache.openejb.server.webservices.saaj.provider =
|
||||
# openejb.nobanner = true
|
||||
# openejb.offline = false
|
||||
# openejb.jmx.active = true
|
||||
# openejb.exclude-include.order = include-exclude
|
||||
# openejb.additional.exclude =
|
||||
# openejb.additional.include =
|
||||
@@ -46,5 +45,9 @@
|
||||
# javax.persistence.jtaDataSource =
|
||||
# javax.persistence.nonJtaDataSource =
|
||||
|
||||
openejb.system.apps = false
|
||||
openejb.jmx.active = false
|
||||
|
||||
AsynchronousPool.CorePoolSize = 1
|
||||
AsynchronousPool.MaximumPoolSize = 100
|
||||
AsynchronousPool.MaximumPoolSize = 100
|
||||
openejb.stats.interceptor.disable = true
|
||||
@@ -7,6 +7,7 @@
|
||||
UserName = sa
|
||||
Password =
|
||||
MaxActive = 50
|
||||
DataSourceCreator bonecp
|
||||
</Resource>
|
||||
|
||||
<!--
|
||||
@@ -16,6 +17,7 @@
|
||||
UserName cf
|
||||
Password cf
|
||||
MaxActive 50
|
||||
DataSourceCreator bonecp
|
||||
</Resource>
|
||||
-->
|
||||
|
||||
@@ -26,6 +28,7 @@
|
||||
UserName cf
|
||||
Password cf
|
||||
MaxActive 50
|
||||
DataSourceCreator bonecp
|
||||
</Resource>
|
||||
-->
|
||||
|
||||
@@ -36,6 +39,7 @@
|
||||
UserName cf
|
||||
Password cf
|
||||
MaxActive 50
|
||||
DataSourceCreator bonecp
|
||||
</Resource>
|
||||
-->
|
||||
|
||||
|
||||
7
src/main/webapp/WEB-INF/scan.xml
Normal file
7
src/main/webapp/WEB-INF/scan.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<scan>
|
||||
<packages>
|
||||
<package>com.commafeed.backend</package>
|
||||
<package>com.commafeed.frontend.rest</package>
|
||||
</packages>
|
||||
</scan>
|
||||
@@ -2,26 +2,51 @@
|
||||
<groups xmlns="http://www.isdc.ro/wro">
|
||||
|
||||
<group name="lib">
|
||||
<js minimize="false">/vendor/jquery/*.js</js>
|
||||
<js minimize="false">/vendor/lodash/*.js</js>
|
||||
<js minimize="false">webjar:lodash.min.js</js>
|
||||
|
||||
<js minimize="false">webjar:jquery/1.11.0/jquery.min.js</js>
|
||||
<js>webjar:jquery.mousewheel.js</js>
|
||||
|
||||
<js minimize="false">/vendor/jqueryui/*.js</js>
|
||||
<js minimize="false">/vendor/jquery-mousewheel/*.js</js>
|
||||
<js minimize="false">/vendor/bootstrap/*.js</js>
|
||||
<js minimize="false">/vendor/angularjs/*.js</js>
|
||||
<js minimize="false">/vendor/angularui/*.js</js>
|
||||
<js minimize="false">/vendor/angularui-bootstrap/*.js</js>
|
||||
<js minimize="false">/vendor/angularui-state/*.js</js>
|
||||
<js minimize="false">/vendor/mousetrap/*.js</js>
|
||||
<js minimize="false">/vendor/nggrid/*.js</js>
|
||||
<js minimize="false">/vendor/nginfinitescroll/*.js</js>
|
||||
<js minimize="false">/vendor/spinjs/*.js</js>
|
||||
<js minimize="false">/vendor/momentjs/*.js</js>
|
||||
|
||||
<css minimize="false">/vendor/bootstrap/*.css</css>
|
||||
<css minimize="false">/vendor/angularui/*.css</css>
|
||||
<css minimize="false">/vendor/jqueryui/*.css</css>
|
||||
|
||||
<js minimize="false">webjar:bootstrap.min.js</js>
|
||||
<css minimize="false">webjar:bootstrap.min.css</css>
|
||||
|
||||
<css minimize="false">/vendor/fontawesome/css/*.css</css>
|
||||
<css minimize="false">/vendor/zocial/*.css</css>
|
||||
<css minimize="false">/vendor/nggrid/*.css</css>
|
||||
|
||||
<js minimize="false">/vendor/select2/*.js</js>
|
||||
<css>/vendor/select2/*.css</css>
|
||||
|
||||
<js minimize="false">webjar:mousetrap.min.js</js>
|
||||
|
||||
<js minimize="false">webjar:device.min.js</js>
|
||||
|
||||
<js minimize="false">webjar:moment.min.js</js>
|
||||
<js minimize="false">webjar:langs.min.js</js>
|
||||
|
||||
<js minimize="false">webjar:angular.min.js</js>
|
||||
<js minimize="false">webjar:angular-resource.min.js</js>
|
||||
<js minimize="false">webjar:angular-route.min.js</js>
|
||||
<js minimize="false">webjar:angular-sanitize.min.js</js>
|
||||
<js minimize="false">webjar:angular-touch.min.js</js>
|
||||
<js minimize="false">webjar:angular-animate.min.js</js>
|
||||
|
||||
<js minimize="false">webjar:angular-ui-router.min.js</js>
|
||||
<js minimize="false">webjar:ui-utils.min.js</js>
|
||||
<js>webjar:ui-select2.js</js>
|
||||
<js minimize="false">webjar:ui-bootstrap-tpls.min.js</js>
|
||||
<js minimize="false">webjar:ng-infinite-scroll.min.js</js>
|
||||
|
||||
<js minimize="false">webjar:ng-grid.min.js</js>
|
||||
<css minimize="false">webjar:ng-grid.min.css</css>
|
||||
|
||||
<js>/vendor/angular-loading-bar/loading-bar.0.4.0.min.js</js>
|
||||
<css minimize="false">/vendor/angular-loading-bar/loading-bar.min.css</css>
|
||||
|
||||
<css>/vendor/zocial/*.css</css>
|
||||
|
||||
<css>/vendor/readabilicons/css/*.css</css>
|
||||
</group>
|
||||
|
||||
<group name="app">
|
||||
|
||||
@@ -1,86 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<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/hightlight.default.css' media='screen' rel='stylesheet' type='text/css'/>
|
||||
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
|
||||
<link href='css/custom.css' media='screen' rel='stylesheet' type='text/css'/>
|
||||
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
|
||||
<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.rc.1.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='swagger-ui.js' type='text/javascript'></script>
|
||||
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
|
||||
<title>Swagger UI</title>
|
||||
<link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css' />
|
||||
<link href='css/hightlight.default.css' media='screen' rel='stylesheet' type='text/css' />
|
||||
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css' />
|
||||
<link href='css/custom.css' media='screen' rel='stylesheet' type='text/css' />
|
||||
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
|
||||
<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.rc.1.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='swagger-ui.js' type='text/javascript'></script>
|
||||
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
var url = window.location.href;
|
||||
if (url.indexOf('#')> -1) {
|
||||
if (url.indexOf('#') > -1) {
|
||||
url = url.substring(0, url.lastIndexOf('#'));
|
||||
}
|
||||
url = url.substring(0, url.length - 1);
|
||||
url = url.substring(0, url.lastIndexOf('/'));
|
||||
var link = $('#title-base-url-link')
|
||||
var link = $('#title-base-url-link');
|
||||
link.attr('href', url + '/rest');
|
||||
link.html(link.attr('href'));
|
||||
|
||||
|
||||
url = url + '/api/api-docs/resources';
|
||||
window.swaggerUi = new SwaggerUi({
|
||||
discoveryUrl:url,
|
||||
apiKey:"",
|
||||
dom_id:"swagger-ui-container",
|
||||
supportHeaderParams: false,
|
||||
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
|
||||
onComplete: function(swaggerApi, swaggerUi){
|
||||
if(console) {
|
||||
console.log("Loaded SwaggerUI")
|
||||
console.log(swaggerApi);
|
||||
console.log(swaggerUi);
|
||||
}
|
||||
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
|
||||
},
|
||||
onFailure: function(data) {
|
||||
if(console) {
|
||||
console.log("Unable to Load SwaggerUI");
|
||||
console.log(data);
|
||||
}
|
||||
},
|
||||
docExpansion: "none"
|
||||
});
|
||||
window.swaggerUi = new SwaggerUi({
|
||||
discoveryUrl : url,
|
||||
apiKey : "",
|
||||
dom_id : "swagger-ui-container",
|
||||
supportHeaderParams : false,
|
||||
supportedSubmitMethods : ['get', 'post', 'put', 'delete'],
|
||||
onComplete : function(swaggerApi, swaggerUi) {
|
||||
if (console) {
|
||||
console.log("Loaded SwaggerUI");
|
||||
console.log(swaggerApi);
|
||||
console.log(swaggerUi);
|
||||
}
|
||||
$('pre code').each(function(i, e) {
|
||||
hljs.highlightBlock(e);
|
||||
});
|
||||
},
|
||||
onFailure : function(data) {
|
||||
if (console) {
|
||||
console.log("Unable to Load SwaggerUI");
|
||||
console.log(data);
|
||||
}
|
||||
},
|
||||
docExpansion : "none"
|
||||
});
|
||||
|
||||
window.swaggerUi.load();
|
||||
});
|
||||
|
||||
</script>
|
||||
window.swaggerUi.load();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id='header'>
|
||||
<div class="swagger-ui-wrap">
|
||||
<a id="logo" href="../" style="padding-left: 0px">CommaFeed API</a>
|
||||
<div id='header'>
|
||||
<div class="swagger-ui-wrap">
|
||||
<a id="logo" href="../" style="padding-left: 0px">CommaFeed API</a>
|
||||
|
||||
<form id='api_selector'>
|
||||
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl"
|
||||
type="text"/></div>
|
||||
<div class='input'><a id="explore" href="#">Explore</a></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form id='api_selector'>
|
||||
<div class='input'>
|
||||
<input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text" />
|
||||
</div>
|
||||
<div class='input'>
|
||||
<a id="explore" href="#">Explore</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="message-bar" class="swagger-ui-wrap">
|
||||
|
||||
</div>
|
||||
<div id="message-bar" class="swagger-ui-wrap"> </div>
|
||||
|
||||
<h3 style="text-align: center">Authentication is required to access the REST API. Use HTTP Basic Authentication to authenticate yourself.</h3>
|
||||
<h3 style="text-align: center">The base URL of the API is <a id="title-base-url-link"></a>.</h3>
|
||||
<div id="swagger-ui-container" class="swagger-ui-wrap">
|
||||
|
||||
</div>
|
||||
<h3 style="text-align: center">Authentication is required to access the REST API. Use HTTP Basic Authentication to authenticate
|
||||
yourself.</h3>
|
||||
<h3 style="text-align: center">
|
||||
The base URL of the API is
|
||||
<a id="title-base-url-link"></a>
|
||||
.
|
||||
</h3>
|
||||
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 305 KiB After Width: | Height: | Size: 193 KiB |
@@ -30,36 +30,16 @@ module.run(['$rootScope', function($rootScope) {
|
||||
});
|
||||
}]);
|
||||
|
||||
module.controller('SubscribeCtrl', ['$scope', 'FeedService', 'CategoryService', 'MobileService',
|
||||
function($scope, FeedService, CategoryService, MobileService) {
|
||||
module.controller('SubscribeCtrl', ['$scope', '$location', 'FeedService', 'CategoryService', 'MobileService',
|
||||
function($scope, $location, FeedService, CategoryService, MobileService) {
|
||||
|
||||
$scope.opts = {
|
||||
backdropFade : true,
|
||||
dialogFade : true
|
||||
$scope.sub = {
|
||||
categoryId : 'all'
|
||||
};
|
||||
|
||||
$scope.isOpen = false;
|
||||
$scope.isOpenImport = false;
|
||||
$scope.sub = {};
|
||||
|
||||
$scope.CategoryService = CategoryService;
|
||||
$scope.MobileService = MobileService;
|
||||
|
||||
$scope.search = function() {
|
||||
$scope.$emit('emitFeedSearch');
|
||||
};
|
||||
|
||||
$scope.open = function() {
|
||||
$scope.sub = {
|
||||
categoryId : $scope.sub.categoryId || 'all'
|
||||
};
|
||||
$scope.isOpen = true;
|
||||
};
|
||||
|
||||
$scope.close = function() {
|
||||
$scope.isOpen = false;
|
||||
};
|
||||
|
||||
// 'ok', 'loading' or 'failed'
|
||||
$scope.state = 'ok';
|
||||
$scope.urlChanged = function() {
|
||||
@@ -72,9 +52,11 @@ module.controller('SubscribeCtrl', ['$scope', 'FeedService', 'CategoryService',
|
||||
$scope.state = 'ok';
|
||||
$scope.sub.title = data.title;
|
||||
$scope.sub.url = data.url;
|
||||
$scope.stacktrace = null;
|
||||
}, function(data) {
|
||||
$scope.state = 'failed';
|
||||
$scope.sub.title = 'Loading failed. Invalid feed?';
|
||||
$scope.stacktrace = data.data;
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -88,59 +70,97 @@ module.controller('SubscribeCtrl', ['$scope', 'FeedService', 'CategoryService',
|
||||
}
|
||||
FeedService.subscribe($scope.sub, function() {
|
||||
CategoryService.init();
|
||||
$scope.close();
|
||||
$location.path('/');
|
||||
}, function(data) {
|
||||
$scope.state = 'failed';
|
||||
$scope.sub.title = 'ERROR: ' + data.data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.openImport = function() {
|
||||
$scope.isOpenImport = true;
|
||||
$scope.back = function() {
|
||||
$location.path('/');
|
||||
};
|
||||
}]);
|
||||
|
||||
$scope.closeImport = function() {
|
||||
$scope.isOpenImport = false;
|
||||
};
|
||||
module.controller('NewCategoryCtrl', ['$scope', '$location', 'FeedService', 'CategoryService', 'MobileService',
|
||||
function($scope, $location, FeedService, CategoryService, MobileService) {
|
||||
|
||||
$scope.cat = {};
|
||||
$scope.CategoryService = CategoryService;
|
||||
$scope.MobileService = MobileService;
|
||||
|
||||
$scope.openCategory = function() {
|
||||
$scope.isOpenCategory = true;
|
||||
$scope.cat = {
|
||||
parentId : 'all'
|
||||
};
|
||||
};
|
||||
|
||||
$scope.closeCategory = function() {
|
||||
$scope.isOpenCategory = false;
|
||||
$scope.cat = {
|
||||
parentId : 'all'
|
||||
};
|
||||
|
||||
$scope.saveCategory = function() {
|
||||
CategoryService.add($scope.cat, function() {
|
||||
CategoryService.init();
|
||||
});
|
||||
$scope.closeCategory();
|
||||
$location.path('/');
|
||||
};
|
||||
|
||||
$scope.back = function() {
|
||||
$location.path('/');
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('CategoryTreeCtrl', ['$scope', '$timeout', '$stateParams', '$window', '$location', '$state', '$route', 'CategoryService',
|
||||
module.controller('ImportCtrl', ['$scope', '$location', 'FeedService', 'CategoryService', 'MobileService',
|
||||
function($scope, $location, FeedService, CategoryService, MobileService) {
|
||||
|
||||
$scope.back = function() {
|
||||
$location.path('/');
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('CategoryTreeCtrl', [
|
||||
'$scope',
|
||||
'$timeout',
|
||||
'$stateParams',
|
||||
'$window',
|
||||
'$location',
|
||||
'$state',
|
||||
'$route',
|
||||
'CategoryService',
|
||||
'AnalyticsService',
|
||||
function($scope, $timeout, $stateParams, $window, $location, $state, $route, CategoryService, AnalyticsService) {
|
||||
'EntryService',
|
||||
'MobileService',
|
||||
function($scope, $timeout, $stateParams, $window, $location, $state, $route, CategoryService, AnalyticsService, EntryService,
|
||||
MobileService) {
|
||||
|
||||
$scope.selectedType = $stateParams._type;
|
||||
$scope.selectedId = $stateParams._id;
|
||||
|
||||
$scope.EntryService = EntryService;
|
||||
$scope.MobileService = MobileService;
|
||||
|
||||
$scope.starred = {
|
||||
id : 'starred',
|
||||
name : 'Starred'
|
||||
};
|
||||
|
||||
$scope.tags = [];
|
||||
$scope.$watch('EntryService.tags', function(newValue, oldValue) {
|
||||
if (newValue) {
|
||||
$scope.tags = [];
|
||||
_.each(newValue, function(e) {
|
||||
$scope.tags.push({
|
||||
id : e,
|
||||
name : e,
|
||||
isTag : true
|
||||
});
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.$on('$stateChangeSuccess', function() {
|
||||
$scope.selectedType = $stateParams._type;
|
||||
$scope.selectedId = $stateParams._id;
|
||||
});
|
||||
|
||||
$scope.resizeCallback = function(event, ui) {
|
||||
$('.main-content').css('margin-left', $(ui.element).outerWidth(true) + 'px');
|
||||
};
|
||||
|
||||
$timeout(function refreshTree() {
|
||||
AnalyticsService.track();
|
||||
CategoryService.refresh(function() {
|
||||
@@ -176,7 +196,7 @@ module.controller('CategoryTreeCtrl', ['$scope', '$timeout', '$stateParams', '$w
|
||||
$scope.$watch(rootUnreadCount, function(value) {
|
||||
var label = 'CommaFeed';
|
||||
if (value > 0) {
|
||||
label = value + ' - ' + label;
|
||||
label = '(' + value + ') ' + label;
|
||||
}
|
||||
$window.document.title = label;
|
||||
});
|
||||
@@ -201,7 +221,7 @@ module.controller('CategoryTreeCtrl', ['$scope', '$timeout', '$stateParams', '$w
|
||||
|
||||
var getCurrentIndex = function(id, type, flat) {
|
||||
var index = -1;
|
||||
for ( var i = 0; i < flat.length; i++) {
|
||||
for (var i = 0; i < flat.length; i++) {
|
||||
var node = flat[i];
|
||||
if (node[0] == id && node[1] == type) {
|
||||
index = i;
|
||||
@@ -266,8 +286,8 @@ module.controller('CategoryTreeCtrl', ['$scope', '$timeout', '$stateParams', '$w
|
||||
});
|
||||
}]);
|
||||
|
||||
module.controller('FeedDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedService', 'CategoryService', 'ProfileService', '$dialog',
|
||||
function($scope, $state, $stateParams, FeedService, CategoryService, ProfileService, $dialog) {
|
||||
module.controller('FeedDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedService', 'CategoryService', 'ProfileService',
|
||||
function($scope, $state, $stateParams, FeedService, CategoryService, ProfileService) {
|
||||
|
||||
$scope.CategoryService = CategoryService;
|
||||
$scope.user = ProfileService.get();
|
||||
@@ -288,30 +308,15 @@ module.controller('FeedDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedS
|
||||
|
||||
$scope.unsubscribe = function() {
|
||||
var sub = $scope.sub;
|
||||
var title = 'Unsubscribe';
|
||||
var msg = 'Unsubscribe from ' + sub.name + '?';
|
||||
var btns = [{
|
||||
result : 'cancel',
|
||||
label : 'Cancel'
|
||||
}, {
|
||||
result : 'ok',
|
||||
label : 'OK',
|
||||
cssClass : 'btn-primary'
|
||||
}];
|
||||
|
||||
$dialog.messageBox(title, msg, btns).open().then(function(result) {
|
||||
if (result == 'ok') {
|
||||
var data = {
|
||||
id : sub.id
|
||||
};
|
||||
FeedService.unsubscribe(data, function() {
|
||||
CategoryService.init();
|
||||
});
|
||||
$state.transitionTo('feeds.view', {
|
||||
_id : 'all',
|
||||
_type : 'category'
|
||||
});
|
||||
}
|
||||
var data = {
|
||||
id : sub.id
|
||||
};
|
||||
FeedService.unsubscribe(data, function() {
|
||||
CategoryService.init();
|
||||
});
|
||||
$state.transitionTo('feeds.view', {
|
||||
_id : 'all',
|
||||
_type : 'category'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -333,7 +338,7 @@ module.controller('FeedDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedS
|
||||
}]);
|
||||
|
||||
module.controller('CategoryDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedService', 'CategoryService', 'ProfileService',
|
||||
'$dialog', function($scope, $state, $stateParams, FeedService, CategoryService, ProfileService, $dialog) {
|
||||
function($scope, $state, $stateParams, FeedService, CategoryService, ProfileService) {
|
||||
$scope.CategoryService = CategoryService;
|
||||
$scope.user = ProfileService.get();
|
||||
|
||||
@@ -355,7 +360,7 @@ module.controller('CategoryDetailsCtrl', ['$scope', '$state', '$stateParams', 'F
|
||||
};
|
||||
return;
|
||||
}
|
||||
for ( var i = 0; i < CategoryService.flatCategories.length; i++) {
|
||||
for (var i = 0; i < CategoryService.flatCategories.length; i++) {
|
||||
var cat = CategoryService.flatCategories[i];
|
||||
if (cat.id == $stateParams._id) {
|
||||
$scope.category = {
|
||||
@@ -380,29 +385,14 @@ module.controller('CategoryDetailsCtrl', ['$scope', '$state', '$stateParams', 'F
|
||||
|
||||
$scope.deleteCategory = function() {
|
||||
var category = $scope.category;
|
||||
var title = 'Delete category';
|
||||
var msg = 'Delete category ' + category.name + ' ?';
|
||||
var btns = [{
|
||||
result : 'cancel',
|
||||
label : 'Cancel'
|
||||
}, {
|
||||
result : 'ok',
|
||||
label : 'OK',
|
||||
cssClass : 'btn-primary'
|
||||
}];
|
||||
|
||||
$dialog.messageBox(title, msg, btns).open().then(function(result) {
|
||||
if (result == 'ok') {
|
||||
CategoryService.remove({
|
||||
id : category.id
|
||||
}, function() {
|
||||
CategoryService.init();
|
||||
});
|
||||
$state.transitionTo('feeds.view', {
|
||||
_id : 'all',
|
||||
_type : 'category'
|
||||
});
|
||||
}
|
||||
CategoryService.remove({
|
||||
id : category.id
|
||||
}, function() {
|
||||
CategoryService.init();
|
||||
});
|
||||
$state.transitionTo('feeds.view', {
|
||||
_id : 'all',
|
||||
_type : 'category'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -423,9 +413,23 @@ module.controller('CategoryDetailsCtrl', ['$scope', '$state', '$stateParams', 'F
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('TagDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedService', 'CategoryService', 'ProfileService',
|
||||
function($scope, $state, $stateParams, FeedService, CategoryService, ProfileService) {
|
||||
$scope.CategoryService = CategoryService;
|
||||
$scope.user = ProfileService.get();
|
||||
|
||||
$scope.tag = $stateParams._id;
|
||||
|
||||
$scope.back = function() {
|
||||
$state.transitionTo('feeds.view', {
|
||||
_id : $scope.tag,
|
||||
_type : 'tag'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('ToolbarCtrl', [
|
||||
'$scope',
|
||||
'$http',
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'$route',
|
||||
@@ -437,24 +441,15 @@ module.controller('ToolbarCtrl', [
|
||||
'ServerService',
|
||||
'FeedService',
|
||||
'MobileService',
|
||||
function($scope, $http, $state, $stateParams, $route, $location, SettingsService, EntryService, ProfileService, AnalyticsService,
|
||||
function($scope, $state, $stateParams, $route, $location, SettingsService, EntryService, ProfileService, AnalyticsService,
|
||||
ServerService, FeedService, MobileService) {
|
||||
|
||||
function totalActiveAjaxRequests() {
|
||||
return ($http.pendingRequests.length + $.active);
|
||||
}
|
||||
|
||||
$scope.keywords = $location.search().q;
|
||||
$scope.session = ProfileService.get();
|
||||
$scope.ServerService = ServerService.get();
|
||||
$scope.settingsService = SettingsService;
|
||||
$scope.MobileService = MobileService;
|
||||
|
||||
$scope.loading = true;
|
||||
$scope.$watch(totalActiveAjaxRequests, function() {
|
||||
$scope.loading = (totalActiveAjaxRequests() !== 0);
|
||||
});
|
||||
|
||||
$scope.$watch('settingsService.settings.readingMode', function(newValue, oldValue) {
|
||||
if (newValue && oldValue && newValue != oldValue) {
|
||||
SettingsService.save();
|
||||
@@ -502,6 +497,10 @@ module.controller('ToolbarCtrl', [
|
||||
markAll();
|
||||
};
|
||||
|
||||
$scope.markAll12Hours = function() {
|
||||
markAll(new Date().getTime() - 43200000);
|
||||
};
|
||||
|
||||
$scope.markAllDay = function() {
|
||||
markAll(new Date().getTime() - 86400000);
|
||||
};
|
||||
@@ -515,9 +514,10 @@ module.controller('ToolbarCtrl', [
|
||||
};
|
||||
|
||||
$scope.search = function() {
|
||||
$location.search('q', $scope.keywords);
|
||||
var keywords = this.keywords;
|
||||
$location.search('q', keywords);
|
||||
$scope.$emit('emitEntrySearch', {
|
||||
keywords : $scope.keywords
|
||||
keywords : keywords
|
||||
});
|
||||
};
|
||||
$scope.showButtons = function() {
|
||||
@@ -570,7 +570,7 @@ module.controller('FeedSearchCtrl', ['$scope', '$state', '$filter', '$timeout',
|
||||
}
|
||||
|
||||
var filtered = $scope.filtered;
|
||||
for ( var i = 0; i < filtered.length; i++) {
|
||||
for (var i = 0; i < filtered.length; i++) {
|
||||
if ($scope.focus.id == filtered[i].id) {
|
||||
index = i;
|
||||
break;
|
||||
@@ -672,8 +672,9 @@ module.controller('FeedListCtrl', [
|
||||
'FeedService',
|
||||
'CategoryService',
|
||||
'AnalyticsService',
|
||||
'MobileService',
|
||||
function($scope, $stateParams, $http, $route, $state, $window, $timeout, $location, EntryService, SettingsService, FeedService,
|
||||
CategoryService, AnalyticsService) {
|
||||
CategoryService, AnalyticsService, MobileService) {
|
||||
|
||||
$window = angular.element($window);
|
||||
AnalyticsService.track();
|
||||
@@ -688,9 +689,11 @@ module.controller('FeedListCtrl', [
|
||||
$scope.errorCount = 0;
|
||||
$scope.timestamp = 0;
|
||||
$scope.entries = [];
|
||||
$scope.ignored_read_status = false;
|
||||
$scope.font_size = 0;
|
||||
|
||||
$scope.settingsService = SettingsService;
|
||||
$scope.MobileService = MobileService;
|
||||
$scope.$watch('settingsService.settings.readingMode', function(newValue, oldValue) {
|
||||
if (newValue && oldValue && newValue != oldValue) {
|
||||
$scope.$emit('emitReload');
|
||||
@@ -702,6 +705,12 @@ module.controller('FeedListCtrl', [
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('settingsService.settings.readingOrder', function(newValue, oldValue) {
|
||||
if (newValue && oldValue && newValue != oldValue) {
|
||||
$scope.$emit('emitReload');
|
||||
}
|
||||
});
|
||||
|
||||
$scope.limit = SettingsService.settings.viewMode == 'title' ? 10 : 5;
|
||||
$scope.busy = false;
|
||||
$scope.hasMore = true;
|
||||
@@ -714,7 +723,9 @@ module.controller('FeedListCtrl', [
|
||||
$scope.busy = true;
|
||||
|
||||
var limit = $scope.limit;
|
||||
var offset = SettingsService.settings.readingMode == 'all' ? $scope.entries.length : _.where($scope.entries, {
|
||||
|
||||
var read_shown = SettingsService.settings.readingMode === 'all' || $scope.ignored_read_status;
|
||||
var offset = read_shown ? $scope.entries.length : _.where($scope.entries, {
|
||||
read : false
|
||||
}).length;
|
||||
if ($scope.entries.length === 0) {
|
||||
@@ -729,7 +740,7 @@ module.controller('FeedListCtrl', [
|
||||
}
|
||||
|
||||
var callback = function(data) {
|
||||
for ( var i = 0; i < data.entries.length; i++) {
|
||||
for (var i = 0; i < data.entries.length; i++) {
|
||||
var entry = data.entries[i];
|
||||
if (!_.some($scope.entries, {
|
||||
id : entry.id
|
||||
@@ -744,17 +755,26 @@ module.controller('FeedListCtrl', [
|
||||
$scope.busy = false;
|
||||
$scope.hasMore = data.hasMore;
|
||||
$scope.feedLink = data.feedLink;
|
||||
$scope.ignored_read_status = data.ignoredReadStatus;
|
||||
};
|
||||
|
||||
var service = $scope.selectedType == 'feed' ? FeedService : CategoryService;
|
||||
service.entries({
|
||||
var data = {
|
||||
id : $scope.selectedId,
|
||||
readType : $scope.keywords ? 'all' : $scope.settingsService.settings.readingMode,
|
||||
order : $scope.settingsService.settings.readingOrder,
|
||||
offset : offset,
|
||||
limit : limit,
|
||||
keywords : $scope.keywords
|
||||
}, callback);
|
||||
};
|
||||
if ($scope.selectedType == 'feed') {
|
||||
FeedService.entries(data, callback);
|
||||
} else if ($scope.selectedType == 'category') {
|
||||
CategoryService.entries(data, callback);
|
||||
} else if ($scope.selectedType == 'tag') {
|
||||
data.tag = data.id;
|
||||
data.id = 'all';
|
||||
CategoryService.entries(data, callback);
|
||||
}
|
||||
};
|
||||
|
||||
var watch_scrolling = true;
|
||||
@@ -781,10 +801,11 @@ module.controller('FeedListCtrl', [
|
||||
return;
|
||||
} else {
|
||||
var scrollTop = elemTop - $('#toolbar').outerHeight();
|
||||
var speed = SettingsService.settings.scrollSpeed;
|
||||
watch_scrolling = false;
|
||||
$('html, body').animate({
|
||||
scrollTop : scrollTop
|
||||
}, 400, 'swing', function() {
|
||||
}, speed, 'swing', function() {
|
||||
watch_scrolling = true;
|
||||
});
|
||||
}
|
||||
@@ -803,7 +824,7 @@ module.controller('FeedListCtrl', [
|
||||
var docTop = w.scrollTop();
|
||||
|
||||
var current = null;
|
||||
for ( var i = 0; i < $scope.entries.length; i++) {
|
||||
for (var i = 0; i < $scope.entries.length; i++) {
|
||||
var entry = $scope.entries[i];
|
||||
var e = $('#entry_' + entry.id);
|
||||
if (e.offset().top + e.height() > docTop + $('#toolbar').outerHeight()) {
|
||||
@@ -866,7 +887,7 @@ module.controller('FeedListCtrl', [
|
||||
|
||||
$scope.markUpTo = function(entry) {
|
||||
var entries = [];
|
||||
for ( var i = 0; i < $scope.entries.length; i++) {
|
||||
for (var i = 0; i < $scope.entries.length; i++) {
|
||||
var e = $scope.entries[i];
|
||||
if (!e.read) {
|
||||
entries.push({
|
||||
@@ -905,7 +926,7 @@ module.controller('FeedListCtrl', [
|
||||
var getCurrentIndex = function() {
|
||||
var index = -1;
|
||||
if ($scope.current) {
|
||||
for ( var i = 0; i < $scope.entries.length; i++) {
|
||||
for (var i = 0; i < $scope.entries.length; i++) {
|
||||
if ($scope.current == $scope.entries[i]) {
|
||||
index = i;
|
||||
break;
|
||||
@@ -1194,6 +1215,7 @@ module.controller('FeedListCtrl', [
|
||||
|
||||
Mousetrap.bind('f', function(e) {
|
||||
$('body').toggleClass('full-screen');
|
||||
$('.main-content').css('margin-left', '');
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -1257,6 +1279,30 @@ module.controller('ManageUsersCtrl', ['$scope', '$state', '$location', 'AdminUse
|
||||
multiSelect : false,
|
||||
showColumnMenu : true,
|
||||
showFilter : true,
|
||||
columnDefs : [{
|
||||
field : 'id',
|
||||
displayName : 'ID'
|
||||
}, {
|
||||
field : 'name',
|
||||
displayName : 'Name'
|
||||
}, {
|
||||
field : 'email',
|
||||
cellClass : 'E-Mail'
|
||||
}, {
|
||||
field : 'created',
|
||||
cellClass : 'Created',
|
||||
cellFilter : 'entryDate'
|
||||
}, {
|
||||
field : 'lastLogin',
|
||||
cellClass : 'Last login',
|
||||
cellFilter : 'entryDate'
|
||||
}, {
|
||||
field : 'admin',
|
||||
cellClass : 'Admin'
|
||||
}, {
|
||||
field : 'enabled',
|
||||
cellClass : 'Enabled'
|
||||
}],
|
||||
|
||||
afterSelectionChange : function(item) {
|
||||
$state.transitionTo('admin.useredit', {
|
||||
@@ -1273,8 +1319,8 @@ module.controller('ManageUsersCtrl', ['$scope', '$state', '$location', 'AdminUse
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('ManageUserCtrl', ['$scope', '$state', '$stateParams', '$dialog', 'AdminUsersService',
|
||||
function($scope, $state, $stateParams, $dialog, AdminUsersService) {
|
||||
module.controller('ManageUserCtrl', ['$scope', '$state', '$stateParams', 'AdminUsersService',
|
||||
function($scope, $state, $stateParams, AdminUsersService) {
|
||||
$scope.user = $stateParams._id ? AdminUsersService.get({
|
||||
id : $stateParams._id
|
||||
}) : {
|
||||
@@ -1301,76 +1347,14 @@ module.controller('ManageUserCtrl', ['$scope', '$state', '$stateParams', '$dialo
|
||||
}, alertFunction);
|
||||
};
|
||||
$scope.remove = function() {
|
||||
var title = 'Delete user';
|
||||
var msg = 'Delete user ' + $scope.user.name + ' ?';
|
||||
var btns = [{
|
||||
result : 'cancel',
|
||||
label : 'Cancel'
|
||||
}, {
|
||||
result : 'ok',
|
||||
label : 'OK',
|
||||
cssClass : 'btn-primary'
|
||||
}];
|
||||
|
||||
$dialog.messageBox(title, msg, btns).open().then(function(result) {
|
||||
if (result == 'ok') {
|
||||
AdminUsersService.remove({
|
||||
id : $scope.user.id
|
||||
}, function() {
|
||||
$state.transitionTo('admin.userlist');
|
||||
}, alertFunction);
|
||||
}
|
||||
});
|
||||
AdminUsersService.remove({
|
||||
id : $scope.user.id
|
||||
}, function() {
|
||||
$state.transitionTo('admin.userlist');
|
||||
}, alertFunction);
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('ManageDuplicateFeedsCtrl', ['$scope', 'AdminCleanupService', function($scope, AdminCleanupService) {
|
||||
|
||||
$scope.limit = 10;
|
||||
$scope.page = 0;
|
||||
$scope.minCount = 1;
|
||||
$scope.mode = 'NORMALIZED_URL';
|
||||
$scope.mergeData = {};
|
||||
$scope.refreshData = function() {
|
||||
AdminCleanupService.findDuplicateFeeds({
|
||||
mode : $scope.mode,
|
||||
limit : $scope.limit,
|
||||
page : $scope.page,
|
||||
minCount : $scope.minCount
|
||||
}, function(data) {
|
||||
$scope.counts = data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.autoMerge = function() {
|
||||
var callback = function() {
|
||||
alert('done!');
|
||||
};
|
||||
for ( var i = 0; i < $scope.counts.length; i++) {
|
||||
var count = $scope.counts[i];
|
||||
if (count.autoMerge) {
|
||||
AdminCleanupService.mergeFeeds({
|
||||
intoFeedId : count.feeds[0].id,
|
||||
feedIds : _.pluck(count.feeds, 'id')
|
||||
}, callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.focus = function(count) {
|
||||
$scope.current = count;
|
||||
$scope.mergeData.intoFeedId = count.feeds[0].id;
|
||||
$scope.mergeData.feedIds = _.pluck(count.feeds, 'id');
|
||||
};
|
||||
|
||||
$scope.merge = function() {
|
||||
AdminCleanupService.mergeFeeds($scope.mergeData, function() {
|
||||
alert('done!');
|
||||
});
|
||||
};
|
||||
|
||||
}]);
|
||||
|
||||
module.controller('SettingsCtrl', ['$scope', '$location', 'SettingsService', 'AnalyticsService', 'ServerService',
|
||||
function($scope, $location, SettingsService, AnalyticsService, ServerService) {
|
||||
|
||||
@@ -1378,7 +1362,7 @@ module.controller('SettingsCtrl', ['$scope', '$location', 'SettingsService', 'An
|
||||
|
||||
$scope.ServerService = ServerService.get();
|
||||
|
||||
$scope.themes = ['default', 'ebraminio', 'MRACHINI', 'svetla'];
|
||||
$scope.themes = ['default', 'bootstrap', 'dark', 'ebraminio', 'MRACHINI', 'svetla', 'third'];
|
||||
|
||||
$scope.settingsService = SettingsService;
|
||||
$scope.$watch('settingsService.settings', function(value) {
|
||||
@@ -1398,8 +1382,8 @@ module.controller('SettingsCtrl', ['$scope', '$location', 'SettingsService', 'An
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('ProfileCtrl', ['$scope', '$location', '$dialog', 'ProfileService', 'AnalyticsService',
|
||||
function($scope, $location, $dialog, ProfileService, AnalyticsService) {
|
||||
module.controller('ProfileCtrl', ['$scope', '$location', 'ProfileService', 'AnalyticsService',
|
||||
function($scope, $location, ProfileService, AnalyticsService) {
|
||||
|
||||
AnalyticsService.track();
|
||||
|
||||
@@ -1423,23 +1407,8 @@ module.controller('ProfileCtrl', ['$scope', '$location', '$dialog', 'ProfileServ
|
||||
});
|
||||
};
|
||||
$scope.deleteAccount = function() {
|
||||
var title = 'Delete account';
|
||||
var msg = 'Delete your acount? There\'s no turning back!';
|
||||
var btns = [{
|
||||
result : 'cancel',
|
||||
label : 'Cancel'
|
||||
}, {
|
||||
result : 'ok',
|
||||
label : 'OK',
|
||||
cssClass : 'btn-primary'
|
||||
}];
|
||||
|
||||
$dialog.messageBox(title, msg, btns).open().then(function(result) {
|
||||
if (result == 'ok') {
|
||||
ProfileService.deleteAccount({});
|
||||
window.location.href = 'logout';
|
||||
}
|
||||
});
|
||||
ProfileService.deleteAccount({});
|
||||
window.location.href = 'logout';
|
||||
};
|
||||
}]);
|
||||
|
||||
@@ -1460,9 +1429,6 @@ module.controller('ManageSettingsCtrl', ['$scope', '$location', '$state', 'Admin
|
||||
$scope.toUsers = function() {
|
||||
$state.transitionTo('admin.userlist');
|
||||
};
|
||||
$scope.toCleanup = function() {
|
||||
$state.transitionTo('admin.duplicate_feeds');
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('HelpController', ['$scope', 'CategoryService', 'AnalyticsService', 'ServerService',
|
||||
@@ -1477,11 +1443,13 @@ module.controller('HelpController', ['$scope', 'CategoryService', 'AnalyticsServ
|
||||
|
||||
}]);
|
||||
|
||||
module.controller('FooterController', ['$scope', function($scope) {
|
||||
module.controller('FooterController', ['$scope', '$sce', function($scope, $sce) {
|
||||
var baseUrl = window.location.href.substring(0, window.location.href.lastIndexOf('#'));
|
||||
var hostname = window.location.hostname;
|
||||
$scope.subToMeUrl = baseUrl + 'rest/feed/subscribe?url={feed}';
|
||||
$scope.subToMeName = hostname.indexOf('www.commafeed.com') !== -1 ? 'CommaFeed' : 'CommaFeed (' + hostname + ')';
|
||||
var url = baseUrl + 'rest/feed/subscribe?url={feed}';
|
||||
var name = hostname.indexOf('www.commafeed.com') !== -1 ? 'CommaFeed' : 'CommaFeed (' + hostname + ')';
|
||||
var subToMeUrl = 'https://www.subtome.com/register-no-ui.html?name=' + name + '&url=' + url;
|
||||
$scope.subToMeUrl = $sce.trustAsResourceUrl(subToMeUrl);
|
||||
|
||||
}]);
|
||||
|
||||
|
||||
@@ -15,6 +15,22 @@ module.directive('focus', ['$timeout', function($timeout) {
|
||||
};
|
||||
}]);
|
||||
|
||||
module.directive('confirmClick', [function() {
|
||||
return {
|
||||
priority : -1,
|
||||
restrict : 'A',
|
||||
link : function(scope, element, attrs) {
|
||||
element.bind('click', function(e) {
|
||||
var message = attrs.confirmClick;
|
||||
if (message && !confirm(message)) {
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
/**
|
||||
* Open a popup window pointing to the url in the href attribute
|
||||
*/
|
||||
@@ -30,6 +46,41 @@ module.directive('popup', function() {
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* entry tag handling
|
||||
*/
|
||||
module.directive('tags', function() {
|
||||
return {
|
||||
restrict : 'E',
|
||||
scope : {
|
||||
entry : '='
|
||||
},
|
||||
replace : true,
|
||||
templateUrl : 'templates/_tags.html',
|
||||
controller : ['$scope', 'EntryService', function($scope, EntryService) {
|
||||
$scope.select2Options = {
|
||||
'multiple' : true,
|
||||
'simple_tags' : true,
|
||||
'maximumInputLength' : 40,
|
||||
tags : EntryService.tags
|
||||
};
|
||||
|
||||
$scope.$watch('entry.tags', function(newValue, oldValue) {
|
||||
if (oldValue && newValue != oldValue) {
|
||||
var data = {
|
||||
entryId : $scope.entry.id,
|
||||
tags : []
|
||||
};
|
||||
if (newValue) {
|
||||
data.tags = newValue.split(',');
|
||||
}
|
||||
EntryService.tag(data);
|
||||
}
|
||||
}, true);
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Reusable favicon component
|
||||
*/
|
||||
@@ -116,13 +167,14 @@ module.directive('category', [function() {
|
||||
selectedId : '=',
|
||||
showLabel : '=',
|
||||
showChildren : '=',
|
||||
unreadCount : '&'
|
||||
unreadCount : '&',
|
||||
tag : '='
|
||||
},
|
||||
restrict : 'E',
|
||||
replace : true,
|
||||
templateUrl : 'templates/_category.html',
|
||||
controller : ['$scope', '$state', '$dialog', 'FeedService', 'CategoryService', 'SettingsService', 'MobileService',
|
||||
function($scope, $state, $dialog, FeedService, CategoryService, SettingsService, MobileService) {
|
||||
controller : ['$scope', '$state', 'FeedService', 'CategoryService', 'SettingsService', 'MobileService',
|
||||
function($scope, $state, FeedService, CategoryService, SettingsService, MobileService) {
|
||||
$scope.settingsService = SettingsService;
|
||||
|
||||
$scope.getClass = function(level) {
|
||||
@@ -174,13 +226,14 @@ module.directive('category', [function() {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.categoryClicked = function(id) {
|
||||
$scope.categoryClicked = function(id, isTag) {
|
||||
MobileService.toggleLeftMenu();
|
||||
if ($scope.selectedType == 'category' && id == $scope.selectedId) {
|
||||
var type = isTag ? 'tag' : 'category';
|
||||
if ($scope.selectedType == type && id == $scope.selectedId) {
|
||||
$scope.$emit('emitReload');
|
||||
} else {
|
||||
$state.transitionTo('feeds.view', {
|
||||
_type : 'category',
|
||||
_type : type,
|
||||
_id : id
|
||||
});
|
||||
}
|
||||
@@ -192,10 +245,16 @@ module.directive('category', [function() {
|
||||
});
|
||||
};
|
||||
|
||||
$scope.showCategoryDetails = function(category) {
|
||||
$state.transitionTo('feeds.category_details', {
|
||||
_id : category.id
|
||||
});
|
||||
$scope.showCategoryDetails = function(id, isTag) {
|
||||
if (isTag) {
|
||||
$state.transitionTo('feeds.tag_details', {
|
||||
_id : id
|
||||
});
|
||||
} else {
|
||||
$state.transitionTo('feeds.category_details', {
|
||||
_id : id
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleCategory = function(category, event) {
|
||||
@@ -214,46 +273,6 @@ module.directive('category', [function() {
|
||||
};
|
||||
}]);
|
||||
|
||||
/**
|
||||
* Reusable spinner component
|
||||
*/
|
||||
module.directive('spinner', function() {
|
||||
return {
|
||||
scope : {
|
||||
shown : '='
|
||||
},
|
||||
restrict : 'A',
|
||||
link : function($scope, element) {
|
||||
element.addClass('spinner');
|
||||
var opts = {
|
||||
lines : 11, // The number of lines to draw
|
||||
length : 5, // The length of each line
|
||||
width : 3, // The line thickness
|
||||
radius : 8, // The radius of the inner circle
|
||||
corners : 1, // Corner roundness (0..1)
|
||||
rotate : 0, // The rotation offset
|
||||
color : '#000', // #rgb or #rrggbb
|
||||
speed : 1.3, // Rounds per second
|
||||
trail : 60, // Afterglow percentage
|
||||
shadow : false, // Whether to render a shadow
|
||||
hwaccel : true, // Whether to use hardware acceleration
|
||||
zIndex : 2e9, // The z-index (defaults to 2000000000)
|
||||
top : 'auto', // Top position relative to parent in px
|
||||
left : 'auto' // Left position relative to parent in px
|
||||
};
|
||||
var spinner = new Spinner(opts);
|
||||
$scope.$watch('shown', function(shown) {
|
||||
if (shown) {
|
||||
spinner.spin();
|
||||
element.append(spinner.el);
|
||||
} else {
|
||||
spinner.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.directive('draggable', function() {
|
||||
return {
|
||||
restrict : 'A',
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
var module = angular.module('commafeed.filters', []);
|
||||
|
||||
/**
|
||||
* smart date formatter
|
||||
*/
|
||||
module.filter('entryDate', function() {
|
||||
return function(timestamp, defaultValue) {
|
||||
if (!timestamp) {
|
||||
@@ -18,10 +21,72 @@ module.filter('entryDate', function() {
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* rewrites iframes to use https if commafeed uses https
|
||||
*/
|
||||
module.filter('iframeHttpsRewrite', function() {
|
||||
return function(html) {
|
||||
var result = html;
|
||||
if (location.protocol === 'https:') {
|
||||
var wrapper = $('<div></div>').html(html);
|
||||
$('iframe', wrapper).each(function(i, elem) {
|
||||
var e = $(elem);
|
||||
e.attr('src', e.attr('src').replace(/^http:\/\//i, 'https://'));
|
||||
});
|
||||
result = wrapper.html();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* inserts title or alt-text after images, if any
|
||||
*/
|
||||
module.filter('appendImageTitles', function() {
|
||||
return function(html) {
|
||||
var result = html;
|
||||
var wrapper = $('<div></div>').html(html);
|
||||
$('img', wrapper).each(function(i, elem) {
|
||||
var e = $(elem);
|
||||
var title = e.attr('title') || e.attr('alt');
|
||||
if (title) {
|
||||
var text = $('<span style="font-style: italic;"></span>').text(title);
|
||||
e.after(text);
|
||||
}
|
||||
});
|
||||
result = wrapper.html();
|
||||
return result;
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* escapes the url
|
||||
*/
|
||||
module.filter('escape', function() {
|
||||
return encodeURIComponent;
|
||||
});
|
||||
|
||||
/**
|
||||
* returns a trusted html content
|
||||
*/
|
||||
module.filter('trustHtml', ['$sce', function($sce) {
|
||||
return function(val) {
|
||||
return $sce.trustAsHtml(val);
|
||||
};
|
||||
}]);
|
||||
|
||||
/**
|
||||
* returns a trusted url
|
||||
*/
|
||||
module.filter('trustUrl', ['$sce', function($sce) {
|
||||
return function(val) {
|
||||
return $sce.trustAsResourceUrl(val);
|
||||
};
|
||||
}]);
|
||||
|
||||
/**
|
||||
* add the 'highlight-search' class to text matching keywords
|
||||
*/
|
||||
module.filter('highlight', function() {
|
||||
return function(html, keywords) {
|
||||
if (keywords) {
|
||||
@@ -39,7 +104,7 @@ module.filter('highlight', function() {
|
||||
};
|
||||
|
||||
var tokens = keywords.split(' ');
|
||||
for ( var i = 0; i < tokens.length; i++) {
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
html = handleKeyword(tokens[i], html);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
var app = angular.module('commafeed', ['ui', 'ui.bootstrap', 'ui.state', 'commafeed.directives', 'commafeed.controllers',
|
||||
'commafeed.services', 'commafeed.filters', 'ngSanitize', 'infinite-scroll', 'ngGrid']);
|
||||
var app = angular.module('commafeed', ['ngRoute', 'ngTouch', 'ngAnimate', 'ui.utils', 'ui.bootstrap', 'ui.router', 'ui.select2',
|
||||
'commafeed.directives', 'commafeed.controllers', 'commafeed.services', 'commafeed.filters', 'ngSanitize', 'infinite-scroll',
|
||||
'ngGrid', 'chieffancypants.loadingBar']);
|
||||
|
||||
app.config(['$routeProvider', '$stateProvider', '$urlRouterProvider', '$httpProvider', '$compileProvider',
|
||||
function($routeProvider, $stateProvider, $urlRouterProvider, $httpProvider, $compileProvider) {
|
||||
app.config(['$routeProvider', '$stateProvider', '$urlRouterProvider', '$httpProvider', '$compileProvider', 'cfpLoadingBarProvider',
|
||||
function($routeProvider, $stateProvider, $urlRouterProvider, $httpProvider, $compileProvider, cfpLoadingBarProvider) {
|
||||
|
||||
cfpLoadingBarProvider.includeSpinner = false;
|
||||
|
||||
$compileProvider.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|javascript):/);
|
||||
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|javascript):/);
|
||||
var interceptor = ['$rootScope', '$q', function(scope, $q) {
|
||||
|
||||
var success = function(response) {
|
||||
@@ -39,6 +42,21 @@ app.config(['$routeProvider', '$stateProvider', '$urlRouterProvider', '$httpProv
|
||||
templateUrl : 'templates/feeds.view.html',
|
||||
controller : 'FeedListCtrl'
|
||||
});
|
||||
$stateProvider.state('feeds.subscribe', {
|
||||
url : '/subscribe',
|
||||
templateUrl : 'templates/feeds.subscribe.html',
|
||||
controller : 'SubscribeCtrl'
|
||||
});
|
||||
$stateProvider.state('feeds.new_category', {
|
||||
url : '/add_category',
|
||||
templateUrl : 'templates/feeds.new_category.html',
|
||||
controller : 'NewCategoryCtrl'
|
||||
});
|
||||
$stateProvider.state('feeds.import', {
|
||||
url : '/import',
|
||||
templateUrl : 'templates/feeds.import.html',
|
||||
controller : 'ImportCtrl'
|
||||
});
|
||||
$stateProvider.state('feeds.search', {
|
||||
url : '/search/:_keywords',
|
||||
templateUrl : 'templates/feeds.view.html',
|
||||
@@ -54,6 +72,11 @@ app.config(['$routeProvider', '$stateProvider', '$urlRouterProvider', '$httpProv
|
||||
templateUrl : 'templates/feeds.category_details.html',
|
||||
controller : 'CategoryDetailsCtrl'
|
||||
});
|
||||
$stateProvider.state('feeds.tag_details', {
|
||||
url : '/details/tag/:_id',
|
||||
templateUrl : 'templates/feeds.tag_details.html',
|
||||
controller : 'TagDetailsCtrl'
|
||||
});
|
||||
$stateProvider.state('feeds.help', {
|
||||
url : '/help',
|
||||
templateUrl : 'templates/feeds.help.html',
|
||||
@@ -90,11 +113,6 @@ app.config(['$routeProvider', '$stateProvider', '$urlRouterProvider', '$httpProv
|
||||
templateUrl : 'templates/admin.useredit.html',
|
||||
controller : 'ManageUserCtrl'
|
||||
});
|
||||
$stateProvider.state('admin.duplicate_feeds', {
|
||||
url : '/feeds/duplicates',
|
||||
templateUrl : 'templates/admin.duplicate_feeds.html',
|
||||
controller : 'ManageDuplicateFeedsCtrl'
|
||||
});
|
||||
$stateProvider.state('admin.settings', {
|
||||
url : '/settings',
|
||||
templateUrl : 'templates/admin.settings.html',
|
||||
@@ -105,7 +123,7 @@ app.config(['$routeProvider', '$stateProvider', '$urlRouterProvider', '$httpProv
|
||||
templateUrl : 'templates/admin.metrics.html',
|
||||
controller : 'MetricsCtrl'
|
||||
});
|
||||
|
||||
|
||||
$urlRouterProvider.when('/', '/feeds/view/category/all');
|
||||
$urlRouterProvider.when('/admin', '/admin/settings');
|
||||
$urlRouterProvider.otherwise('/');
|
||||
|
||||
@@ -23,8 +23,7 @@ module.service('MobileService', ['$state', function($state) {
|
||||
this.rightMenu = !this.rightMenu;
|
||||
$('body').toggleClass('right-menu-active');
|
||||
};
|
||||
var width = (window.innerWidth > 0) ? window.innerWidth : screen.width;
|
||||
this.mobile = width < 979;
|
||||
this.mobile = device.mobile() || device.tablet();
|
||||
}]);
|
||||
|
||||
module.factory('ProfileService', ['$resource', function($resource) {
|
||||
@@ -126,7 +125,7 @@ module.factory('CategoryService', ['$resource', '$http', function($resource, $ht
|
||||
callback(category, parentName);
|
||||
var children = category.children;
|
||||
if (children) {
|
||||
for ( var c = 0; c < children.length; c++) {
|
||||
for (var c = 0; c < children.length; c++) {
|
||||
traverse(callback, children[c], category.name);
|
||||
}
|
||||
}
|
||||
@@ -174,6 +173,7 @@ module.factory('CategoryService', ['$resource', '$http', function($resource, $ht
|
||||
var actions = {
|
||||
get : {
|
||||
method : 'GET',
|
||||
ignoreLoadingBar: true,
|
||||
params : {
|
||||
_method : 'get'
|
||||
}
|
||||
@@ -256,6 +256,7 @@ module.factory('EntryService', ['$resource', '$http', function($resource, $http)
|
||||
},
|
||||
mark : {
|
||||
method : 'POST',
|
||||
ignoreLoadingBar: true,
|
||||
params : {
|
||||
_method : 'mark'
|
||||
}
|
||||
@@ -271,9 +272,29 @@ module.factory('EntryService', ['$resource', '$http', function($resource, $http)
|
||||
params : {
|
||||
_method : 'star'
|
||||
}
|
||||
},
|
||||
tag : {
|
||||
method : 'POST',
|
||||
params : {
|
||||
_method : 'tag'
|
||||
}
|
||||
}
|
||||
};
|
||||
var res = $resource('rest/entry/:_method', {}, actions);
|
||||
res.tags = [];
|
||||
var initTags = function() {
|
||||
$http.get('rest/entry/tags').success(function(data) {
|
||||
res.tags = [];
|
||||
res.tags.push.apply(res.tags, data);
|
||||
});
|
||||
};
|
||||
var oldTag = res.tag;
|
||||
res.tag = function(data) {
|
||||
oldTag(data, function() {
|
||||
initTags();
|
||||
});
|
||||
};
|
||||
initTags();
|
||||
return res;
|
||||
}]);
|
||||
|
||||
@@ -296,27 +317,6 @@ module.factory('AdminMetricsService', ['$resource', function($resource) {
|
||||
return res;
|
||||
}]);
|
||||
|
||||
|
||||
module.factory('AdminCleanupService', ['$resource', function($resource) {
|
||||
var actions = {
|
||||
findDuplicateFeeds : {
|
||||
method : 'GET',
|
||||
isArray : true,
|
||||
params : {
|
||||
_method : 'findDuplicateFeeds'
|
||||
}
|
||||
},
|
||||
mergeFeeds : {
|
||||
method : 'POST',
|
||||
params : {
|
||||
_method : 'merge'
|
||||
}
|
||||
}
|
||||
};
|
||||
var res = $resource('rest/admin/cleanup/:_method', {}, actions);
|
||||
return res;
|
||||
}]);
|
||||
|
||||
module.factory('ServerService', ['$resource', function($resource) {
|
||||
var res = $resource('rest/server/get');
|
||||
return res;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user