mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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"
|
||||
49
pom.xml
49
pom.xml
@@ -4,7 +4,7 @@
|
||||
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.4.0</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.4</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.0.7</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.2</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.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<version>1.7.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
@@ -336,23 +341,23 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-core</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<version>6.13.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-auth-roles</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<version>6.13.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-extensions</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<version>6.13.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-cdi</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<version>6.13.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ro.isdc.wro4j</groupId>
|
||||
@@ -549,7 +554,7 @@
|
||||
<plugin>
|
||||
<groupId>ro.isdc.wro4j</groupId>
|
||||
<artifactId>wro4j-maven-plugin</artifactId>
|
||||
<version>1.6.3</version>
|
||||
<version>1.7.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
||||
@@ -7,44 +7,33 @@ import java.security.cert.X509Certificate;
|
||||
|
||||
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.HttpEntity;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpResponse;
|
||||
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.protocol.HttpContext;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.config.ConnectionConfig;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
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.util.EntityUtils;
|
||||
import org.apache.wicket.util.io.IOUtils;
|
||||
|
||||
/**
|
||||
* Smart HTTP getter: handles gzip, ssl, last modified and etag headers
|
||||
@@ -57,8 +46,6 @@ 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 SSLContext SSL_CONTEXT = null;
|
||||
static {
|
||||
@@ -70,8 +57,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 +80,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 +98,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 +135,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 +191,24 @@ 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.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.UTF_8).build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static class NotModifiedException extends Exception {
|
||||
@@ -245,24 +234,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.cleanEntriesForFeedsWithoutSubscriptions();
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import javax.persistence.criteria.Root;
|
||||
|
||||
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.Feed_;
|
||||
@@ -36,6 +37,21 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
return Iterables.getFirst(list, null);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -82,7 +82,7 @@ public class FeedParser {
|
||||
continue;
|
||||
}
|
||||
entry.setGuid(FeedUtils.truncate(guid, 2048));
|
||||
entry.setUrl(FeedUtils.truncate(FeedUtils.toAbsoluteUrl(item.getLink(), feed.getLink()), 2048));
|
||||
entry.setUrl(FeedUtils.truncate(FeedUtils.toAbsoluteUrl(item.getLink(), feed.getLink(), feed.getUrlAfterRedirect()), 2048));
|
||||
entry.setUpdated(validateDate(getEntryUpdateDate(item), true));
|
||||
|
||||
FeedEntryContent content = new FeedEntryContent();
|
||||
|
||||
@@ -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
|
||||
@@ -147,22 +166,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 +200,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;
|
||||
@@ -389,17 +392,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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -68,4 +68,7 @@ public class UserSettings extends AbstractModel {
|
||||
@Column(length = Integer.MAX_VALUE)
|
||||
private String customCss;
|
||||
|
||||
@Column(name = "scroll_speed")
|
||||
private int scrollSpeed;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import com.commafeed.backend.model.FeedSubscription;
|
||||
*/
|
||||
@Slf4j
|
||||
public class DatabaseCleaningService {
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
@Inject
|
||||
FeedDAO feedDAO;
|
||||
@@ -44,12 +46,29 @@ public class DatabaseCleaningService {
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
public long cleanFeedsWithoutSubscriptions() {
|
||||
|
||||
public long cleanEntriesForFeedsWithoutSubscriptions() {
|
||||
log.info("cleaning entries for feeds without subscriptions");
|
||||
long total = 0;
|
||||
int deleted = -1;
|
||||
int deleted = 0;
|
||||
do {
|
||||
deleted = feedDAO.deleteWithoutSubscriptions(10);
|
||||
deleted = 0;
|
||||
List<Feed> feeds = feedDAO.findWithoutSubscriptions(1);
|
||||
for (Feed feed : feeds) {
|
||||
deleted += feedEntryDAO.delete(feed, BATCH_SIZE);
|
||||
total += deleted;
|
||||
log.info("removed {} entries for feeds without subscriptions", total);
|
||||
}
|
||||
} while (deleted != 0);
|
||||
log.info("cleanup done: {} entries for feeds without subscriptions deleted", total);
|
||||
return total;
|
||||
}
|
||||
|
||||
public long cleanFeedsWithoutSubscriptions() {
|
||||
log.info("cleaning feeds without subscriptions");
|
||||
long total = 0;
|
||||
int deleted = 0;
|
||||
do {
|
||||
deleted = feedDAO.delete(feedDAO.findWithoutSubscriptions(1));
|
||||
total += deleted;
|
||||
log.info("removed {} feeds without subscriptions", total);
|
||||
} while (deleted != 0);
|
||||
@@ -58,15 +77,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 +94,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 +124,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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -38,5 +38,8 @@ 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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -146,4 +146,5 @@ public abstract class AbstractREST {
|
||||
boolean authorized = roles.hasAnyRole(new Roles(requiredRole.name()));
|
||||
return authorized;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.commafeed.frontend.rest.resources;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -19,14 +18,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 +34,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;
|
||||
@@ -259,36 +253,4 @@ public class AdminREST extends AbstractREST {
|
||||
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(
|
||||
@@ -192,16 +193,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(
|
||||
@@ -195,16 +196,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 +265,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 +275,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 +374,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) {
|
||||
|
||||
@@ -79,6 +79,7 @@ public class UserREST extends AbstractREST {
|
||||
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());
|
||||
@@ -88,6 +89,7 @@ public class UserREST extends AbstractREST {
|
||||
s.setSocialButtons(true);
|
||||
s.setScrollMarks(true);
|
||||
s.setLanguage("en");
|
||||
s.setScrollSpeed(400);
|
||||
}
|
||||
return Response.ok(s).build();
|
||||
}
|
||||
@@ -116,6 +118,7 @@ public class UserREST extends AbstractREST {
|
||||
s.setCustomCss(settings.getCustomCss());
|
||||
s.setSocialButtons(settings.isSocialButtons());
|
||||
s.setLanguage(settings.getLanguage());
|
||||
s.setScrollSpeed(settings.getScrollSpeed());
|
||||
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>
|
||||
@@ -7,5 +7,6 @@
|
||||
<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" />
|
||||
|
||||
</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
|
||||
|
||||
@@ -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 ####### Needs translation
|
||||
|
||||
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=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=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=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=Kategoriedetails
|
||||
details.tag_details=Tag details ####### Needs translation
|
||||
details.parent_category=Übergeordnete Kategorie
|
||||
|
||||
profile.user_name=Benutzername
|
||||
@@ -98,6 +105,7 @@ 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_confirmation=Delete your acount? There's no turning back! ####### Needs translation
|
||||
|
||||
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=단축기
|
||||
|
||||
@@ -7,6 +7,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>
|
||||
@@ -3,24 +3,29 @@
|
||||
|
||||
<group name="lib">
|
||||
<js minimize="false">/vendor/jquery/*.js</js>
|
||||
<js minimize="false">/vendor/select2/*.js</js>
|
||||
<js minimize="false">/vendor/lodash/*.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/ui-utils/*.js</js>
|
||||
<js minimize="false">/vendor/ui-select2/*.js</js>
|
||||
<js minimize="false">/vendor/ui-bootstrap/*.js</js>
|
||||
<js minimize="false">/vendor/ui-router/*.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>
|
||||
<js minimize="false">/vendor/devicejs/*.js</js>
|
||||
|
||||
<css minimize="false">/vendor/jqueryui/*.css</css>
|
||||
<css minimize="false">/vendor/select2/*.css</css>
|
||||
<css minimize="false">/vendor/bootstrap/*.css</css>
|
||||
<css minimize="false">/vendor/angularui/*.css</css>
|
||||
<css minimize="false">/vendor/fontawesome/css/*.css</css>
|
||||
<css minimize="false">/vendor/zocial/*.css</css>
|
||||
<css minimize="false">/vendor/readabilicons/css/*.css</css>
|
||||
<css minimize="false">/vendor/nggrid/*.css</css>
|
||||
</group>
|
||||
|
||||
|
||||
@@ -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() {
|
||||
@@ -88,59 +68,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() {
|
||||
@@ -201,7 +219,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 +284,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 +306,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 +336,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 +358,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 +383,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,6 +411,21 @@ 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',
|
||||
@@ -502,6 +505,10 @@ module.controller('ToolbarCtrl', [
|
||||
markAll();
|
||||
};
|
||||
|
||||
$scope.markAll12Hours = function() {
|
||||
markAll(new Date().getTime() - 43200000);
|
||||
};
|
||||
|
||||
$scope.markAllDay = function() {
|
||||
markAll(new Date().getTime() - 86400000);
|
||||
};
|
||||
@@ -515,9 +522,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 +578,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;
|
||||
@@ -702,6 +710,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 +728,10 @@ 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.selectedType === 'tag'
|
||||
|| ($scope.selectedType === 'category' && $scope.selectedId === 'starred');
|
||||
var offset = read_shown ? $scope.entries.length : _.where($scope.entries, {
|
||||
read : false
|
||||
}).length;
|
||||
if ($scope.entries.length === 0) {
|
||||
@@ -729,7 +746,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
|
||||
@@ -746,15 +763,23 @@ module.controller('FeedListCtrl', [
|
||||
$scope.feedLink = data.feedLink;
|
||||
};
|
||||
|
||||
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 +806,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 +829,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 +892,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 +931,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 +1220,7 @@ module.controller('FeedListCtrl', [
|
||||
|
||||
Mousetrap.bind('f', function(e) {
|
||||
$('body').toggleClass('full-screen');
|
||||
$('.main-content').css('margin-left', '');
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -1257,6 +1284,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 +1324,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 +1352,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) {
|
||||
|
||||
@@ -1398,8 +1387,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 +1412,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 +1434,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 +1448,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,38 @@ 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 (newValue && oldValue && newValue != oldValue) {
|
||||
var data = {
|
||||
entryId : $scope.entry.id,
|
||||
tags : newValue
|
||||
};
|
||||
EntryService.tag(data);
|
||||
}
|
||||
}, true);
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Reusable favicon component
|
||||
*/
|
||||
@@ -116,13 +164,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 +223,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 +242,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) {
|
||||
|
||||
@@ -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,52 @@ 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;
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* 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 +84,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,10 @@
|
||||
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', 'ui.utils', 'ui.bootstrap', 'ui.router', 'ui.select2', 'commafeed.directives',
|
||||
'commafeed.controllers', 'commafeed.services', 'commafeed.filters', 'ngSanitize', 'infinite-scroll', 'ngGrid']);
|
||||
|
||||
app.config(['$routeProvider', '$stateProvider', '$urlRouterProvider', '$httpProvider', '$compileProvider',
|
||||
function($routeProvider, $stateProvider, $urlRouterProvider, $httpProvider, $compileProvider) {
|
||||
|
||||
$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 +39,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 +69,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 +110,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 +120,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);
|
||||
}
|
||||
}
|
||||
@@ -271,9 +270,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 +315,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;
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.full-screen .main-content {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
@@ -14,6 +19,7 @@
|
||||
|
||||
.entrylist-header h3 {
|
||||
margin: 0;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.entrylist-header a {
|
||||
@@ -70,7 +76,7 @@
|
||||
|
||||
#feed-accordion .entry-heading-link {
|
||||
color: black;
|
||||
height: 20px;
|
||||
height: 32px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
padding: 6px 0px;
|
||||
@@ -143,7 +149,7 @@
|
||||
}
|
||||
|
||||
.full-screen #feed-accordion .entry-body-content {
|
||||
max-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-enclosure {
|
||||
@@ -172,6 +178,24 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#feed-accordion .tags-panel {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
#feed-accordion .tags-panel .label{
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.select2-container-multi .select2-choices .select2-search-field input {
|
||||
padding: 2px
|
||||
}
|
||||
|
||||
#feed-accordion .tag-input {
|
||||
margin: 0 0 0 5px;
|
||||
padding: 0;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#feed-accordion .entry-buttons label {
|
||||
margin-bottom: 0px;
|
||||
font-size: 12px;
|
||||
|
||||
@@ -22,15 +22,20 @@
|
||||
/* tree*/
|
||||
.sidebar-nav-fixed {
|
||||
margin-top: 10px;
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 16%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-nav-fixed:hover {
|
||||
overflow-y: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar-nav-fixed .ui-resizable-e {
|
||||
width: 5px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.full-screen .left-menu {
|
||||
@@ -39,6 +44,7 @@
|
||||
|
||||
.css-treeview {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 30px;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
@@ -47,6 +53,7 @@
|
||||
.css-treeview ul {
|
||||
list-style: none;
|
||||
margin-left: 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.css-treeview li {
|
||||
@@ -118,26 +125,27 @@
|
||||
.css-treeview .tree-item:hover .config {
|
||||
display: block;
|
||||
padding-top: 3px;
|
||||
margin-right: 5px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.css-treeview .indent1 {
|
||||
padding-left: 22px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.css-treeview .indent2 {
|
||||
padding-left: 44px;
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
.css-treeview .indent3 {
|
||||
padding-left: 66px;
|
||||
padding-left: 48px;
|
||||
}
|
||||
|
||||
.css-treeview .indent4 {
|
||||
padding-left: 88px;
|
||||
padding-left: 64px;
|
||||
}
|
||||
|
||||
.css-treeview .indent5 {
|
||||
padding-left: 110px;
|
||||
padding-left: 80px;
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
.toolbar {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
@@ -12,3 +11,6 @@
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toolbar .keywords {
|
||||
width: 244px;
|
||||
}
|
||||
@@ -1,7 +1,36 @@
|
||||
.container-full {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
body img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body .modal {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body .modal-backdrop {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nolink {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.nolink:hover {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
@@ -41,6 +70,7 @@
|
||||
|
||||
.welcome .preview {
|
||||
margin: 20px 0 20px 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.welcome .demo {
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
@media ( max-width : 979px) {
|
||||
html.mobile, html.tablet {
|
||||
body {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
.container-full {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.container-fluid {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
.row {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.left-menu {
|
||||
display: none !important;
|
||||
}
|
||||
.toolbar .hidden-desktop.btn-group {
|
||||
.left-menu .feed-link, .left-menu .category-link {
|
||||
line-height: 30px;
|
||||
font-size: 105%;
|
||||
}
|
||||
.toolbar .toolbar-expand.btn-group {
|
||||
display: inline-block !important;
|
||||
}
|
||||
.main-content {
|
||||
@@ -18,6 +27,8 @@
|
||||
float: none !important;
|
||||
width: 100% !important;
|
||||
margin-left: 0 !important;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
#feed-accordion .entry-heading .shrink {
|
||||
margin-left: 0;
|
||||
@@ -30,16 +41,27 @@
|
||||
margin-top: 22px;
|
||||
}
|
||||
#feed-accordion .entry-external-link {
|
||||
right: 10px;
|
||||
right: 15px;
|
||||
margin-top: -24px;
|
||||
}
|
||||
#feed-accordion .entry-heading-link {
|
||||
height: 40px;
|
||||
height: 52px;
|
||||
}
|
||||
#feed-accordion .entry-heading .entry-date {
|
||||
right: 35px;
|
||||
right: 40px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
#feed-accordion .entry-buttons .checkbox, #feed-accordion .entry-buttons .form-group {
|
||||
display: inline-block;
|
||||
}
|
||||
#feed-accordion .tags-panel {
|
||||
display: block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
#feed-accordion a.mark-up-to {
|
||||
right: 20px;
|
||||
margin-top: -18px;
|
||||
}
|
||||
body.left-menu-active .left-menu {
|
||||
display: block !important;
|
||||
width: 100%;
|
||||
@@ -55,10 +77,10 @@
|
||||
margin-top: 5px;
|
||||
margin-left: 0;
|
||||
}
|
||||
body.right-menu-active .toolbar .actions .visible-desktop {
|
||||
body.right-menu-active .toolbar .actions {
|
||||
display: inherit !important;
|
||||
}
|
||||
#uvTab {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,37 @@
|
||||
<li>
|
||||
<div class="pointer tree-item" ui-if="showLabel" ng-class="getClass(level - 1)" droppable="node">
|
||||
<div class="pointer tree-item" ng-if="showLabel" ng-class="getClass(level - 1)" droppable="node">
|
||||
<div class="dropdown pull-right">
|
||||
<div class="pull-right" ng-click="showCategoryDetails(node)">
|
||||
<div class="pull-right" ng-click="showCategoryDetails(node.id, node.isTag)">
|
||||
<i class="icon-wrench config pointer"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-click="categoryClicked(node.id)" ng-dblclick="showCategoryDetails(node)">
|
||||
<div class="category-link" ng-click="categoryClicked(node.id, node.isTag)" ng-dblclick="showCategoryDetails(node.id, node.isTag)">
|
||||
<span class="fldr">
|
||||
<i ng-class="{'icon-caret-right': !node.expanded, 'icon-caret-down': node.expanded}" ng-click="toggleCategory(node, $event)" ng-show="showChildren"></i>
|
||||
<i ng-class="{'icon-star' : node.id == 'starred', 'icon-inbox': node.id == 'all'}" ng-show="!showChildren"></i>
|
||||
<i ng-class="{'icon-caret-right': !node.expanded, 'icon-caret-down': node.expanded}" ng-click="toggleCategory(node, $event)"
|
||||
ng-show="showChildren"></i>
|
||||
<i ng-class="{'icon-star' : node.id == 'starred', 'icon-inbox': node.id == 'all', 'icon-tag' : node.isTag}" ng-show="!showChildren"></i>
|
||||
</span>
|
||||
<span ng-class="{selected: (node.id == selectedId && selectedType == 'category')}">
|
||||
<span ng-class="{unread: unreadCount({category:node})}" class="bidi-embed">
|
||||
{{categoryLabel(node)}}
|
||||
</span>
|
||||
<span class="unread-counter">
|
||||
{{categoryCountLabel(node)}}
|
||||
</span>
|
||||
<span ng-class="{selected: (node.id == selectedId && (node.isTag ? selectedType == 'tag' : selectedType == 'category'))}">
|
||||
<span ng-class="{unread: unreadCount({category:node})}" class="bidi-embed"> {{categoryLabel(node)}} </span>
|
||||
<span class="unread-counter"> {{categoryCountLabel(node)}} </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul ng-show="node.expanded && showChildren">
|
||||
<recursive>
|
||||
<category ng-repeat="child in node.children" ng-show="settingsService.settings.showRead == true || unreadCount({category:node}) > 0"
|
||||
node="child" level="level + 1" selected-type="selectedType"
|
||||
selected-id="selectedId" show-label="true" show-children="true"
|
||||
unread-count="unreadCount({category:category})">
|
||||
</category>
|
||||
</recursive>
|
||||
<recursive> <category ng-repeat="child in node.children"
|
||||
ng-show="settingsService.settings.showRead == true || unreadCount({category:node}) > 0" node="child" level="level + 1"
|
||||
selected-type="selectedType" selected-id="selectedId" show-label="true" show-children="true"
|
||||
unread-count="unreadCount({category:category})"> </category> </recursive>
|
||||
<li ng-repeat="feed in node.feeds" ng-class="getClass(level)" class="tree-item" draggable="feed" droppable="feed"
|
||||
ng-show="settingsService.settings.showRead == true || feed.unread > 0">
|
||||
<div class="pull-right" ng-click="showFeedDetails(feed)">
|
||||
<i class="icon-wrench config pointer"></i>
|
||||
</div>
|
||||
<a ng-click="feedClicked(feed.id, $event)" ng-dblclick="showFeedDetails(feed)" class="feed-link"
|
||||
href="{{feed.feedLink}}" target="_blank"
|
||||
<a ng-click="feedClicked(feed.id, $event)" ng-dblclick="showFeedDetails(feed)" class="feed-link" href="{{feed.feedLink}}" target="_blank"
|
||||
ng-class="{error: feed.message && feed.errorCount > 10, selected: (feed.id == selectedId && selectedType == 'feed') }">
|
||||
<favicon url="feed.iconUrl" />
|
||||
<span ng-class="{unread: feed.unread}" class="bidi-embed">
|
||||
{{feed.name}}
|
||||
</span>
|
||||
<span class="unread-counter">
|
||||
{{feedCountLabel(feed)}}
|
||||
</span>
|
||||
<span ng-class="{unread: feed.unread}" class="bidi-embed"> {{feed.name}} </span>
|
||||
<span class="unread-counter"> {{feedCountLabel(feed)}} </span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
<div ng-controller="FeedSearchCtrl">
|
||||
<div modal="feedSearchModal" close="close()"
|
||||
options="{dialogClass: 'modal feed-search-dialog'}">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()">×</button>
|
||||
<h4>
|
||||
<input ng-model="filter" class="filter-input"
|
||||
ui-keydown="{'up': 'focusPrevious($event)', 'down': 'focusNext($event)', 'enter': 'openFocused()' }"
|
||||
placeholder="${feedsearch.hint}" focus="feedSearchModal">
|
||||
</h4>
|
||||
<small>${feedsearch.help}</small>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<strong>${feedsearch.result_prefix}</strong>
|
||||
<span
|
||||
ng-repeat="feed in (filtered = (CategoryService.feeds | filter:{name: filter} | limitTo:40))">
|
||||
<span ng-class="{block: filter, focus: focus.id == feed.id}" class="feed-link">
|
||||
<a class=" pointer" ng-click="goToFeed(feed.id)">
|
||||
<favicon url="feed.iconUrl" />
|
||||
{{feed.name}}
|
||||
</a>
|
||||
<span ng-show="!filter && !$last">•</span>
|
||||
</span>
|
||||
</span>
|
||||
<div modal="feedSearchModal" close="close()" options="{dialogClass: 'modal feed-search-dialog'}">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()">×</button>
|
||||
<h4>
|
||||
<input ng-model="filter" class="filter-input"
|
||||
ui-keydown="{'up': 'focusPrevious($event)', 'down': 'focusNext($event)', 'enter': 'openFocused()' }" placeholder="${feedsearch.hint}"
|
||||
focus="feedSearchModal">
|
||||
</h4>
|
||||
<small>${feedsearch.help}</small>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<strong>${feedsearch.result_prefix}</strong>
|
||||
<span ng-repeat="feed in (filtered = (CategoryService.feeds | filter:{name: filter} | limitTo:40))">
|
||||
<span ng-class="{block: filter, focus: focus.id == feed.id}" class="feed-link">
|
||||
<a class=" pointer" ng-click="goToFeed(feed.id)">
|
||||
<favicon url="feed.iconUrl" />
|
||||
{{feed.name}}
|
||||
</a>
|
||||
<span ng-show="!filter && !$last">•</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,3 +1,3 @@
|
||||
<div ng-controller="FooterController">
|
||||
<iframe ui-if="subToMeUrl" style="display:none;" ng-src='https://www.subtome.com/register-no-ui.html?name={{subToMeName}}&url={{subToMeUrl}}'></iframe>
|
||||
<iframe ng-if="subToMeUrl" style="display: none;" ng-src='{{subToMeUrl}}'></iframe>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user