mirror of
https://github.com/sismics/docs.git
synced 2025-12-14 18:26:17 +00:00
Compare commits
124 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a927fd320 | ||
|
|
523501a592 | ||
|
|
ff8155be6a | ||
|
|
6c5d697051 | ||
|
|
b19145160e | ||
|
|
c7ada71ef5 | ||
|
|
4951229576 | ||
|
|
d98c1bddec | ||
|
|
b0d0e93364 | ||
|
|
f20a562439 | ||
|
|
4ae8475f5e | ||
|
|
fd4c627c61 | ||
|
|
a867d48232 | ||
|
|
f6bf61fce9 | ||
|
|
c60c9a8f74 | ||
|
|
dc021ab71e | ||
|
|
18b5551f6c | ||
|
|
6fcd8771a5 | ||
|
|
1fef4c3d2e | ||
|
|
ee6ed2bf0b | ||
|
|
57b67fee09 | ||
|
|
a6cbacae72 | ||
|
|
1e0f8e2484 | ||
|
|
bcb4c6d7b0 | ||
|
|
ea1d5907c1 | ||
|
|
05bac38fc3 | ||
|
|
69746cd369 | ||
|
|
ff3db531e5 | ||
|
|
558de7ba3f | ||
|
|
af15116bf9 | ||
|
|
36e5a9747b | ||
|
|
1d66b47f5f | ||
|
|
1346dd3616 | ||
|
|
b6ec5e108b | ||
|
|
5b2833350c | ||
|
|
66acb380ab | ||
|
|
00c62f2ad4 | ||
|
|
7205863d95 | ||
|
|
2a4274d583 | ||
|
|
087184b598 | ||
|
|
e5600e0be7 | ||
|
|
964f3128d2 | ||
|
|
69905cdc55 | ||
|
|
bf4e277db7 | ||
|
|
eaa7cca278 | ||
|
|
0e115bb808 | ||
|
|
1897f5567b | ||
|
|
d647528b3c | ||
|
|
07d42cdb9c | ||
|
|
dabb960c94 | ||
|
|
c71e794051 | ||
|
|
1584c0cbb2 | ||
|
|
22f0f1abf4 | ||
|
|
205f92d093 | ||
|
|
7488ac15a7 | ||
|
|
44f5db993a | ||
|
|
f76eae23ca | ||
|
|
5e2a18f819 | ||
|
|
2f6e5d53c2 | ||
|
|
50e6c4d965 | ||
|
|
3ad0554a7d | ||
|
|
113ec78c67 | ||
|
|
f814927eca | ||
|
|
a9719feeec | ||
|
|
6dc4f1b448 | ||
|
|
e1fa17691d | ||
|
|
42e61d6e1f | ||
|
|
2bf3e6bd3c | ||
|
|
608b2f868d | ||
|
|
46638bab5b | ||
|
|
4607362e46 | ||
|
|
041b2dfcc1 | ||
|
|
7ad0dd43e2 | ||
|
|
35339f7328 | ||
|
|
e474e7cd75 | ||
|
|
612fab2aef | ||
|
|
3f67bd471b | ||
|
|
cb29dcd6cc | ||
|
|
d428e89c30 | ||
|
|
9b2aeb7480 | ||
|
|
d9ad69c7ff | ||
|
|
16fc058264 | ||
|
|
520b143165 | ||
|
|
95c37a03f8 | ||
|
|
0d058b9c9c | ||
|
|
7c72b5e69b | ||
|
|
3ec254e908 | ||
|
|
fda13c004e | ||
|
|
3af85eeea6 | ||
|
|
c08616e6df | ||
|
|
7faa0f8a54 | ||
|
|
26c5fe2e69 | ||
|
|
6bdaa8352b | ||
|
|
6367a1fd15 | ||
|
|
2c5ff64d42 | ||
|
|
e614cb41d8 | ||
|
|
82737e2280 | ||
|
|
3b5c27096b | ||
|
|
8a85830bd3 | ||
|
|
19ac90688e | ||
|
|
5f4a6bc462 | ||
|
|
4c7f3166d4 | ||
|
|
4233f4dd88 | ||
|
|
bd09312418 | ||
|
|
11ab07b238 | ||
|
|
d2e2f089fb | ||
|
|
d619f98de7 | ||
|
|
89228a52dc | ||
|
|
90a49efa4a | ||
|
|
a7423caeb1 | ||
|
|
6f31a2c228 | ||
|
|
fc98b0882f | ||
|
|
dff05967ea | ||
|
|
ec836a2f9d | ||
|
|
737c85cf00 | ||
|
|
ff7b07f464 | ||
|
|
19422b5afa | ||
|
|
6b93e413b6 | ||
|
|
ab72736bcc | ||
|
|
38939e5d05 | ||
|
|
1a90a0e0ad | ||
|
|
94252de73f | ||
|
|
d43072663e | ||
|
|
eb3562567d |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [jendib]
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -11,6 +11,6 @@
|
||||
*.iml
|
||||
node_modules
|
||||
import_test
|
||||
docs-importer-linux
|
||||
docs-importer-macos
|
||||
docs-importer-win.exe
|
||||
teedy-importer-linux
|
||||
teedy-importer-macos
|
||||
teedy-importer-win.exe
|
||||
26
.travis.yml
26
.travis.yml
@@ -1,26 +0,0 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: java
|
||||
before_install:
|
||||
- sudo add-apt-repository -y ppa:mc3man/trusty-media
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -y -q install ffmpeg mediainfo tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra tesseract-ocr-nld tesseract-ocr-tur tesseract-ocr-heb
|
||||
- sudo apt-get -y -q install haveged && sudo service haveged start
|
||||
after_success:
|
||||
- |
|
||||
if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
|
||||
mvn -Pprod -DskipTests clean install
|
||||
docker login -u $DOCKER_USER -p $DOCKER_PASS
|
||||
export REPO=sismics/docs
|
||||
export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo $TRAVIS_BRANCH ; fi`
|
||||
docker build -f Dockerfile -t $REPO:$COMMIT .
|
||||
docker tag $REPO:$COMMIT $REPO:$TAG
|
||||
docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER
|
||||
docker push $REPO
|
||||
fi
|
||||
env:
|
||||
global:
|
||||
- secure: LRGpjWORb0qy6VuypZjTAfA8uRHlFUMTwb77cenS9PPRBxuSnctC531asS9Xg3DqC5nsRxBBprgfCKotn5S8nBSD1ceHh84NASyzLSBft3xSMbg7f/2i7MQ+pGVwLncusBU6E/drnMFwZBleo+9M8Tf96axY5zuUp90MUTpSgt0=
|
||||
- secure: bCDDR6+I7PmSkuTYZv1HF/z98ANX/SFEESUCqxVmV5Gs0zFC0vQXaPJQ2xaJNRop1HZBFMZLeMMPleb0iOs985smpvK2F6Rbop9Tu+Vyo0uKqv9tbZ7F8Nfgnv9suHKZlL84FNeUQZJX6vsFIYPEJ/r7K5P/M0PdUy++fEwxEhU=
|
||||
- secure: ewXnzbkgCIHpDWtaWGMa1OYZJ/ki99zcIl4jcDPIC0eB3njX/WgfcC6i0Ke9mLqDqwXarWJ6helm22sNh+xtQiz6isfBtBX+novfRt9AANrBe3koCMUemMDy7oh5VflBaFNP0DVb8LSCnwf6dx6ZB5E9EB8knvk40quc/cXpGjY=
|
||||
- COMMIT=${TRAVIS_COMMIT::8}
|
||||
37
Dockerfile
37
Dockerfile
@@ -1,7 +1,36 @@
|
||||
FROM sismics/ubuntu-jetty:9.4.12
|
||||
MAINTAINER b.gamard@sismics.com
|
||||
FROM sismics/ubuntu-jetty:9.4.36
|
||||
LABEL maintainer="b.gamard@sismics.com"
|
||||
|
||||
RUN apt-get update && apt-get -y -q install ffmpeg mediainfo tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra tesseract-ocr-nld tesseract-ocr-tur tesseract-ocr-heb && \
|
||||
RUN apt-get update && \
|
||||
apt-get -y -q --no-install-recommends install \
|
||||
ffmpeg \
|
||||
mediainfo \
|
||||
tesseract-ocr \
|
||||
tesseract-ocr-ara \
|
||||
tesseract-ocr-chi-sim \
|
||||
tesseract-ocr-chi-tra \
|
||||
tesseract-ocr-dan \
|
||||
tesseract-ocr-deu \
|
||||
tesseract-ocr-fin \
|
||||
tesseract-ocr-fra \
|
||||
tesseract-ocr-heb \
|
||||
tesseract-ocr-hin \
|
||||
tesseract-ocr-hun \
|
||||
tesseract-ocr-ita \
|
||||
tesseract-ocr-jpn \
|
||||
tesseract-ocr-kor \
|
||||
tesseract-ocr-lav \
|
||||
tesseract-ocr-nld \
|
||||
tesseract-ocr-nor \
|
||||
tesseract-ocr-pol \
|
||||
tesseract-ocr-por \
|
||||
tesseract-ocr-rus \
|
||||
tesseract-ocr-spa \
|
||||
tesseract-ocr-swe \
|
||||
tesseract-ocr-tha \
|
||||
tesseract-ocr-tur \
|
||||
tesseract-ocr-ukr \
|
||||
tesseract-ocr-vie && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Remove the embedded javax.mail jar from Jetty
|
||||
@@ -9,3 +38,5 @@ RUN rm -f /opt/jetty/lib/mail/javax.mail.glassfish-*.jar
|
||||
|
||||
ADD docs.xml /opt/jetty/webapps/docs.xml
|
||||
ADD docs-web/target/docs-web-*.war /opt/jetty/webapps/docs.war
|
||||
|
||||
ENV JAVA_OPTIONS -Xmx1g
|
||||
147
README.md
147
README.md
@@ -2,17 +2,13 @@
|
||||
<img src="https://teedy.io/img/github-title.png" alt="Teedy" width=500 />
|
||||
</h3>
|
||||
|
||||
[](https://twitter.com/teedyio)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](http://travis-ci.org/sismics/docs)
|
||||
|
||||
Teedy is an open source, lightweight document management system for individuals and businesses.
|
||||
|
||||
**Discuss it on [Product Hunt](https://www.producthunt.com/posts/sismics-docs) 🦄**
|
||||
|
||||
<hr />
|
||||
<h2 align="center">
|
||||
✨ Sismics Docs is now called Teedy! You can still find our cloud and support offer on <a href="https://teedy.io">teedy.io</a> ✨
|
||||
✨ <a href="https://github.com/users/jendib/sponsorship">Sponsor this project if you use and appreciate it!</a> ✨
|
||||
</h2>
|
||||
<hr />
|
||||
|
||||
@@ -22,6 +18,7 @@ Demo
|
||||
----
|
||||
|
||||
A demo is available at [demo.teedy.io](https://demo.teedy.io)
|
||||
|
||||
- Guest login is enabled with read access on all documents
|
||||
- "admin" login with "admin" password
|
||||
- "demo" login with "password" password
|
||||
@@ -31,6 +28,7 @@ Features
|
||||
|
||||
- Responsive user interface
|
||||
- Optical character recognition
|
||||
- LDAP authentication 
|
||||
- Support image, PDF, ODT, DOCX, PPTX files
|
||||
- Video file support
|
||||
- Flexible search engine with suggestions and highlighting
|
||||
@@ -62,37 +60,151 @@ Install with Docker
|
||||
A preconfigured Docker image is available, including OCR and media conversion tools, listening on port 8080. The database is an embedded H2 database but PostgreSQL is also supported for more performance.
|
||||
|
||||
**The default admin password is "admin". Don't forget to change it before going to production.**
|
||||
|
||||
- Master branch, can be unstable. Not recommended for production use: `sismics/docs:latest`
|
||||
- Latest stable version: `sismics/docs:v1.7`
|
||||
- Latest stable version: `sismics/docs:v1.10`
|
||||
|
||||
The data directory is `/data`. Don't forget to mount a volume on it.
|
||||
|
||||
To build external URL, the server is expecting a `DOCS_BASE_URL` environment variable (for example https://teedy.mycompany.com)
|
||||
|
||||
### Available environment variables
|
||||
|
||||
- General
|
||||
- `DOCS_BASE_URL`: The base url used by the application. Generated url's will be using this as base.
|
||||
- `DOCS_GLOBAL_QUOTA`: Defines the default quota applying to all users.
|
||||
- `DOCS_BCRYPT_WORK`: Defines the work factor which is used for password hashing. The default is `10`. This value may be `4...31` including `4` and `31`. The specified value will be used for all new users and users changing their password. Be aware that setting this factor to high can heavily impact login and user creation performance.
|
||||
|
||||
- Admin
|
||||
- `DOCS_ADMIN_EMAIL_INIT`: Defines the e-mail-address the admin user should have upon initialization.
|
||||
- `DOCS_ADMIN_PASSWORD_INIT`: Defines the password the admin user should have upon initialization. Needs to be a bcrypt hash. **Be aware that `$` within the hash have to be escaped with a second `$`.**
|
||||
|
||||
- Database
|
||||
- `DATABASE_URL`: The jdbc connection string to be used by `hibernate`.
|
||||
- `DATABASE_USER`: The user which should be used for the database connection.
|
||||
- `DATABASE_PASSWORD`: The password to be used for the database connection.
|
||||
|
||||
- Language
|
||||
- `DOCS_DEFAULT_LANGUAGE`: The language which will be used as default. Currently supported values are:
|
||||
- `eng`, `fra`, `ita`, `deu`, `spa`, `por`, `pol`, `rus`, `ukr`, `ara`, `hin`, `chi_sim`, `chi_tra`, `jpn`, `tha`, `kor`, `nld`, `tur`, `heb`, `hun`, `fin`, `swe`, `lav`, `dan`
|
||||
|
||||
- E-Mail
|
||||
- `DOCS_SMTP_HOSTNAME`: Hostname of the SMTP-Server to be used by Teedy.
|
||||
- `DOCS_SMTP_PORT`: The port which should be used.
|
||||
- `DOCS_SMTP_USERNAME`: The username to be used.
|
||||
- `DOCS_SMTP_PASSWORD`: The password to be used.
|
||||
|
||||
### Examples
|
||||
|
||||
In the following examples some passwords are exposed in cleartext. This was done in order to keep the examples simple. We strongly encourage you to use variables with an `.env` file or other means to securely store your passwords.
|
||||
|
||||
#### Using the internal database
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
# Teedy Application
|
||||
teedy-server:
|
||||
image: sismics/docs:v1.10
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
# Map internal port to host
|
||||
- 8080:8080
|
||||
environment:
|
||||
# Base url to be used
|
||||
DOCS_BASE_URL: "https://docs.example.com"
|
||||
# Set the admin email
|
||||
DOCS_ADMIN_EMAIL_INIT: "admin@example.com"
|
||||
# Set the admin password (in this example: "superSecure")
|
||||
DOCS_ADMIN_PASSWORD_INIT: "$$2a$$05$$PcMNUbJvsk7QHFSfEIDaIOjk1VI9/E7IPjTKx.jkjPxkx2EOKSoPS"
|
||||
volumes:
|
||||
- ./docs/data:/data
|
||||
```
|
||||
|
||||
#### Using PostgreSQL
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
# Teedy Application
|
||||
teedy-server:
|
||||
image: sismics/docs:v1.10
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
# Map internal port to host
|
||||
- 8080:8080
|
||||
environment:
|
||||
# Base url to be used
|
||||
DOCS_BASE_URL: "https://docs.example.com"
|
||||
# Set the admin email
|
||||
DOCS_ADMIN_EMAIL_INIT: "admin@example.com"
|
||||
# Set the admin password (in this example: "superSecure")
|
||||
DOCS_ADMIN_PASSWORD_INIT: "$$2a$$05$$PcMNUbJvsk7QHFSfEIDaIOjk1VI9/E7IPjTKx.jkjPxkx2EOKSoPS"
|
||||
# Setup the database connection. "teedy-db" is the hostname
|
||||
# and "teedy" is the name of the database the application
|
||||
# will connect to.
|
||||
DATABASE_URL: "jdbc:postgresql://teedy-db:5432/teedy"
|
||||
DATABASE_USER: "teedy_db_user"
|
||||
DATABASE_PASSWORD: "teedy_db_password"
|
||||
volumes:
|
||||
- ./docs/data:/data
|
||||
networks:
|
||||
- docker-internal
|
||||
- internet
|
||||
depends_on:
|
||||
- teedy-db
|
||||
|
||||
# DB for Teedy
|
||||
teedy-db:
|
||||
image: postgres:13.1-alpine
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 5432
|
||||
environment:
|
||||
POSTGRES_USER: "teedy_db_user"
|
||||
POSTGRES_PASSWORD: "teedy_db_password"
|
||||
POSTGRES_DB: "teedy"
|
||||
volumes:
|
||||
- ./docs/db:/var/lib/postgresql/data
|
||||
networks:
|
||||
- docker-internal
|
||||
|
||||
networks:
|
||||
# Network without internet access. The db does not need
|
||||
# access to the host network.
|
||||
docker-internal:
|
||||
driver: bridge
|
||||
internal: true
|
||||
internet:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
Manual installation
|
||||
-------------------
|
||||
|
||||
#### Requirements
|
||||
- Java 8 with the [Java Cryptography Extension](http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html)
|
||||
- Tesseract 3 or 4 for OCR
|
||||
|
||||
- Java 11
|
||||
- Tesseract 4 for OCR
|
||||
- ffmpeg for video thumbnails
|
||||
- mediainfo for video metadata extraction
|
||||
- A webapp server like [Jetty](http://eclipse.org/jetty/) or [Tomcat](http://tomcat.apache.org/)
|
||||
|
||||
#### Download
|
||||
|
||||
The latest release is downloadable here: <https://github.com/sismics/docs/releases> in WAR format.
|
||||
**The default admin password is "admin". Don't forget to change it before going to production.**
|
||||
|
||||
How to build Teedy from the sources
|
||||
----------------------------------
|
||||
|
||||
Prerequisites: JDK 8 with JCE, Maven 3, Tesseract 3 or 4
|
||||
Prerequisites: JDK 11, Maven 3, NPM, Grunt, Tesseract 4
|
||||
|
||||
Teedy is organized in several Maven modules:
|
||||
|
||||
- docs-core
|
||||
- docs-web
|
||||
- docs-web-common
|
||||
- docs-core
|
||||
- docs-web
|
||||
- docs-web-common
|
||||
|
||||
First off, clone the repository: `git clone git://github.com/sismics/docs.git`
|
||||
or download the sources from GitHub.
|
||||
@@ -124,17 +236,6 @@ All contributions are more than welcomed. Contributions may close an issue, fix
|
||||
|
||||
The `master` branch is the default and base branch for the project. It is used for development and all Pull Requests should go there.
|
||||
|
||||
|
||||
Community
|
||||
---------
|
||||
|
||||
Get updates on Teedy's development and chat with the project maintainers:
|
||||
|
||||
- Follow [@teedyio on Twitter](https://twitter.com/teedyio)
|
||||
- Read and subscribe to [The Official Teedy Blog](https://blog.teedy.io/)
|
||||
- Check the [Official Website](https://teedy.io)
|
||||
- Join us [on Facebook](https://www.facebook.com/teedyio)
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public class LanguageAdapter extends BaseAdapter {
|
||||
languageList.add(new Language("fra", R.string.language_french, R.drawable.fra));
|
||||
languageList.add(new Language("eng", R.string.language_english, R.drawable.eng));
|
||||
languageList.add(new Language("deu", R.string.language_german, R.drawable.deu));
|
||||
languageList.add(new Language("pol", R.string.language_polish, R.drawable.pol));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,9 @@ public class SearchQueryBuilder {
|
||||
*/
|
||||
public SearchQueryBuilder simpleSearch(String simpleSearch) {
|
||||
if (isValid(simpleSearch)) {
|
||||
query.append(SEARCH_SEPARATOR).append(simpleSearch);
|
||||
query.append(SEARCH_SEPARATOR)
|
||||
.append("simple:")
|
||||
.append(simpleSearch);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
BIN
docs-android/app/src/main/res/drawable-xhdpi/pol.png
Normal file
BIN
docs-android/app/src/main/res/drawable-xhdpi/pol.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 B |
164
docs-android/app/src/main/res/values-pl/strings.xml
Normal file
164
docs-android/app/src/main/res/values-pl/strings.xml
Normal file
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Validation -->
|
||||
<string name="validate_error_email">Nieprawidłowy email</string>
|
||||
<string name="validate_error_length_min">Za krótki (min. %d)</string>
|
||||
<string name="validate_error_length_max">Za długi (max. %d)</string>
|
||||
<string name="validate_error_required">Wymagany</string>
|
||||
<string name="validate_error_alphanumeric">Tylko litery i cyfry</string>
|
||||
|
||||
<!-- App -->
|
||||
<string name="app_name" translatable="false">Teedy</string>
|
||||
<string name="drawer_open">Otwórz szufladę nawigacji</string>
|
||||
<string name="drawer_close">Zamknij szufladę nawigacji</string>
|
||||
<string name="login_explain"><![CDATA[Aby rozpocząć, musisz pobrać i zainstalować serwer Teedy na <a href="https://github.com/sismics/docs">github.com/sismics/docs</a> i poniżej wprowadzić adres]]></string>
|
||||
<string name="server">Serwer</string>
|
||||
<string name="username">Użytkownik</string>
|
||||
<string name="password">Hasło</string>
|
||||
<string name="login">Zaloguj</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="cancel">Anuluj</string>
|
||||
<string name="login_fail_title">Błąd logowania</string>
|
||||
<string name="login_fail">Nieprawidłowa nazwa użytkownika lub hasło</string>
|
||||
<string name="network_error_title">Błąd sieci</string>
|
||||
<string name="network_error">Błąd sieci, sprawdź połączenie z interneterm oraz adres URL serwera</string>
|
||||
<string name="invalid_url_title">Nieprawidłowy adres URL</string>
|
||||
<string name="invalid_url">Sprawdź adres URL serwera i spróbuj ponownie</string>
|
||||
<string name="crash_toast_text">Wystąpiła awaria, wysłano raport w celu rozwiązania tego problemu</string>
|
||||
<string name="created_date">Data utworzenia</string>
|
||||
<string name="download_file">Pobierz bieżący plik</string>
|
||||
<string name="download_document">Pobierz</string>
|
||||
<string name="action_search">Znadź dokumenty</string>
|
||||
<string name="all_documents">Wszystkie dokumenty</string>
|
||||
<string name="shared_documents">Udostępnione dokumenty</string>
|
||||
<string name="all_tags">Wszystkie etykiety</string>
|
||||
<string name="no_tags">Brak etykiet</string>
|
||||
<string name="error_loading_tags">Błąd ładowania etykiet</string>
|
||||
<string name="no_documents">Brak dokumentów</string>
|
||||
<string name="error_loading_documents">Błąd ładowania dokumentów</string>
|
||||
<string name="no_files">Brak plików</string>
|
||||
<string name="error_loading_files">Błąd ładowania plików</string>
|
||||
<string name="new_document">Nowy dokument</string>
|
||||
<string name="share">Udostępnij</string>
|
||||
<string name="close">Zamknij</string>
|
||||
<string name="add">Dodaj</string>
|
||||
<string name="add_share_hint">Nazwa udostępnienia (opcjonalnie)</string>
|
||||
<string name="document_not_shared">Ten dokument nie jest obecnie udostępniony</string>
|
||||
<string name="delete_share">Usuń udostępnienie</string>
|
||||
<string name="send_share">Wyślij link udostępnienia</string>
|
||||
<string name="error_loading_shares">Błąd ładowania udostępnień</string>
|
||||
<string name="error_adding_share">Błąd dodawania udostępnienia</string>
|
||||
<string name="share_default_name">Udostępnij link</string>
|
||||
<string name="error_deleting_share">Błąd usuwania udostępnienia</string>
|
||||
<string name="send_share_to">Wyślij link udostępnienia do</string>
|
||||
<string name="upload_file">dodaj plik</string>
|
||||
<string name="upload_from">Przeslij plik z</string>
|
||||
<string name="settings">ustawienia</string>
|
||||
<string name="logout">Wyloguj</string>
|
||||
<string name="version">Wersja</string>
|
||||
<string name="build">Kompilacja</string>
|
||||
<string name="pref_advanced_category">Ustawienia zaawansowane</string>
|
||||
<string name="pref_about_category">O programie</string>
|
||||
<string name="pref_github">GitHub</string>
|
||||
<string name="pref_issue">Zgłoś błąd</string>
|
||||
<string name="pref_clear_cache_title">Wyczyść cache</string>
|
||||
<string name="pref_clear_cache_summary">Wyczyść podręczne pliki</string>
|
||||
<string name="pref_clear_cache_success">Cache wyczyszczony</string>
|
||||
<string name="pref_clear_history_title">Wyczyść historię wyszukiwania</string>
|
||||
<string name="pref_clear_history_summary">Opróżnij ostatnie sugestie wyszukiwania</string>
|
||||
<string name="pref_clear_history_success">Historia wyszukiwania wyczyszczona</string>
|
||||
<string name="pref_cache_size">Rozmiar cache</string>
|
||||
<string name="language_french" translatable="false">Francuski</string>
|
||||
<string name="language_english" translatable="false">Angielski</string>
|
||||
<string name="language_german" translatable="false">Niemiecki</string>
|
||||
<string name="language_polish" translatable="false">Polski</string>
|
||||
<string name="save">Zapisz</string>
|
||||
<string name="edit_document">Edytuj</string>
|
||||
<string name="error_editing_document">Błąd sieci, spróbuj ponownie</string>
|
||||
<string name="please_wait">Proszę czekać</string>
|
||||
<string name="document_editing_message">Wysyłam twoje dane</string>
|
||||
<string name="delete_document">Usuń</string>
|
||||
<string name="delete_document_title">Usuń dokument</string>
|
||||
<string name="delete_document_message">Naprawdę chcesz usunąć dokument i powiązane z nim pliki?</string>
|
||||
<string name="document_delete_failure">Błąd sieci w czasie usuwania tego dokumentu</string>
|
||||
<string name="document_deleting_message">Usuwanie dokumentu</string>
|
||||
<string name="delete_file_title">Usuń plik</string>
|
||||
<string name="delete_file_message">Naprawdę chcesz usunąć ten plik?</string>
|
||||
<string name="file_delete_failure">Błąd sieci w czasie usuwania bieżącego pliku</string>
|
||||
<string name="file_deleting_message">Usuwanie pliku</string>
|
||||
<string name="error_reading_file">Błąd podczas odczytu pliku</string>
|
||||
<string name="upload_notification_title">Teedy</string>
|
||||
<string name="upload_notification_message">Przesyłanie nowego pliku do dokumentu</string>
|
||||
<string name="upload_notification_error">Błąd przsyłania nowego pliku</string>
|
||||
<string name="delete_file">Usuń bieżący plik</string>
|
||||
<string name="advanced_search">Zaawansowane wyszukiwanie</string>
|
||||
<string name="search">Znajdź</string>
|
||||
<string name="add_tags">Dodaj eytkiety</string>
|
||||
<string name="creation_date">Data utworzenia</string>
|
||||
<string name="description">Opis</string>
|
||||
<string name="title">Tytuł</string>
|
||||
<string name="simple_search">Proste wyszukiwanie</string>
|
||||
<string name="fulltext_search">Wyszukiwanie pełnotekstowe</string>
|
||||
<string name="creator">Autor</string>
|
||||
<string name="after_date">Po dacie</string>
|
||||
<string name="before_date">Przed datą</string>
|
||||
<string name="search_tags">Znajdź etykiety</string>
|
||||
<string name="all_languages">Wszystkie języki</string>
|
||||
<string name="toggle_informations">Przełącz informacje</string>
|
||||
<string name="who_can_access">Kto ma dostęp</string>
|
||||
<string name="comments">Komentarze</string>
|
||||
<string name="no_comments">Brak komentarzy</string>
|
||||
<string name="error_loading_comments">Błąd ładowania komentarzy</string>
|
||||
<string name="send">Wyślij</string>
|
||||
<string name="add_comment">Dodaj komentarz</string>
|
||||
<string name="comment_add_failure">Błąd dodawania komentarza</string>
|
||||
<string name="adding_comment">Dodawanie komentarza</string>
|
||||
<string name="comment_delete">Usuń komentarz</string>
|
||||
<string name="deleting_comment">Usuwanie komentarza</string>
|
||||
<string name="error_deleting_comment">Błąd usuwania komentarza</string>
|
||||
<string name="export_pdf">PDF</string>
|
||||
<string name="download">Pobierz</string>
|
||||
<string name="margin">Margines</string>
|
||||
<string name="fit_image_to_page">Dostosuj obraz do strony</string>
|
||||
<string name="export_comments">Eksport komentarzy</string>
|
||||
<string name="export_metadata">Eksport metadanych</string>
|
||||
<string name="mm">mm</string>
|
||||
<string name="download_file_title">Eksport plików Teedy</string>
|
||||
<string name="download_document_title">Eksport dokumentu Teedy</string>
|
||||
<string name="download_pdf_title">Eksport Teedy jako PDF</string>
|
||||
<string name="latest_activity">Ostatnie aktywności</string>
|
||||
<string name="activity">Aktywności</string>
|
||||
<string name="email">E-mail</string>
|
||||
<string name="storage_quota">Limit magazynu</string>
|
||||
<string name="storage_display">%1$d/%2$d MB</string>
|
||||
<string name="validation_code">Kod weryfikujący</string>
|
||||
<string name="shared">Udostępnienie</string>
|
||||
<string name="language">Język</string>
|
||||
<string name="coverage">Zakres</string>
|
||||
<string name="type">Rodzaj</string>
|
||||
<string name="source">Źródło</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="publisher">Udostępniający</string>
|
||||
<string name="identifier">Identifikator</string>
|
||||
<string name="subject">temat</string>
|
||||
<string name="rights">Prawa</string>
|
||||
<string name="contributors">Współtwórcy</string>
|
||||
<string name="relations">Powiązania</string>
|
||||
|
||||
<!-- Audit log -->
|
||||
<string name="auditlog_Acl">ACL</string>
|
||||
<string name="auditlog_Comment">Komentarz</string>
|
||||
<string name="auditlog_Document">Dokument</string>
|
||||
<string name="auditlog_File">Plik</string>
|
||||
<string name="auditlog_Group">Grupa</string>
|
||||
<string name="auditlog_Route">Przepływ</string>
|
||||
<string name="auditlog_RouteModel">Model przepływu</string>
|
||||
<string name="auditlog_Tag">Etykieta</string>
|
||||
<string name="auditlog_User">Użytkownik</string>
|
||||
<string name="auditlog_Webhook">Webhook</string>
|
||||
<string name="auditlog_created">utworzony</string>
|
||||
<string name="auditlog_updated">zaktualizowany</string>
|
||||
<string name="auditlog_deleted">usunięty</string>
|
||||
|
||||
</resources>
|
||||
@@ -72,6 +72,7 @@
|
||||
<string name="language_french" translatable="false">Français</string>
|
||||
<string name="language_english" translatable="false">English</string>
|
||||
<string name="language_german" translatable="false">Deutsch</string>
|
||||
<string name="language_polish" translatable="false">Polski</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="edit_document">Edit</string>
|
||||
<string name="error_editing_document">Network error, please try again</string>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-parent</artifactId>
|
||||
<version>1.7</version>
|
||||
<version>1.10</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -91,10 +91,10 @@
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mindrot</groupId>
|
||||
<artifactId>jbcrypt</artifactId>
|
||||
<groupId>at.favre.lib</groupId>
|
||||
<artifactId>bcrypt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -131,7 +131,12 @@
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.api</groupId>
|
||||
<artifactId>api-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Only there to read old index and rebuild them -->
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
@@ -189,7 +194,26 @@
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- JDK 11 JAXB dependencies -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-core</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
|
||||
@@ -42,5 +42,21 @@ public enum ConfigType {
|
||||
INBOX_PORT,
|
||||
INBOX_USERNAME,
|
||||
INBOX_PASSWORD,
|
||||
INBOX_TAG
|
||||
INBOX_FOLDER,
|
||||
INBOX_TAG,
|
||||
INBOX_AUTOMATIC_TAGS,
|
||||
INBOX_DELETE_IMPORTED,
|
||||
|
||||
/**
|
||||
* LDAP connection.
|
||||
*/
|
||||
LDAP_ENABLED,
|
||||
LDAP_HOST,
|
||||
LDAP_PORT,
|
||||
LDAP_ADMIN_DN,
|
||||
LDAP_ADMIN_PASSWORD,
|
||||
LDAP_BASE_DN,
|
||||
LDAP_FILTER,
|
||||
LDAP_DEFAULT_EMAIL,
|
||||
LDAP_DEFAULT_STORAGE
|
||||
}
|
||||
|
||||
@@ -18,13 +18,18 @@ public class Constants {
|
||||
/**
|
||||
* Administrator's default password ("admin").
|
||||
*/
|
||||
public static final String DEFAULT_ADMIN_PASSWORD = "$2a$05$6Ny3TjrW3aVAL1or2SlcR.fhuDgPKp5jp.P9fBXwVNePgeLqb4i3C";
|
||||
public static final String DEFAULT_ADMIN_PASSWORD = "$2y$10$xg0EEKVUehutDI1m6qQhVeFz7SMQMl1jQzjf2KkVsR2c7aV2vyyjK";
|
||||
|
||||
/**
|
||||
* Administrator's default email.
|
||||
*/
|
||||
public static final String DEFAULT_ADMIN_EMAIL = "admin@localhost";
|
||||
|
||||
/**
|
||||
* Bcrypt default work factor
|
||||
*/
|
||||
public static final int DEFAULT_BCRYPT_WORK = 10;
|
||||
|
||||
/**
|
||||
* Guest user ID.
|
||||
*/
|
||||
@@ -38,7 +43,7 @@ public class Constants {
|
||||
/**
|
||||
* Supported document languages.
|
||||
*/
|
||||
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor", "nld", "tur", "heb");
|
||||
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor", "nld", "tur", "heb", "hun", "fin", "swe", "lav", "dan", "nor", "vie");
|
||||
|
||||
/**
|
||||
* Base URL environment variable.
|
||||
@@ -73,6 +78,11 @@ public class Constants {
|
||||
*/
|
||||
public static final String ADMIN_EMAIL_INIT_ENV = "DOCS_ADMIN_EMAIL_INIT";
|
||||
|
||||
/**
|
||||
* Work factor to be used by Bcrypt
|
||||
*/
|
||||
public static final String BCRYPT_WORK_ENV = "DOCS_BCRYPT_WORK";
|
||||
|
||||
/**
|
||||
* Expiration time of the password recovery in hours.
|
||||
*/
|
||||
|
||||
@@ -128,6 +128,9 @@ public class AclDao {
|
||||
if (SecurityUtil.skipAclCheck(targetIdList)) {
|
||||
return true;
|
||||
}
|
||||
if (targetIdList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C from T_ACL a ");
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ContributorDao {
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ContributorDto> getByDocumentId(String documentId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select u.USE_USERNAME_C, u.USE_EMAIL_C from T_CONTRIBUTOR c ");
|
||||
StringBuilder sb = new StringBuilder("select distinct u.USE_USERNAME_C, u.USE_EMAIL_C from T_CONTRIBUTOR c ");
|
||||
sb.append(" join T_USER u on u.USE_ID_C = c.CTR_IDUSER_C ");
|
||||
sb.append(" where c.CTR_IDDOC_C = :documentId ");
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
|
||||
@@ -232,7 +232,7 @@ public class DocumentDao {
|
||||
*/
|
||||
public void updateFileId(Document document) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query query = em.createNativeQuery("update T_DOCUMENT d set d.DOC_IDFILE_C = :fileId, d.DOC_UPDATEDATE_D = :updateDate where d.DOC_ID_C = :id");
|
||||
Query query = em.createNativeQuery("update T_DOCUMENT d set DOC_IDFILE_C = :fileId, DOC_UPDATEDATE_D = :updateDate where d.DOC_ID_C = :id");
|
||||
query.setParameter("updateDate", new Date());
|
||||
query.setParameter("fileId", document.getFileId());
|
||||
query.setParameter("id", document.getId());
|
||||
|
||||
@@ -153,7 +153,7 @@ public class FileDao {
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a file by its ID.
|
||||
*
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
package com.sismics.docs.core.dao;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||
import org.joda.time.DateTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.dao.criteria.UserCriteria;
|
||||
import com.sismics.docs.core.dao.dto.UserDto;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
@@ -11,8 +17,6 @@ import com.sismics.docs.core.util.jpa.QueryParam;
|
||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
import org.joda.time.DateTime;
|
||||
import org.mindrot.jbcrypt.BCrypt;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
@@ -26,6 +30,11 @@ import java.util.*;
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class UserDao {
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger log = LoggerFactory.getLogger(UserDao.class);
|
||||
|
||||
/**
|
||||
* Authenticates an user.
|
||||
*
|
||||
@@ -39,7 +48,8 @@ public class UserDao {
|
||||
q.setParameter("username", username);
|
||||
try {
|
||||
User user = (User) q.getSingleResult();
|
||||
if (!BCrypt.checkpw(password, user.getPassword()) || user.getDisableDate() != null) {
|
||||
BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), user.getPassword());
|
||||
if (!result.verified || user.getDisableDate() != null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
@@ -277,7 +287,21 @@ public class UserDao {
|
||||
* @return Hashed password
|
||||
*/
|
||||
private String hashPassword(String password) {
|
||||
return BCrypt.hashpw(password, BCrypt.gensalt());
|
||||
int bcryptWork = Constants.DEFAULT_BCRYPT_WORK;
|
||||
String envBcryptWork = System.getenv(Constants.BCRYPT_WORK_ENV);
|
||||
if (envBcryptWork != null) {
|
||||
try {
|
||||
int envBcryptWorkInt = Integer.parseInt(envBcryptWork);
|
||||
if (envBcryptWorkInt >= 4 && envBcryptWorkInt <= 31) {
|
||||
bcryptWork = envBcryptWorkInt;
|
||||
} else {
|
||||
log.warn(Constants.BCRYPT_WORK_ENV + " needs to be in range 4...31. Falling back to " + Constants.DEFAULT_BCRYPT_WORK + ".");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn(Constants.BCRYPT_WORK_ENV + " needs to be a number in range 4...31. Falling back to " + Constants.DEFAULT_BCRYPT_WORK + ".");
|
||||
}
|
||||
}
|
||||
return BCrypt.withDefaults().hashToString(bcryptWork, password.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,7 +52,7 @@ public class DocumentCriteria {
|
||||
private List<List<String>> tagIdList;
|
||||
|
||||
/**
|
||||
* Tag IDs to excluded.
|
||||
* Tag IDs to exclude.
|
||||
* The first and second level list will be excluded.
|
||||
*/
|
||||
private List<List<String>> excludedTagIdList;
|
||||
@@ -76,7 +76,17 @@ public class DocumentCriteria {
|
||||
* A route is active.
|
||||
*/
|
||||
private Boolean activeRoute;
|
||||
|
||||
|
||||
/**
|
||||
* MIME type of a file.
|
||||
*/
|
||||
private String mimeType;
|
||||
|
||||
/**
|
||||
* The title.
|
||||
*/
|
||||
private String title;
|
||||
|
||||
public List<String> getTargetIdList() {
|
||||
return targetIdList;
|
||||
}
|
||||
@@ -181,4 +191,20 @@ public class DocumentCriteria {
|
||||
public void setActiveRoute(Boolean activeRoute) {
|
||||
this.activeRoute = activeRoute;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public void setMimeType(String mimeType) {
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.core.event;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
|
||||
/**
|
||||
* Document created event.
|
||||
@@ -10,32 +9,22 @@ import com.sismics.docs.core.model.jpa.Document;
|
||||
*/
|
||||
public class DocumentCreatedAsyncEvent extends UserEvent {
|
||||
/**
|
||||
* Created document.
|
||||
* Document ID.
|
||||
*/
|
||||
private Document document;
|
||||
|
||||
/**
|
||||
* Getter of document.
|
||||
*
|
||||
* @return the document
|
||||
*/
|
||||
public Document getDocument() {
|
||||
return document;
|
||||
private String documentId;
|
||||
|
||||
public String getDocumentId() {
|
||||
return documentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of document.
|
||||
*
|
||||
* @param document document
|
||||
*/
|
||||
public void setDocument(Document document) {
|
||||
this.document = document;
|
||||
public void setDocumentId(String documentId) {
|
||||
this.documentId = documentId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("document", document)
|
||||
.toString();
|
||||
.add("documentId", documentId)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.core.event;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
|
||||
/**
|
||||
* File deleted event.
|
||||
@@ -10,22 +9,22 @@ import com.sismics.docs.core.model.jpa.File;
|
||||
*/
|
||||
public class FileDeletedAsyncEvent extends UserEvent {
|
||||
/**
|
||||
* Deleted file.
|
||||
* File ID.
|
||||
*/
|
||||
private File file;
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
private String fileId;
|
||||
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public void setFile(File file) {
|
||||
this.file = file;
|
||||
public void setFileId(String fileId) {
|
||||
this.fileId = fileId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("file", file)
|
||||
.add("fileId", fileId)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.core.event;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@@ -12,9 +11,9 @@ import java.nio.file.Path;
|
||||
*/
|
||||
public abstract class FileEvent extends UserEvent {
|
||||
/**
|
||||
* Created file.
|
||||
* File ID.
|
||||
*/
|
||||
private File file;
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* Language of the file.
|
||||
@@ -25,15 +24,15 @@ public abstract class FileEvent extends UserEvent {
|
||||
* Unencrypted original file.
|
||||
*/
|
||||
private Path unencryptedFile;
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public void setFile(File file) {
|
||||
this.file = file;
|
||||
public void setFileId(String fileId) {
|
||||
this.fileId = fileId;
|
||||
}
|
||||
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
@@ -54,7 +53,7 @@ public abstract class FileEvent extends UserEvent {
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("file", file)
|
||||
.add("fileId", fileId)
|
||||
.add("language", language)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.sismics.docs.core.listener.async;
|
||||
import com.google.common.eventbus.AllowConcurrentEvents;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sismics.docs.core.dao.ContributorDao;
|
||||
import com.sismics.docs.core.dao.DocumentDao;
|
||||
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
|
||||
import com.sismics.docs.core.model.context.AppContext;
|
||||
import com.sismics.docs.core.model.jpa.Contributor;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.util.TransactionUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -34,15 +36,22 @@ public class DocumentCreatedAsyncListener {
|
||||
}
|
||||
|
||||
TransactionUtil.handle(() -> {
|
||||
// Fetch a fresh document
|
||||
Document document = new DocumentDao().getById(event.getDocumentId());
|
||||
if (document == null) {
|
||||
// The document has been deleted since
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the first contributor (the creator of the document)
|
||||
ContributorDao contributorDao = new ContributorDao();
|
||||
Contributor contributor = new Contributor();
|
||||
contributor.setDocumentId(event.getDocument().getId());
|
||||
contributor.setDocumentId(event.getDocumentId());
|
||||
contributor.setUserId(event.getUserId());
|
||||
contributorDao.create(contributor);
|
||||
|
||||
// Update index
|
||||
AppContext.getInstance().getIndexingHandler().createDocument(event.getDocument());
|
||||
AppContext.getInstance().getIndexingHandler().createDocument(document);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.google.common.eventbus.AllowConcurrentEvents;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
|
||||
import com.sismics.docs.core.model.context.AppContext;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.util.FileUtil;
|
||||
import com.sismics.docs.core.util.TransactionUtil;
|
||||
import org.slf4j.Logger;
|
||||
@@ -35,12 +34,11 @@ public class FileDeletedAsyncListener {
|
||||
}
|
||||
|
||||
// Delete the file from storage
|
||||
File file = event.getFile();
|
||||
FileUtil.delete(file);
|
||||
FileUtil.delete(event.getFileId());
|
||||
|
||||
TransactionUtil.handle(() -> {
|
||||
// Update index
|
||||
AppContext.getInstance().getIndexingHandler().deleteDocument(file.getId());
|
||||
AppContext.getInstance().getIndexingHandler().deleteDocument(event.getFileId());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Listener on file processing.
|
||||
@@ -52,15 +53,7 @@ public class FileProcessingAsyncListener {
|
||||
log.info("File created event: " + event.toString());
|
||||
}
|
||||
|
||||
TransactionUtil.handle(() -> {
|
||||
// Generate thumbnail, extract content
|
||||
processFile(event);
|
||||
|
||||
// Update index
|
||||
AppContext.getInstance().getIndexingHandler().createFile(event.getFile());
|
||||
});
|
||||
|
||||
FileUtil.endProcessingFile(event.getFile().getId());
|
||||
processFile(event, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,43 +64,84 @@ public class FileProcessingAsyncListener {
|
||||
@Subscribe
|
||||
@AllowConcurrentEvents
|
||||
public void on(final FileUpdatedAsyncEvent event) {
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("File updated event: " + event.toString());
|
||||
}
|
||||
log.info("File updated event: " + event.toString());
|
||||
|
||||
TransactionUtil.handle(() -> {
|
||||
// Generate thumbnail, extract content
|
||||
processFile(event);
|
||||
|
||||
// Update index
|
||||
AppContext.getInstance().getIndexingHandler().updateFile(event.getFile());
|
||||
});
|
||||
|
||||
FileUtil.endProcessingFile(event.getFile().getId());
|
||||
processFile(event, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the file (create/update).
|
||||
* Process a file :
|
||||
* Generate thumbnails
|
||||
* Extract and save text content
|
||||
*
|
||||
* @param event File event
|
||||
* @param isFileCreated True if the file was just created
|
||||
*/
|
||||
private void processFile(FileEvent event) {
|
||||
private void processFile(FileEvent event, boolean isFileCreated) {
|
||||
AtomicReference<File> file = new AtomicReference<>();
|
||||
AtomicReference<User> user = new AtomicReference<>();
|
||||
|
||||
// Open a first transaction to get what we need to start the processing
|
||||
TransactionUtil.handle(() -> {
|
||||
// Generate thumbnail, extract content
|
||||
file.set(new FileDao().getActiveById(event.getFileId()));
|
||||
if (file.get() == null) {
|
||||
// The file has been deleted since
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the creating user from the database for its private key
|
||||
UserDao userDao = new UserDao();
|
||||
user.set(userDao.getById(file.get().getUserId()));
|
||||
});
|
||||
|
||||
// Process the file outside of a transaction
|
||||
if (user.get() == null || file.get() == null) {
|
||||
// The user or file has been deleted
|
||||
FileUtil.endProcessingFile(event.getFileId());
|
||||
return;
|
||||
}
|
||||
String content = extractContent(event, user.get(), file.get());
|
||||
|
||||
// Open a new transaction to save the file content
|
||||
TransactionUtil.handle(() -> {
|
||||
// Save the file to database
|
||||
FileDao fileDao = new FileDao();
|
||||
File freshFile = fileDao.getActiveById(event.getFileId());
|
||||
if (freshFile == null) {
|
||||
// The file has been deleted since the text extraction started, ignore the result
|
||||
return;
|
||||
}
|
||||
|
||||
freshFile.setContent(content);
|
||||
fileDao.update(freshFile);
|
||||
|
||||
// Update index with the updated file
|
||||
if (isFileCreated) {
|
||||
AppContext.getInstance().getIndexingHandler().createFile(freshFile);
|
||||
} else {
|
||||
AppContext.getInstance().getIndexingHandler().updateFile(freshFile);
|
||||
}
|
||||
});
|
||||
|
||||
FileUtil.endProcessingFile(event.getFileId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract text content from a file.
|
||||
* This is executed outside of a transaction.
|
||||
*
|
||||
* @param event File event
|
||||
* @param user User whom created the file
|
||||
* @param file Fresh file
|
||||
* @return Text content
|
||||
*/
|
||||
private String extractContent(FileEvent event, User user, File file) {
|
||||
// Find a format handler
|
||||
final File file = event.getFile();
|
||||
FormatHandler formatHandler = FormatHandlerUtil.find(file.getMimeType());
|
||||
if (formatHandler == null) {
|
||||
log.info("Format unhandled: " + file.getMimeType());
|
||||
FileUtil.endProcessingFile(file.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the user from the database
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getById(event.getUserId());
|
||||
if (user == null) {
|
||||
// The user has been deleted meanwhile
|
||||
FileUtil.endProcessingFile(file.getId());
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Generate file variations
|
||||
@@ -132,28 +166,21 @@ public class FileProcessingAsyncListener {
|
||||
ImageUtil.writeJpeg(thumbnail, outputStream);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Unable to generate thumbnails", e);
|
||||
} catch (Throwable e) {
|
||||
log.error("Unable to generate thumbnails for: " + file, e);
|
||||
}
|
||||
|
||||
// Extract text content from the file
|
||||
long startTime = System.currentTimeMillis();
|
||||
String content = null;
|
||||
log.info("Start extracting content from: " + file);
|
||||
try {
|
||||
content = formatHandler.extractContent(event.getLanguage(), event.getUnencryptedFile());
|
||||
} catch (Exception e) {
|
||||
log.error("Error extracting content from: " + event.getFile(), e);
|
||||
} catch (Throwable e) {
|
||||
log.error("Error extracting content from: " + file, e);
|
||||
}
|
||||
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
|
||||
log.info(MessageFormat.format("File content extracted in {0}ms: " + file.getId(), System.currentTimeMillis() - startTime));
|
||||
|
||||
// Save the file to database
|
||||
FileDao fileDao = new FileDao();
|
||||
if (fileDao.getActiveById(file.getId()) == null) {
|
||||
// The file has been deleted since the text extraction started, ignore the result
|
||||
return;
|
||||
}
|
||||
|
||||
file.setContent(content);
|
||||
fileDao.update(file);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class WebhookAsyncListener {
|
||||
@Subscribe
|
||||
@AllowConcurrentEvents
|
||||
public void on(final DocumentCreatedAsyncEvent event) {
|
||||
triggerWebhook(WebhookEvent.DOCUMENT_CREATED, event.getDocument().getId());
|
||||
triggerWebhook(WebhookEvent.DOCUMENT_CREATED, event.getDocumentId());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@@ -54,19 +54,19 @@ public class WebhookAsyncListener {
|
||||
@Subscribe
|
||||
@AllowConcurrentEvents
|
||||
public void on(final FileCreatedAsyncEvent event) {
|
||||
triggerWebhook(WebhookEvent.FILE_CREATED, event.getFile().getId());
|
||||
triggerWebhook(WebhookEvent.FILE_CREATED, event.getFileId());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@AllowConcurrentEvents
|
||||
public void on(final FileUpdatedAsyncEvent event) {
|
||||
triggerWebhook(WebhookEvent.FILE_UPDATED, event.getFile().getId());
|
||||
triggerWebhook(WebhookEvent.FILE_UPDATED, event.getFileId());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@AllowConcurrentEvents
|
||||
public void on(final FileDeletedAsyncEvent event) {
|
||||
triggerWebhook(WebhookEvent.FILE_DELETED, event.getFile().getId());
|
||||
triggerWebhook(WebhookEvent.FILE_DELETED, event.getFileId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +86,7 @@ public class WebhookAsyncListener {
|
||||
}
|
||||
});
|
||||
|
||||
RequestBody body = RequestBody.create(JSON, "{\"event\": \"" + event.name() + "\", \"id\": \"" + id + "\"}");
|
||||
RequestBody body = RequestBody.create("{\"event\": \"" + event.name() + "\", \"id\": \"" + id + "\"}", JSON);
|
||||
|
||||
for (String webhookUrl : webhookUrlList) {
|
||||
Request request = new Request.Builder()
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.dao.UserDao;
|
||||
import com.sismics.docs.core.event.RebuildIndexAsyncEvent;
|
||||
import com.sismics.docs.core.listener.async.*;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.service.FileService;
|
||||
@@ -81,7 +80,7 @@ public class AppContext {
|
||||
List<Class<? extends IndexingHandler>> indexingHandlerList = Lists.newArrayList(
|
||||
new ClasspathScanner<IndexingHandler>().findClasses(IndexingHandler.class, "com.sismics.docs.core.util.indexing"));
|
||||
for (Class<? extends IndexingHandler> handlerClass : indexingHandlerList) {
|
||||
IndexingHandler handler = handlerClass.newInstance();
|
||||
IndexingHandler handler = handlerClass.getDeclaredConstructor().newInstance();
|
||||
if (handler.accept()) {
|
||||
indexingHandler = handler;
|
||||
break;
|
||||
@@ -172,7 +171,8 @@ public class AppContext {
|
||||
if (EnvironmentUtil.isUnitTest()) {
|
||||
return new EventBus();
|
||||
} else {
|
||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(8, 8,
|
||||
int threadCount = Math.max(Runtime.getRuntime().availableProcessors() / 2, 2);
|
||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(threadCount, threadCount,
|
||||
1L, TimeUnit.MINUTES,
|
||||
new LinkedBlockingQueue<>());
|
||||
asyncExecutorList.add(executor);
|
||||
|
||||
@@ -49,7 +49,6 @@ public class File implements Loggable {
|
||||
/**
|
||||
* OCR-ized content.
|
||||
*/
|
||||
@Lob
|
||||
@Column(name = "FIL_CONTENT_C")
|
||||
private String content;
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ public class FileService extends AbstractScheduledService {
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
class TemporaryPathReference extends PhantomReference<Path> {
|
||||
static class TemporaryPathReference extends PhantomReference<Path> {
|
||||
String path;
|
||||
TemporaryPathReference(Path referent, ReferenceQueue<? super Path> q) {
|
||||
super(referent, q);
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package com.sismics.docs.core.service;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.util.concurrent.AbstractScheduledService;
|
||||
import com.sismics.docs.core.constant.ConfigType;
|
||||
import com.sismics.docs.core.dao.TagDao;
|
||||
import com.sismics.docs.core.dao.criteria.TagCriteria;
|
||||
import com.sismics.docs.core.dao.dto.TagDto;
|
||||
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
|
||||
import com.sismics.docs.core.model.jpa.Config;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.Tag;
|
||||
import com.sismics.docs.core.util.ConfigUtil;
|
||||
import com.sismics.docs.core.util.DocumentUtil;
|
||||
import com.sismics.docs.core.util.FileUtil;
|
||||
import com.sismics.docs.core.util.TransactionUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.util.EmailUtil;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -19,9 +24,10 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.mail.*;
|
||||
import javax.mail.search.FlagTerm;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Inbox scanning service.
|
||||
@@ -79,22 +85,25 @@ public class InboxService extends AbstractScheduledService {
|
||||
lastSyncDate = new Date();
|
||||
lastSyncMessageCount = 0;
|
||||
try {
|
||||
HashMap<String, String> tagsNameToId = getAllTags();
|
||||
|
||||
inbox = openInbox();
|
||||
Message[] messages = inbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
|
||||
log.info(messages.length + " messages found");
|
||||
for (Message message : messages) {
|
||||
importMessage(message);
|
||||
importMessage(message, tagsNameToId);
|
||||
lastSyncMessageCount++;
|
||||
}
|
||||
} catch (FolderClosedException e) {
|
||||
// Ignore this, we will just continue importing on the next cycle
|
||||
} catch (Exception e) {
|
||||
log.error("Error synching the inbox", e);
|
||||
log.error("Error syncing the inbox", e);
|
||||
lastSyncError = e.getMessage();
|
||||
} finally {
|
||||
try {
|
||||
if (inbox != null) {
|
||||
inbox.close(false);
|
||||
// The parameter controls if the messages flagged to be deleted, should actually get deleted.
|
||||
inbox.close(ConfigUtil.getConfigBooleanValue(ConfigType.INBOX_DELETE_IMPORTED));
|
||||
inbox.getStore().close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -172,7 +181,7 @@ public class InboxService extends AbstractScheduledService {
|
||||
store.connect(ConfigUtil.getConfigStringValue(ConfigType.INBOX_USERNAME),
|
||||
ConfigUtil.getConfigStringValue(ConfigType.INBOX_PASSWORD));
|
||||
|
||||
Folder inbox = store.getFolder("INBOX");
|
||||
Folder inbox = store.getFolder(ConfigUtil.getConfigStringValue(ConfigType.INBOX_FOLDER));
|
||||
inbox.open(Folder.READ_WRITE);
|
||||
return inbox;
|
||||
}
|
||||
@@ -183,7 +192,7 @@ public class InboxService extends AbstractScheduledService {
|
||||
* @param message Message
|
||||
* @throws Exception e
|
||||
*/
|
||||
private void importMessage(Message message) throws Exception {
|
||||
private void importMessage(Message message, HashMap<String, String> tags) throws Exception {
|
||||
log.info("Importing message: " + message.getSubject());
|
||||
|
||||
// Parse the mail
|
||||
@@ -194,12 +203,27 @@ public class InboxService extends AbstractScheduledService {
|
||||
|
||||
// Create the document
|
||||
Document document = new Document();
|
||||
document.setUserId("admin");
|
||||
if (mailContent.getSubject() == null) {
|
||||
document.setTitle("Imported email from EML file");
|
||||
} else {
|
||||
document.setTitle(StringUtils.abbreviate(mailContent.getSubject(), 100));
|
||||
String subject = mailContent.getSubject();
|
||||
if (subject == null) {
|
||||
subject = "Imported email from EML file";
|
||||
}
|
||||
|
||||
HashSet<String> tagsFound = new HashSet<>();
|
||||
if (tags != null) {
|
||||
Pattern pattern = Pattern.compile("#([^\\s:#]+)");
|
||||
Matcher matcher = pattern.matcher(subject);
|
||||
while (matcher.find()) {
|
||||
if (tags.containsKey(matcher.group(1)) && tags.get(matcher.group(1)) != null) {
|
||||
tagsFound.add(tags.get(matcher.group(1)));
|
||||
subject = subject.replaceFirst("#" + matcher.group(1), "");
|
||||
}
|
||||
}
|
||||
log.debug("Tags found: " + String.join(", ", tagsFound));
|
||||
subject = subject.trim().replaceAll(" +", " ");
|
||||
}
|
||||
|
||||
document.setUserId("admin");
|
||||
document.setTitle(StringUtils.abbreviate(subject, 100));
|
||||
document.setDescription(StringUtils.abbreviate(mailContent.getMessage(), 4000));
|
||||
document.setSubject(StringUtils.abbreviate(mailContent.getSubject(), 500));
|
||||
document.setFormat("EML");
|
||||
@@ -220,14 +244,19 @@ public class InboxService extends AbstractScheduledService {
|
||||
TagDao tagDao = new TagDao();
|
||||
Tag tag = tagDao.getById(tagId);
|
||||
if (tag != null) {
|
||||
tagDao.updateTagList(document.getId(), Sets.newHashSet(tagId));
|
||||
tagsFound.add(tagId);
|
||||
}
|
||||
}
|
||||
|
||||
// Update tags
|
||||
if (!tagsFound.isEmpty()) {
|
||||
new TagDao().updateTagList(document.getId(), tagsFound);
|
||||
}
|
||||
|
||||
// Raise a document created event
|
||||
DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent();
|
||||
documentCreatedAsyncEvent.setUserId("admin");
|
||||
documentCreatedAsyncEvent.setDocument(document);
|
||||
documentCreatedAsyncEvent.setDocumentId(document.getId());
|
||||
ThreadLocalContext.get().addAsyncEvent(documentCreatedAsyncEvent);
|
||||
|
||||
// Add files to the document
|
||||
@@ -235,6 +264,29 @@ public class InboxService extends AbstractScheduledService {
|
||||
FileUtil.createFile(fileContent.getName(), null, fileContent.getFile(), fileContent.getSize(),
|
||||
document.getLanguage(), "admin", document.getId());
|
||||
}
|
||||
|
||||
if (ConfigUtil.getConfigBooleanValue(ConfigType.INBOX_DELETE_IMPORTED)) {
|
||||
message.setFlag(Flags.Flag.DELETED, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a HashMap with all tag names as keys and their respective ids as values.
|
||||
*
|
||||
* @return HashMap with all tags or null if not enabled
|
||||
*/
|
||||
private HashMap<String, String> getAllTags() {
|
||||
if (!ConfigUtil.getConfigBooleanValue(ConfigType.INBOX_AUTOMATIC_TAGS)) {
|
||||
return null;
|
||||
}
|
||||
TagDao tagDao = new TagDao();
|
||||
List<TagDto> tags = tagDao.findByCriteria(new TagCriteria().setTargetIdList(null), new SortCriteria(1, true));
|
||||
|
||||
HashMap<String, String> tagsNameToId = new HashMap<>();
|
||||
for (TagDto tagDto : tags) {
|
||||
tagsNameToId.put(tagDto.getName(), tagDto.getId());
|
||||
}
|
||||
return tagsNameToId;
|
||||
}
|
||||
|
||||
public Date getLastSyncDate() {
|
||||
|
||||
@@ -50,6 +50,19 @@ public class ConfigUtil {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long value of a configuration parameter.
|
||||
*
|
||||
* @param configType Type of the configuration parameter
|
||||
* @return Long value of the configuration parameter
|
||||
* @throws IllegalStateException Configuration parameter undefined
|
||||
*/
|
||||
public static long getConfigLongValue(ConfigType configType) {
|
||||
String value = getConfigStringValue(configType);
|
||||
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boolean value of a configuration parameter.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,8 @@ import com.sismics.util.context.ThreadLocalContext;
|
||||
import com.sismics.util.io.InputStreamReaderThread;
|
||||
import com.sismics.util.mime.MimeTypeUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
@@ -36,6 +38,11 @@ import java.util.*;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class FileUtil {
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
|
||||
|
||||
/**
|
||||
* File ID of files currently being processed.
|
||||
*/
|
||||
@@ -76,12 +83,12 @@ public class FileUtil {
|
||||
/**
|
||||
* Remove a file from the storage filesystem.
|
||||
*
|
||||
* @param file File to delete
|
||||
* @param fileId ID of file to delete
|
||||
*/
|
||||
public static void delete(File file) throws IOException {
|
||||
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||
Path webFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_web");
|
||||
Path thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_thumb");
|
||||
public static void delete(String fileId) throws IOException {
|
||||
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(fileId);
|
||||
Path webFile = DirectoryUtil.getStorageDirectory().resolve(fileId + "_web");
|
||||
Path thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(fileId + "_thumb");
|
||||
|
||||
if (Files.exists(storedFile)) {
|
||||
Files.delete(storedFile);
|
||||
@@ -126,7 +133,7 @@ public class FileUtil {
|
||||
// Validate global quota
|
||||
String globalStorageQuotaStr = System.getenv(Constants.GLOBAL_QUOTA_ENV);
|
||||
if (!Strings.isNullOrEmpty(globalStorageQuotaStr)) {
|
||||
long globalStorageQuota = Long.valueOf(globalStorageQuotaStr);
|
||||
long globalStorageQuota = Long.parseLong(globalStorageQuotaStr);
|
||||
long globalStorageCurrent = userDao.getGlobalStorageCurrent();
|
||||
if (globalStorageCurrent + fileSize > globalStorageQuota) {
|
||||
throw new IOException("QuotaReached");
|
||||
@@ -190,7 +197,7 @@ public class FileUtil {
|
||||
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||
fileCreatedAsyncEvent.setUserId(userId);
|
||||
fileCreatedAsyncEvent.setLanguage(language);
|
||||
fileCreatedAsyncEvent.setFile(file);
|
||||
fileCreatedAsyncEvent.setFileId(file.getId());
|
||||
fileCreatedAsyncEvent.setUnencryptedFile(unencryptedFile);
|
||||
ThreadLocalContext.get().addAsyncEvent(fileCreatedAsyncEvent);
|
||||
|
||||
@@ -211,6 +218,7 @@ public class FileUtil {
|
||||
*/
|
||||
public static void startProcessingFile(String fileId) {
|
||||
processingFileSet.add(fileId);
|
||||
log.info("Processing started for file: " + fileId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,6 +228,7 @@ public class FileUtil {
|
||||
*/
|
||||
public static void endProcessingFile(String fileId) {
|
||||
processingFileSet.remove(fileId);
|
||||
log.info("Processing ended for file: " + fileId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,7 +48,7 @@ public class ProcessFilesAction implements Action {
|
||||
FileUpdatedAsyncEvent event = new FileUpdatedAsyncEvent();
|
||||
event.setUserId("admin");
|
||||
event.setLanguage(documentDto.getLanguage());
|
||||
event.setFile(file);
|
||||
event.setFileId(file.getId());
|
||||
event.setUnencryptedFile(unencryptedFile);
|
||||
ThreadLocalContext.get().addAsyncEvent(event);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class AuthenticationUtil {
|
||||
|
||||
.map(clazz -> {
|
||||
try {
|
||||
return clazz.newInstance();
|
||||
return clazz.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.sismics.docs.core.util.authentication;
|
||||
|
||||
import com.sismics.docs.core.constant.ConfigType;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.dao.ConfigDao;
|
||||
import com.sismics.docs.core.dao.UserDao;
|
||||
import com.sismics.docs.core.model.jpa.Config;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.ConfigUtil;
|
||||
import com.sismics.util.ClasspathScanner;
|
||||
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
|
||||
import org.apache.directory.api.ldap.model.entry.Attribute;
|
||||
import org.apache.directory.api.ldap.model.entry.Entry;
|
||||
import org.apache.directory.api.ldap.model.entry.Value;
|
||||
import org.apache.directory.api.ldap.model.message.SearchScope;
|
||||
import org.apache.directory.ldap.client.api.DefaultLdapConnectionFactory;
|
||||
import org.apache.directory.ldap.client.api.LdapConnectionConfig;
|
||||
import org.apache.directory.ldap.client.api.LdapConnectionPool;
|
||||
import org.apache.directory.ldap.client.api.ValidatingPoolableLdapConnectionFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* LDAP authentication handler.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@ClasspathScanner.Priority(50) // Before the internal database
|
||||
public class LdapAuthenticationHandler implements AuthenticationHandler {
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger log = LoggerFactory.getLogger(LdapAuthenticationHandler.class);
|
||||
|
||||
/**
|
||||
* LDAP connection pool.
|
||||
*/
|
||||
private static LdapConnectionPool pool;
|
||||
|
||||
/**
|
||||
* Reset the LDAP pool.
|
||||
*/
|
||||
public static void reset() {
|
||||
if (pool != null) {
|
||||
try {
|
||||
pool.close();
|
||||
} catch (Exception e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
pool = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the LDAP pool.
|
||||
*/
|
||||
private static void init() {
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
Config ldapEnabled = configDao.getById(ConfigType.LDAP_ENABLED);
|
||||
if (pool != null || ldapEnabled == null || !Boolean.parseBoolean(ldapEnabled.getValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
LdapConnectionConfig config = new LdapConnectionConfig();
|
||||
config.setLdapHost(ConfigUtil.getConfigStringValue(ConfigType.LDAP_HOST));
|
||||
config.setLdapPort(ConfigUtil.getConfigIntegerValue(ConfigType.LDAP_PORT));
|
||||
config.setName(ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_DN));
|
||||
config.setCredentials(ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_PASSWORD));
|
||||
|
||||
DefaultLdapConnectionFactory factory = new DefaultLdapConnectionFactory(config);
|
||||
pool = new LdapConnectionPool(new ValidatingPoolableLdapConnectionFactory(factory), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User authenticate(String username, String password) {
|
||||
init();
|
||||
if (pool == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fetch and authenticate the user
|
||||
Entry userEntry;
|
||||
try {
|
||||
EntryCursor cursor = pool.getConnection().search(ConfigUtil.getConfigStringValue(ConfigType.LDAP_BASE_DN),
|
||||
ConfigUtil.getConfigStringValue(ConfigType.LDAP_FILTER).replace("USERNAME", username), SearchScope.SUBTREE);
|
||||
if (cursor.next()) {
|
||||
userEntry = cursor.get();
|
||||
pool.getConnection().bind(userEntry.getDn(), password);
|
||||
} else {
|
||||
// User not found
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error authenticating \"" + username + "\" using the LDAP", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
// The user is valid but never authenticated, create the user now
|
||||
log.info("\"" + username + "\" authenticated for the first time, creating the internal user");
|
||||
user = new User();
|
||||
user.setRoleId(Constants.DEFAULT_USER_ROLE);
|
||||
user.setUsername(username);
|
||||
user.setPassword(UUID.randomUUID().toString()); // No authentication using the internal database
|
||||
Attribute mailAttribute = userEntry.get("mail");
|
||||
if (mailAttribute == null || mailAttribute.get() == null) {
|
||||
user.setEmail(ConfigUtil.getConfigStringValue(ConfigType.LDAP_DEFAULT_EMAIL));
|
||||
} else {
|
||||
Value value = mailAttribute.get();
|
||||
user.setEmail(value.getString());
|
||||
}
|
||||
user.setStorageQuota(ConfigUtil.getConfigLongValue(ConfigType.LDAP_DEFAULT_STORAGE));
|
||||
try {
|
||||
userDao.create(user, "admin");
|
||||
} catch (Exception e) {
|
||||
log.error("Error while creating the internal user", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.sismics.docs.core.util.format;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.util.ClasspathScanner;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -26,12 +27,12 @@ public class FormatHandlerUtil {
|
||||
public static FormatHandler find(String mimeType) {
|
||||
try {
|
||||
for (Class<? extends FormatHandler> formatHandlerClass : FORMAT_HANDLERS) {
|
||||
FormatHandler formatHandler = formatHandlerClass.newInstance();
|
||||
FormatHandler formatHandler = formatHandlerClass.getDeclaredConstructor().newInstance();
|
||||
if (formatHandler.accept(mimeType)) {
|
||||
return formatHandler;
|
||||
}
|
||||
}
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.sismics.util.mime.MimeType;
|
||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.slf4j.Logger;
|
||||
@@ -60,7 +61,7 @@ public class PdfFormatHandler implements FormatHandler {
|
||||
for (int pageIndex = 0; pageIndex < pdfDocument.getNumberOfPages(); pageIndex++) {
|
||||
log.info("OCR page " + (pageIndex + 1) + "/" + pdfDocument.getNumberOfPages() + " of PDF file containing only images");
|
||||
sb.append(" ");
|
||||
sb.append(FileUtil.ocrFile(language, renderer.renderImage(pageIndex)));
|
||||
sb.append(FileUtil.ocrFile(language, renderer.renderImageWithDPI(pageIndex, 300, ImageType.GRAY)));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.apache.poi.xslf.extractor.XSLFPowerPointExtractor;
|
||||
import org.apache.poi.sl.extractor.SlideShowExtractor;
|
||||
import org.apache.poi.xslf.usermodel.XMLSlideShow;
|
||||
import org.apache.poi.xslf.usermodel.XSLFSlide;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class PptxFormatHandler implements FormatHandler {
|
||||
@Override
|
||||
public String extractContent(String language, Path file) throws Exception {
|
||||
XMLSlideShow pptx = loadPPtxFile(file);
|
||||
return new XSLFPowerPointExtractor(pptx).getText();
|
||||
return new SlideShowExtractor<>(pptx).getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,16 +37,15 @@ import org.apache.lucene.search.spell.LuceneDictionary;
|
||||
import org.apache.lucene.search.suggest.Lookup;
|
||||
import org.apache.lucene.search.suggest.analyzing.FuzzySuggester;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.NIOFSDirectory;
|
||||
import org.apache.lucene.store.NoLockFactory;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.apache.lucene.store.SimpleFSDirectory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
|
||||
@@ -117,7 +116,7 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
||||
} else if (luceneStorage.equals("FILE")) {
|
||||
Path luceneDirectory = DirectoryUtil.getLuceneDirectory();
|
||||
log.info("Using file Lucene storage: {}", luceneDirectory);
|
||||
directory = new SimpleFSDirectory(luceneDirectory, NoLockFactory.INSTANCE);
|
||||
directory = new NIOFSDirectory(luceneDirectory, NoLockFactory.INSTANCE);
|
||||
}
|
||||
|
||||
// Create an index writer
|
||||
@@ -252,7 +251,7 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
||||
" s.SHA_DELETEDATE_D IS NULL group by ac.ACL_SOURCEID_C) s on s.ACL_SOURCEID_C = d.DOC_ID_C " +
|
||||
" left join (SELECT count(f.FIL_ID_C) count, f.FIL_IDDOC_C " +
|
||||
" FROM T_FILE f " +
|
||||
" WHERE f.FIL_DELETEDATE_D IS NULL group by f.FIL_IDDOC_C) f on f.FIL_IDDOC_C = d.DOC_ID_C ");
|
||||
" WHERE f.FIL_DELETEDATE_D is null group by f.FIL_IDDOC_C) f on f.FIL_IDDOC_C = d.DOC_ID_C ");
|
||||
sb.append(" left join (select rs.*, rs3.idDocument " +
|
||||
"from T_ROUTE_STEP rs " +
|
||||
"join (select r.RTE_IDDOCUMENT_C idDocument, rs.RTP_IDROUTE_C idRoute, min(rs.RTP_ORDER_N) minOrder from T_ROUTE_STEP rs join T_ROUTE r on r.RTE_ID_C = rs.RTP_IDROUTE_C and r.RTE_DELETEDATE_D is null where rs.RTP_DELETEDATE_D is null and rs.RTP_ENDDATE_D is null group by rs.RTP_IDROUTE_C, r.RTE_IDDOCUMENT_C) rs3 on rs.RTP_IDROUTE_C = rs3.idRoute and rs.RTP_ORDER_N = rs3.minOrder " +
|
||||
@@ -278,7 +277,7 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
||||
criteriaList.add("d.DOC_ID_C in :documentIdList");
|
||||
parameterMap.put("documentIdList", documentSearchMap.keySet());
|
||||
|
||||
suggestSearchTerms(criteria.getSearch(), suggestionList);
|
||||
suggestSearchTerms(criteria.getFullSearch(), suggestionList);
|
||||
}
|
||||
if (criteria.getCreateDateMin() != null) {
|
||||
criteriaList.add("d.DOC_CREATEDATE_D >= :createDateMin");
|
||||
@@ -296,6 +295,10 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
||||
criteriaList.add("d.DOC_UPDATEDATE_D <= :updateDateMax");
|
||||
parameterMap.put("updateDateMax", criteria.getUpdateDateMax());
|
||||
}
|
||||
if (criteria.getTitle() != null) {
|
||||
criteriaList.add("d.DOC_TITLE_C = :title");
|
||||
parameterMap.put("title", criteria.getTitle());
|
||||
}
|
||||
if (criteria.getTagIdList() != null && !criteria.getTagIdList().isEmpty()) {
|
||||
int index = 0;
|
||||
for (List<String> tagIdList : criteria.getTagIdList()) {
|
||||
@@ -325,6 +328,11 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
||||
if (criteria.getShared() != null && criteria.getShared()) {
|
||||
criteriaList.add("s.count > 0");
|
||||
}
|
||||
if (criteria.getMimeType() != null) {
|
||||
sb.append("left join T_FILE f0 on f0.FIL_IDDOC_C = d.DOC_ID_C and f0.FIL_MIMETYPE_C = :mimeType and f0.FIL_DELETEDATE_D is null");
|
||||
parameterMap.put("mimeType", criteria.getMimeType());
|
||||
criteriaList.add("f0.FIL_ID_C is not null");
|
||||
}
|
||||
if (criteria.getLanguage() != null) {
|
||||
criteriaList.add("d.DOC_LANGUAGE_C = :language");
|
||||
parameterMap.put("language", criteria.getLanguage());
|
||||
@@ -390,7 +398,7 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
||||
LuceneDictionary dictionary = new LuceneDictionary(directoryReader, "title");
|
||||
suggester.build(dictionary);
|
||||
int lastIndex = search.lastIndexOf(' ');
|
||||
String suggestQuery = search.substring(lastIndex < 0 ? 0 : lastIndex);
|
||||
String suggestQuery = search.substring(Math.max(lastIndex, 0));
|
||||
List<Lookup.LookupResult> lookupResultList = suggester.lookup(suggestQuery, false, 10);
|
||||
for (Lookup.LookupResult lookupResult : lookupResultList) {
|
||||
suggestionList.add(lookupResult.key.toString());
|
||||
|
||||
@@ -99,11 +99,16 @@ public class EmailUtil {
|
||||
}
|
||||
|
||||
// Port
|
||||
int port = ConfigUtil.getConfigIntegerValue(ConfigType.SMTP_PORT);
|
||||
String envPort = System.getenv(Constants.SMTP_PORT_ENV);
|
||||
if (envPort == null) {
|
||||
email.setSmtpPort(ConfigUtil.getConfigIntegerValue(ConfigType.SMTP_PORT));
|
||||
} else {
|
||||
email.setSmtpPort(Integer.valueOf(envPort));
|
||||
if (envPort != null) {
|
||||
port = Integer.valueOf(envPort);
|
||||
}
|
||||
email.setSmtpPort(port);
|
||||
if (port == 465) {
|
||||
email.setSSLOnConnect(true);
|
||||
} else if (port == 587) {
|
||||
email.setStartTLSRequired(true);
|
||||
}
|
||||
|
||||
// Username and password
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.sismics.util;
|
||||
|
||||
import org.jsoup.helper.StringUtil;
|
||||
import org.jsoup.internal.StringUtil;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
@@ -28,7 +28,7 @@ public class HtmlToPlainText {
|
||||
}
|
||||
|
||||
// the formatting rules, implemented in a breadth-first DOM traverse
|
||||
private class FormattingVisitor implements NodeVisitor {
|
||||
static private class FormattingVisitor implements NodeVisitor {
|
||||
private static final int maxWidth = 80;
|
||||
private int width = 0;
|
||||
private StringBuilder accum = new StringBuilder(); // holds the accumulated text
|
||||
@@ -64,7 +64,7 @@ public class HtmlToPlainText {
|
||||
return; // don't accumulate long runs of empty spaces
|
||||
|
||||
if (text.length() + width > maxWidth) { // won't fit, needs to wrap
|
||||
String words[] = text.split("\\s+");
|
||||
String[] words = text.split("\\s+");
|
||||
for (int i = 0; i < words.length; i++) {
|
||||
String word = words[i];
|
||||
boolean last = i == words.length - 1;
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.sismics.util.jpa;
|
||||
|
||||
import com.sismics.docs.core.util.DirectoryUtil;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.slf4j.Logger;
|
||||
@@ -34,7 +33,6 @@ public final class EMF {
|
||||
try {
|
||||
properties = getEntityManagerProperties();
|
||||
|
||||
Environment.verifyProperties(properties);
|
||||
ConfigurationHelper.resolvePlaceHolders(properties);
|
||||
ServiceRegistry reg = new StandardServiceRegistryBuilder().applySettings(properties).build();
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
db.version=24
|
||||
db.version=27
|
||||
@@ -41,4 +41,4 @@ insert into T_LOCALE(LOC_ID_C) values('fr');
|
||||
insert into T_ROLE(ROL_ID_C, ROL_NAME_C, ROL_CREATEDATE_D) values('admin', 'Admin', NOW());
|
||||
insert into T_ROLE(ROL_ID_C, ROL_NAME_C, ROL_CREATEDATE_D) values('user', 'User', NOW());
|
||||
insert into T_ROLE_BASE_FUNCTION(RBF_ID_C, RBF_IDROLE_C, RBF_IDBASEFUNCTION_C, RBF_CREATEDATE_D) values('admin_ADMIN', 'admin', 'ADMIN', NOW());
|
||||
insert into T_USER(USE_ID_C, USE_IDLOCALE_C, USE_IDROLE_C, USE_USERNAME_C, USE_PASSWORD_C, USE_EMAIL_C, USE_THEME_C, USE_FIRSTCONNECTION_B, USE_CREATEDATE_D, USE_PRIVATEKEY_C) values('admin', 'en', 'admin', 'admin', '$2a$05$6Ny3TjrW3aVAL1or2SlcR.fhuDgPKp5jp.P9fBXwVNePgeLqb4i3C', 'admin@localhost', 'default.less', true, NOW(), 'AdminPk');
|
||||
insert into T_USER(USE_ID_C, USE_IDLOCALE_C, USE_IDROLE_C, USE_USERNAME_C, USE_PASSWORD_C, USE_EMAIL_C, USE_THEME_C, USE_FIRSTCONNECTION_B, USE_CREATEDATE_D, USE_PRIVATEKEY_C) values('admin', 'en', 'admin', 'admin', '$2y$10$xg0EEKVUehutDI1m6qQhVeFz7SMQMl1jQzjf2KkVsR2c7aV2vyyjK', 'admin@localhost', 'default.less', true, NOW(), 'AdminPk');
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
insert into T_CONFIG(CFG_ID_C, CFG_VALUE_C) values('INBOX_AUTOMATIC_TAGS', 'false');
|
||||
insert into T_CONFIG(CFG_ID_C, CFG_VALUE_C) values('INBOX_DELETE_IMPORTED', 'false');
|
||||
update T_CONFIG set CFG_VALUE_C = '25' where CFG_ID_C = 'DB_VERSION';
|
||||
@@ -0,0 +1,2 @@
|
||||
!PGSQL!UPDATE t_file SET fil_content_c = convert_from(loread(lo_open(fil_content_c::int, CAST( x'20000' AS integer)), 999999999), 'UNICODE')::TEXT WHERE fil_content_c IS NOT NULL;
|
||||
update T_CONFIG set CFG_VALUE_C = '26' where CFG_ID_C = 'DB_VERSION';
|
||||
@@ -0,0 +1,2 @@
|
||||
insert into T_CONFIG(CFG_ID_C, CFG_VALUE_C) values('INBOX_FOLDER', 'INBOX');
|
||||
update T_CONFIG set CFG_VALUE_C = '27' where CFG_ID_C = 'DB_VERSION';
|
||||
@@ -1,10 +0,0 @@
|
||||
email.template.password_recovery.subject=Bitte setzen Sie ihr Passwort zur\u00FCck
|
||||
email.template.password_recovery.hello=Hallo {0}.
|
||||
email.template.password_recovery.instruction1=Wir haben eine Anfrage zum Zur\u00FCcksetzen Ihres Passworts erhalten.<br/>Wenn Sie keine Hilfe angefordert haben, k\u00F6nnen Sie diese E-Mail einfach ignorieren.
|
||||
email.template.password_recovery.instruction2=Um Ihr Passwort zur\u00FCckzusetzen, besuchen Sie bitte den folgenden Link:
|
||||
email.template.password_recovery.click_here=Klicken Sie hier, um Ihr Passwort zur\u00FCckzusetzen
|
||||
email.template.route_step_validate.subject=Ein Dokument braucht Ihre Aufmerksamkeit
|
||||
email.template.route_step_validate.hello=Hallo {0}.
|
||||
email.template.route_step_validate.instruction1=Ihnen wurde ein Workflow-Schritt zugewiesen, der Ihre Aufmerksamkeit erfordert.
|
||||
email.template.route_step_validate.instruction2=Um das Dokument anzuzeigen und den Workflow zu \u00FCberpr\u00FCfen, besuchen Sie bitte den folgenden Link:
|
||||
email.no_html.error=Ihr E-Mail-Client unterst\u00FCtzt keine HTML-Nachrichten
|
||||
@@ -1,10 +0,0 @@
|
||||
email.template.password_recovery.subject=R\u00E9initialiser votre mot de passe
|
||||
email.template.password_recovery.hello=Bonjour {0}.
|
||||
email.template.password_recovery.instruction1=Nous avons re\u00E7u une demande de r\u00E9initialisation de mot de passe.<br/>Si vous n'avez rien demand\u00E9, vous pouvez ignorer cet mail.
|
||||
email.template.password_recovery.instruction2=Pour r\u00E9initialiser votre mot de passe, cliquez sur le lien ci-dessous :
|
||||
email.template.password_recovery.click_here=Cliquez ici pour r\u00E9initialiser votre mot de passe.
|
||||
email.template.route_step_validate.subject=Un document n\u00E9cessite votre attention
|
||||
email.template.route_step_validate.hello=Bonjour {0}.
|
||||
email.template.route_step_validate.instruction1=Une \u00E9tape de workflow vous a \u00E9t\u00E9 attribu\u00E9e et n\u00E9cessite votre attention.
|
||||
email.template.route_step_validate.instruction2=Pour voir le document et valider le workflow, veuillez visiter le lien ci-dessous :
|
||||
email.no_html.error=Votre client mail ne supporte pas les messages HTML
|
||||
@@ -1,10 +0,0 @@
|
||||
email.template.password_recovery.subject=\u8BF7\u91CD\u7F6E\u60A8\u7684\u5BC6\u7801
|
||||
email.template.password_recovery.hello=\u60A8\u597D {0}.
|
||||
email.template.password_recovery.instruction1=\u6211\u4EEC\u6536\u5230\u4E86\u4E00\u4E2A\u91CD\u7F6E\u60A8\u7684\u5BC6\u7801\u7684\u8BF7\u6C42\u3002<br/>\u5982\u679C\u60A8\u6CA1\u6709\u53D1\u9001\u8BE5\u8BF7\u6C42\uFF0C\u8BF7\u5FFD\u7565\u6B64\u7535\u5B50\u90AE\u4EF6
|
||||
email.template.password_recovery.instruction2=\u8981\u91CD\u7F6E\u60A8\u7684\u5BC6\u7801\uFF0C\u8BF7\u8BBF\u95EE\u4EE5\u4E0B\u94FE\u63A5\uFF1A
|
||||
email.template.password_recovery.click_here=\u8BF7\u70B9\u51FB\u6B64\u5904\u91CD\u7F6E\u60A8\u7684\u5BC6\u7801
|
||||
email.template.route_step_validate.subject=\u4E00\u4EFD\u6587\u4EF6\u9700\u8981\u4F60\u7684\u5173\u6CE8
|
||||
email.template.route_step_validate.hello={0}\uFF0C\u60A8\u597D.
|
||||
email.template.route_step_validate.instruction1=\u5DE5\u4F5C\u6D41\u6B65\u9AA4\u5DF2\u7ECF\u5206\u914D\u7ED9\u60A8\uFF0C\u9700\u8981\u60A8\u7684\u5173\u6CE8\u3002
|
||||
email.template.route_step_validate.instruction2=\u8981\u67E5\u770B\u6587\u6863\u5E76\u9A8C\u8BC1\u5DE5\u4F5C\u6D41\u7A0B\uFF0C\u8BF7\u8BBF\u95EE\u4EE5\u4E0B\u94FE\u63A5\uFF1A
|
||||
email.no_html.error=\u60A8\u7684\u7535\u5B50\u90AE\u4EF6\u5BA2\u6237\u7AEF\u4E0D\u652F\u6301HTML\u683C\u5F0F\u90AE\u4EF6
|
||||
@@ -1,10 +0,0 @@
|
||||
email.template.password_recovery.subject=\u8ACB\u91CD\u65B0\u8A2D\u7F6E\u60A8\u7684\u5BC6\u78BC
|
||||
email.template.password_recovery.hello=\u60A8\u597D{0}\uFF01
|
||||
email.template.password_recovery.instruction1=\u6211\u5011\u6536\u5230\u4E86\u91CD\u7F6E\u5BC6\u78BC\u7684\u8ACB\u6C42\u3002<br/>\u5982\u679C\u60A8\u6C92\u6709\u8ACB\u6C42\u5E6B\u52A9\uFF0C\u8ACB\u5FFD\u7565\u6B64\u96FB\u5B50\u90F5\u4EF6\u3002
|
||||
email.template.password_recovery.instruction2=\u8981\u91CD\u7F6E\u60A8\u7684\u5BC6\u78BC\uFF0C\u8ACB\u8A2A\u554F\u4EE5\u4E0B\u93C8\u63A5\uFF1A
|
||||
email.template.password_recovery.click_here=\u9EDE\u64CA\u9019\u88E1\u91CD\u7F6E\u60A8\u7684\u5BC6\u78BC
|
||||
email.template.route_step_validate.subject=\u4E00\u4EFD\u6587\u4EF6\u9700\u8981\u4F60\u7684\u95DC\u6CE8
|
||||
email.template.route_step_validate.hello={0}\uFF0C\u60A8\u597D.
|
||||
email.template.route_step_validate.instruction1=\u5DE5\u4F5C\u6D41\u6B65\u9A5F\u5DF2\u7D93\u5206\u914D\u7D66\u60A8\uFF0C\u9700\u8981\u60A8\u7684\u95DC\u6CE8\u3002
|
||||
email.template.route_step_validate.instruction2=\u8981\u67E5\u770B\u6587\u6A94\u4E26\u9A57\u8B49\u5DE5\u4F5C\u6D41\u7A0B\uFF0C\u8ACB\u8A2A\u554F\u4EE5\u4E0B\u93C8\u63A5\uFF1A
|
||||
email.no_html.error=\u60A8\u7684\u96FB\u5B50\u90F5\u4EF6\u5BA2\u6236\u7AEF\u4E0D\u652F\u6301HTML\u683C\u5F0F\u90F5\u4EF6
|
||||
10
docs-core/src/main/resources/messages_pl.properties
Normal file
10
docs-core/src/main/resources/messages_pl.properties
Normal file
@@ -0,0 +1,10 @@
|
||||
email.template.password_recovery.subject=Proszę zresetować swoje hasło
|
||||
email.template.password_recovery.hello=Witaj {0}.
|
||||
email.template.password_recovery.instruction1=Otrzymaliśmy żądanie zresetowania twojego hasła.<br/>Jeśli to nie ty potrzebujesz pomocy, moóżesz zignorować ten email.
|
||||
email.template.password_recovery.instruction2=Aby zresetować swoje hasło, proszę naciśnij link poniżej:
|
||||
email.template.password_recovery.click_here=Naciśnij, aby zresetować swoje hasło
|
||||
email.template.route_step_validate.subject=Dokument potrzebuje twojej uwagi
|
||||
email.template.route_step_validate.hello=Witaj {0}.
|
||||
email.template.route_step_validate.instruction1=Został Ci przypisany etap przepływu i wymaga Twojej uwagi.
|
||||
email.template.route_step_validate.instruction2=Aby wyświetlić dokument i zweryfikować przepływ pracy, kliknij poniższy link:
|
||||
email.no_html.error=Twój klient poczty e-mail nie obsługuje wiadomości HTML
|
||||
@@ -137,7 +137,6 @@ public class TestFileUtil {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
PdfUtil.convertToPdf(documentDto, Lists.newArrayList(file0, file1, file2, file3, file4, file5), true, true, 10, outputStream);
|
||||
Assert.assertTrue(outputStream.toByteArray().length > 0);
|
||||
com.google.common.io.Files.write(outputStream.toByteArray(), new java.io.File("C:\\Users\\Jendib\\Downloads\\test.pdf"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.sismics.util.format;
|
||||
|
||||
import com.sismics.docs.core.util.format.PdfFormatHandler;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Test of {@link PdfFormatHandler}
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class TestPdfFormatHandler {
|
||||
/**
|
||||
* Test related to https://github.com/sismics/docs/issues/373.
|
||||
*/
|
||||
@Test
|
||||
public void testIssue373() throws Exception {
|
||||
PdfFormatHandler formatHandler = new PdfFormatHandler();
|
||||
String content = formatHandler.extractContent("deu", Paths.get(ClassLoader.getSystemResource("file/issue373.pdf").toURI()));
|
||||
Assert.assertTrue(content.contains("Aufrechterhaltung"));
|
||||
Assert.assertTrue(content.contains("Außentemperatur"));
|
||||
Assert.assertTrue(content.contains("Grundumsatzmessungen"));
|
||||
Assert.assertTrue(content.contains("ermitteln"));
|
||||
}
|
||||
}
|
||||
BIN
docs-core/src/test/resources/file/issue373.pdf
Normal file
BIN
docs-core/src/test/resources/file/issue373.pdf
Normal file
Binary file not shown.
@@ -6,4 +6,5 @@ log4j.appender.MEMORY=com.sismics.util.log4j.MemoryAppender
|
||||
log4j.appender.MEMORY.size=1000
|
||||
|
||||
log4j.logger.com.sismics=INFO
|
||||
log4j.logger.org.hibernate=ERROR
|
||||
log4j.logger.org.hibernate=ERROR
|
||||
log4j.logger.org.apache.directory=ERROR
|
||||
16
docs-importer/Dockerfile
Normal file
16
docs-importer/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM node:14.2-alpine AS builder
|
||||
WORKDIR /build
|
||||
COPY main.js package-lock.json package.json ./
|
||||
RUN npm install && npm install -g pkg
|
||||
RUN pkg -t node14-alpine-x64 .
|
||||
|
||||
FROM alpine
|
||||
ENV TEEDY_TAG= TEEDY_ADDTAGS=false TEEDY_LANG=eng TEEDY_URL='http://localhost:8080' TEEDY_USERNAME=username TEEDY_PASSWORD=password TEEDY_COPYFOLDER= TEEDY_FILEFILTER=*
|
||||
RUN apk add --no-cache \
|
||||
libc6-compat \
|
||||
libstdc++
|
||||
ADD pref /root/.config/preferences/com.sismics.docs.importer.pref
|
||||
ADD env.sh /
|
||||
COPY --from=builder /build/teedy-importer ./
|
||||
|
||||
CMD ["/bin/ash","-c","/env.sh && /teedy-importer -d"]
|
||||
@@ -1,35 +1,53 @@
|
||||
File Importer
|
||||
=============
|
||||
# File Importer
|
||||
|
||||
This tool can be used to do a single import of files or to periodically scan for files in an input folder.
|
||||
|
||||
Downloads
|
||||
---------
|
||||
## Downloads
|
||||
|
||||
Built binaries for Windows/Linux/MacOSX can be found at <https://github.com/sismics/docs/releases>
|
||||
|
||||
Usage
|
||||
-----
|
||||
## Usage
|
||||
|
||||
```console
|
||||
./docs-importer-macos (for MacOSX)
|
||||
./docs-importer-linux (for Linux)
|
||||
docs-importer-win.exe (for Windows)
|
||||
```
|
||||
|
||||
A wizard will ask you for the import configuration and write it in `~/.config/preferences/com.sismics.docs.importer.pref`
|
||||
A wizard will ask you for the import configuration and write it in `~/.config/preferences/com.sismics.docs.importer.pref`.
|
||||
Words following a `#` in the filename will be added as tags to the document, if there is a tag with the same name on the Server.
|
||||
|
||||
For the next start, pass the `-d` argument to skip the wizard:
|
||||
|
||||
```console
|
||||
./docs-importer-linux -d
|
||||
```
|
||||
|
||||
Daemon mode
|
||||
-----------
|
||||
The daemon mode scan the input directory every 30 seconds for new files. Once a file is found and imported, it is **deleted**.
|
||||
## Daemon mode
|
||||
|
||||
The daemon mode scan the input directory every 30 seconds for new files. Once a file is found and imported, it is **deleted**. You can set a `copyFolder` to copy the file to before deletion.
|
||||
|
||||
## Docker
|
||||
|
||||
The docker image needs a volume mounted from a previously generated preference file at `/root/.config/preferences/com.sismics.docs.importer.pref`. The container will start the importer in daemon mode. It will look for files in `/import`.
|
||||
Example usage:
|
||||
|
||||
```
|
||||
docker run --name teedy-import -d -v /path/to/preferencefile:/root/.config/preferences/com.sismics.docs.importer.pref -v /path/to/import/folder:/import sismics/docs-importer:latest
|
||||
```
|
||||
### Environment variables
|
||||
Instead of mounting the preferences file, the options can also be set by setting the environment variables `TEEDY_TAG`, `TEEDY_ADDTAGS`, `TEEDY_LANG`, `TEEDY_COPYFOLDER`, `TEEDY_FILEFILTER`, `TEEDY_URL`, `TEEDY_USERNAME` and `TEEDY_PASSWORD`.
|
||||
The latter three have to be set for the importer to work. The value of `TEEDY_TAG` has to be set to the UUID of the tag, not the name (The UUID can be found by visiting `baseUrl/api/tag/list` in your browser).
|
||||
Example usage:
|
||||
|
||||
```
|
||||
docker run --name teedy-import -d -e TEEDY_TAG=2071fdf7-0e26-409d-b53d-f25823a5eb9e -e TEEDY_ADDTAGS=false -e TEEDY_LANG=eng -e TEEDY_URL='http://teedy.example.com:port' -e TEEDY_USERNAME=username -e TEEDY_PASSWORD=superSecretPassword -v /path/to/import/folder:/import sismics/docs-importer:latest
|
||||
```
|
||||
|
||||
## Build from sources
|
||||
|
||||
Build from sources
|
||||
------------------
|
||||
```console
|
||||
npm install
|
||||
npm install -g pkg
|
||||
pkg .
|
||||
```
|
||||
```
|
||||
|
||||
11
docs-importer/env.sh
Executable file
11
docs-importer/env.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/ash
|
||||
file=/root/.config/preferences/com.sismics.docs.importer.pref
|
||||
sed -i "s/env1/$TEEDY_TAG/g" $file
|
||||
sed -i "s/env2/$TEEDY_ADDTAGS/g" $file
|
||||
sed -i "s/env3/$TEEDY_LANG/g" $file
|
||||
sed -i "s,env4,$TEEDY_URL,g" $file
|
||||
sed -i "s/env5/$TEEDY_USERNAME/g" $file
|
||||
sed -i "s/env6/$TEEDY_PASSWORD/g" $file
|
||||
sed -i "s,env7,$TEEDY_COPYFOLDER,g" $file
|
||||
sed -i "s,env8,$TEEDY_FILEFILTER,g" $file
|
||||
echo "Environment variables replaced"
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const recursive = require('recursive-readdir');
|
||||
const minimatch = require("minimatch");
|
||||
const ora = require('ora');
|
||||
const inquirer = require('inquirer');
|
||||
const preferences = require('preferences');
|
||||
@@ -10,6 +11,7 @@ const _ = require('underscore');
|
||||
const request = require('request').defaults({
|
||||
jar: true
|
||||
});
|
||||
const qs = require('querystring');
|
||||
|
||||
// Load preferences
|
||||
const prefs = new preferences('com.sismics.docs.importer',{
|
||||
@@ -22,7 +24,7 @@ const prefs = new preferences('com.sismics.docs.importer',{
|
||||
});
|
||||
|
||||
// Welcome message
|
||||
console.log('Teedy Importer 1.0.0, https://teedy.io' +
|
||||
console.log('Teedy Importer 1.9, https://teedy.io' +
|
||||
'\n\n' +
|
||||
'This program let you import files from your system to Teedy' +
|
||||
'\n');
|
||||
@@ -141,13 +143,32 @@ const askPath = () => {
|
||||
|
||||
recursive(answers.path, function (error, files) {
|
||||
spinner.succeed(files.length + ' files in this directory');
|
||||
askTag();
|
||||
askFileFilter();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Ask for the file filter
|
||||
const askFileFilter = () => {
|
||||
console.log('');
|
||||
|
||||
inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'fileFilter',
|
||||
message: 'What pattern do you want to use to match files? (eg. *.+(pdf|txt|jpg))',
|
||||
default: prefs.importer.fileFilter || "*"
|
||||
}
|
||||
]).then(answers => {
|
||||
// Save fileFilter
|
||||
prefs.importer.fileFilter = answers.fileFilter;
|
||||
|
||||
askTag();
|
||||
});
|
||||
};
|
||||
|
||||
// Ask for the tag to add
|
||||
const askTag = () => {
|
||||
console.log('');
|
||||
@@ -176,7 +197,7 @@ const askTag = () => {
|
||||
{
|
||||
type: 'list',
|
||||
name: 'tag',
|
||||
message: 'Which tag to add on imported documents?',
|
||||
message: 'Which tag to add to all imported documents?',
|
||||
default: defaultTagName,
|
||||
choices: [ 'No tag' ].concat(_.pluck(tags, 'name'))
|
||||
}
|
||||
@@ -184,11 +205,109 @@ const askTag = () => {
|
||||
// Save tag
|
||||
prefs.importer.tag = answers.tag === 'No tag' ?
|
||||
'' : _.findWhere(tags, { name: answers.tag }).id;
|
||||
askDaemon();
|
||||
askAddTag();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const askAddTag = () => {
|
||||
console.log('');
|
||||
|
||||
inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'addtags',
|
||||
message: 'Do you want to add tags from the filename given with # ?',
|
||||
default: prefs.importer.addtags === true
|
||||
}
|
||||
]).then(answers => {
|
||||
// Save daemon
|
||||
prefs.importer.addtags = answers.addtags;
|
||||
|
||||
// Save all preferences in case the program is sig-killed
|
||||
askLang();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const askLang = () => {
|
||||
console.log('');
|
||||
|
||||
// Load tags
|
||||
const spinner = ora({
|
||||
text: 'Loading default language',
|
||||
spinner: 'flips'
|
||||
}).start();
|
||||
|
||||
request.get({
|
||||
url: prefs.importer.baseUrl + '/api/app',
|
||||
}, function (error, response, body) {
|
||||
if (error || !response || response.statusCode !== 200) {
|
||||
spinner.fail('Connection to Teedy failed: ' + error);
|
||||
askLang();
|
||||
return;
|
||||
}
|
||||
spinner.succeed('Language loaded');
|
||||
const defaultLang = prefs.importer.lang ? prefs.importer.lang : JSON.parse(body).default_language;
|
||||
|
||||
inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'lang',
|
||||
message: 'Which should be the default language of the document?',
|
||||
default: defaultLang
|
||||
}
|
||||
]).then(answers => {
|
||||
// Save tag
|
||||
prefs.importer.lang = answers.lang
|
||||
askCopyFolder();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const askCopyFolder = () => {
|
||||
console.log('');
|
||||
|
||||
inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'copyFolder',
|
||||
message: 'Enter a path to copy files before they are deleted or leave empty to disable. The path must end with a \'/\' on MacOS and Linux or with a \'\\\' on Windows. Entering \'undefined\' will disable this again after setting the folder.',
|
||||
default: prefs.importer.copyFolder
|
||||
}
|
||||
]).then(answers => {
|
||||
// Save path
|
||||
prefs.importer.copyFolder = answers.copyFolder=='undefined' ? '' : answers.copyFolder;
|
||||
|
||||
if (prefs.importer.copyFolder) {
|
||||
// Test path
|
||||
const spinner = ora({
|
||||
text: 'Checking copy folder path',
|
||||
spinner: 'flips'
|
||||
}).start();
|
||||
fs.lstat(answers.copyFolder, (error, stats) => {
|
||||
if (error || !stats.isDirectory()) {
|
||||
spinner.fail('Please enter a valid directory path');
|
||||
askCopyFolder();
|
||||
return;
|
||||
}
|
||||
|
||||
fs.access(answers.copyFolder, fs.W_OK | fs.R_OK, (error) => {
|
||||
if (error) {
|
||||
spinner.fail('This directory is not writable');
|
||||
askCopyFolder();
|
||||
return;
|
||||
}
|
||||
spinner.succeed('Copy folder set!');
|
||||
askDaemon();
|
||||
});
|
||||
});
|
||||
}
|
||||
else {askDaemon();}
|
||||
});
|
||||
};
|
||||
|
||||
// Ask for daemon mode
|
||||
const askDaemon = () => {
|
||||
console.log('');
|
||||
@@ -245,6 +364,8 @@ const start = () => {
|
||||
// Import the files
|
||||
const importFiles = (remove, filesImported) => {
|
||||
recursive(prefs.importer.path, function (error, files) {
|
||||
|
||||
files = files.filter(minimatch.filter(prefs.importer.fileFilter || '*', { matchBase: true }));
|
||||
if (files.length === 0) {
|
||||
filesImported();
|
||||
return;
|
||||
@@ -270,37 +391,94 @@ const importFile = (file, remove, resolve) => {
|
||||
spinner: 'flips'
|
||||
}).start();
|
||||
|
||||
request.put({
|
||||
url: prefs.importer.baseUrl + '/api/document',
|
||||
form: {
|
||||
title: file.replace(/^.*[\\\/]/, ''),
|
||||
language: 'eng',
|
||||
tags: prefs.importer.tag === '' ? undefined : prefs.importer.tag
|
||||
}
|
||||
}, function (error, response, body) {
|
||||
// Remove path of file
|
||||
let filename = file.replace(/^.*[\\\/]/, '');
|
||||
|
||||
// Get Tags given as hashtags from filename
|
||||
let taglist = filename.match(/#[^\s:#]+/mg);
|
||||
taglist = taglist ? taglist.map(s => s.substr(1)) : [];
|
||||
|
||||
// Get available tags and UUIDs from server
|
||||
request.get({
|
||||
url: prefs.importer.baseUrl + '/api/tag/list',
|
||||
}, function (error, response, body) {
|
||||
if (error || !response || response.statusCode !== 200) {
|
||||
spinner.fail('Upload failed for ' + file + ': ' + error);
|
||||
resolve();
|
||||
spinner.fail('Error loading tags');
|
||||
return;
|
||||
}
|
||||
|
||||
let tagsarray = {};
|
||||
for (let l of JSON.parse(body).tags) {
|
||||
tagsarray[l.name] = l.id;
|
||||
}
|
||||
|
||||
request.put({
|
||||
url: prefs.importer.baseUrl + '/api/file',
|
||||
formData: {
|
||||
id: JSON.parse(body).id,
|
||||
file: fs.createReadStream(file)
|
||||
// Intersect tags from filename with existing tags on server
|
||||
let foundtags = [];
|
||||
for (let j of taglist) {
|
||||
// If the tag is last in the filename it could include a file extension and would not be recognized
|
||||
if (j.includes('.') && !tagsarray.hasOwnProperty(j) && !foundtags.includes(tagsarray[j])) {
|
||||
while (j.includes('.') && !tagsarray.hasOwnProperty(j)) {
|
||||
j = j.replace(/\.[^.]*$/,'');
|
||||
}
|
||||
}
|
||||
}, function (error, response) {
|
||||
if (tagsarray.hasOwnProperty(j) && !foundtags.includes(tagsarray[j])) {
|
||||
foundtags.push(tagsarray[j]);
|
||||
filename = filename.split('#'+j).join('');
|
||||
}
|
||||
}
|
||||
if (prefs.importer.tag !== '' && !foundtags.includes(prefs.importer.tag)){
|
||||
foundtags.push(prefs.importer.tag);
|
||||
}
|
||||
|
||||
let data = {}
|
||||
if (prefs.importer.addtags) {
|
||||
data = {
|
||||
title: prefs.importer.addtags ? filename : file.replace(/^.*[\\\/]/, '').substring(0, 100),
|
||||
language: prefs.importer.lang || 'eng',
|
||||
tags: foundtags
|
||||
}
|
||||
}
|
||||
else {
|
||||
data = {
|
||||
title: prefs.importer.addtags ? filename : file.replace(/^.*[\\\/]/, '').substring(0, 100),
|
||||
language: prefs.importer.lang || 'eng',
|
||||
tags: prefs.importer.tag === '' ? undefined : prefs.importer.tag
|
||||
}
|
||||
}
|
||||
// Create document
|
||||
request.put({
|
||||
url: prefs.importer.baseUrl + '/api/document',
|
||||
form: qs.stringify(data)
|
||||
}, function (error, response, body) {
|
||||
if (error || !response || response.statusCode !== 200) {
|
||||
spinner.fail('Upload failed for ' + file + ': ' + error);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
spinner.succeed('Upload successful for ' + file);
|
||||
if (remove) {
|
||||
fs.unlinkSync(file);
|
||||
}
|
||||
resolve();
|
||||
|
||||
// Upload file
|
||||
request.put({
|
||||
url: prefs.importer.baseUrl + '/api/file',
|
||||
formData: {
|
||||
id: JSON.parse(body).id,
|
||||
file: fs.createReadStream(file)
|
||||
}
|
||||
}, function (error, response) {
|
||||
if (error || !response || response.statusCode !== 200) {
|
||||
spinner.fail('Upload failed for ' + file + ': ' + error);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
spinner.succeed('Upload successful for ' + file);
|
||||
if (remove) {
|
||||
if (prefs.importer.copyFolder) {
|
||||
fs.copyFileSync(file, prefs.importer.copyFolder + file.replace(/^.*[\\\/]/, ''));
|
||||
fs.unlinkSync(file);
|
||||
}
|
||||
else {fs.unlinkSync(file);}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -312,7 +490,12 @@ if (argv.hasOwnProperty('d')) {
|
||||
'Username: ' + prefs.importer.username + '\n' +
|
||||
'Password: ***********\n' +
|
||||
'Tag: ' + prefs.importer.tag + '\n' +
|
||||
'Daemon mode: ' + prefs.importer.daemon);
|
||||
'Add tags given #: ' + prefs.importer.addtags + '\n' +
|
||||
'Language: ' + prefs.importer.lang + '\n' +
|
||||
'Daemon mode: ' + prefs.importer.daemon + '\n' +
|
||||
'Copy folder: ' + prefs.importer.copyFolder + '\n' +
|
||||
'File filter: ' + prefs.importer.fileFilter
|
||||
);
|
||||
start();
|
||||
} else {
|
||||
askBaseUrl();
|
||||
|
||||
294
docs-importer/package-lock.json
generated
294
docs-importer/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docs-importer",
|
||||
"version": "1.5.1",
|
||||
"name": "teedy-importer",
|
||||
"version": "1.9.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -9,10 +9,10 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
||||
"requires": {
|
||||
"co": "4.6.0",
|
||||
"fast-deep-equal": "1.1.0",
|
||||
"fast-json-stable-stringify": "2.0.0",
|
||||
"json-schema-traverse": "0.3.1"
|
||||
"co": "^4.6.0",
|
||||
"fast-deep-equal": "^1.0.0",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"ansi-escapes": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||
"requires": {
|
||||
"color-convert": "1.9.1"
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
@@ -38,7 +38,7 @@
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"requires": {
|
||||
"sprintf-js": "1.0.3"
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"asn1": {
|
||||
@@ -75,9 +75,8 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
|
||||
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"tweetnacl": "0.14.5"
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"boom": {
|
||||
@@ -85,7 +84,7 @@
|
||||
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
|
||||
"integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
|
||||
"requires": {
|
||||
"hoek": "4.2.1"
|
||||
"hoek": "4.x.x"
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
@@ -93,7 +92,7 @@
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
@@ -107,9 +106,9 @@
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz",
|
||||
"integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==",
|
||||
"requires": {
|
||||
"ansi-styles": "3.2.0",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"supports-color": "5.2.0"
|
||||
"ansi-styles": "^3.2.0",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.2.0"
|
||||
}
|
||||
},
|
||||
"chardet": {
|
||||
@@ -122,7 +121,7 @@
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
|
||||
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
|
||||
"requires": {
|
||||
"restore-cursor": "2.0.0"
|
||||
"restore-cursor": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"cli-spinners": {
|
||||
@@ -150,7 +149,7 @@
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
"color-name": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
@@ -163,7 +162,7 @@
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
|
||||
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
|
||||
"requires": {
|
||||
"delayed-stream": "1.0.0"
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
@@ -181,7 +180,7 @@
|
||||
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
|
||||
"integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
|
||||
"requires": {
|
||||
"boom": "5.2.0"
|
||||
"boom": "5.x.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"boom": {
|
||||
@@ -189,7 +188,7 @@
|
||||
"resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
|
||||
"integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
|
||||
"requires": {
|
||||
"hoek": "4.2.1"
|
||||
"hoek": "4.x.x"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,7 +198,7 @@
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
@@ -207,7 +206,7 @@
|
||||
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
|
||||
"integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
|
||||
"requires": {
|
||||
"clone": "1.0.3"
|
||||
"clone": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
@@ -219,9 +218,8 @@
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsbn": "0.1.1"
|
||||
"jsbn": "~0.1.0"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
@@ -244,9 +242,9 @@
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz",
|
||||
"integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==",
|
||||
"requires": {
|
||||
"chardet": "0.4.2",
|
||||
"iconv-lite": "0.4.19",
|
||||
"tmp": "0.0.33"
|
||||
"chardet": "^0.4.0",
|
||||
"iconv-lite": "^0.4.17",
|
||||
"tmp": "^0.0.33"
|
||||
}
|
||||
},
|
||||
"extsprintf": {
|
||||
@@ -269,7 +267,7 @@
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
|
||||
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
|
||||
"requires": {
|
||||
"escape-string-regexp": "1.0.5"
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"forever-agent": {
|
||||
@@ -282,9 +280,9 @@
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
|
||||
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
|
||||
"requires": {
|
||||
"asynckit": "0.4.0",
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "1.0.6",
|
||||
"mime-types": "2.1.18"
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
@@ -292,7 +290,7 @@
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
@@ -310,8 +308,8 @@
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
|
||||
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
|
||||
"requires": {
|
||||
"ajv": "5.5.2",
|
||||
"har-schema": "2.0.0"
|
||||
"ajv": "^5.1.0",
|
||||
"har-schema": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
@@ -324,10 +322,10 @@
|
||||
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
|
||||
"integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
|
||||
"requires": {
|
||||
"boom": "4.3.1",
|
||||
"cryptiles": "3.1.2",
|
||||
"hoek": "4.2.1",
|
||||
"sntp": "2.1.0"
|
||||
"boom": "4.x.x",
|
||||
"cryptiles": "3.x.x",
|
||||
"hoek": "4.x.x",
|
||||
"sntp": "2.x.x"
|
||||
}
|
||||
},
|
||||
"hoek": {
|
||||
@@ -340,9 +338,9 @@
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"jsprim": "1.4.1",
|
||||
"sshpk": "1.13.1"
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
"sshpk": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
@@ -360,19 +358,19 @@
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.1.0.tgz",
|
||||
"integrity": "sha512-kn7N70US1MSZHZHSGJLiZ7iCwwncc7b0gc68YtlX29OjI3Mp0tSVV+snVXpZ1G+ONS3Ac9zd1m6hve2ibLDYfA==",
|
||||
"requires": {
|
||||
"ansi-escapes": "3.0.0",
|
||||
"chalk": "2.3.1",
|
||||
"cli-cursor": "2.1.0",
|
||||
"cli-width": "2.2.0",
|
||||
"external-editor": "2.1.0",
|
||||
"figures": "2.0.0",
|
||||
"lodash": "4.17.5",
|
||||
"ansi-escapes": "^3.0.0",
|
||||
"chalk": "^2.0.0",
|
||||
"cli-cursor": "^2.1.0",
|
||||
"cli-width": "^2.0.0",
|
||||
"external-editor": "^2.1.0",
|
||||
"figures": "^2.0.0",
|
||||
"lodash": "^4.3.0",
|
||||
"mute-stream": "0.0.7",
|
||||
"run-async": "2.3.0",
|
||||
"rxjs": "5.5.6",
|
||||
"string-width": "2.1.1",
|
||||
"strip-ansi": "4.0.0",
|
||||
"through": "2.3.8"
|
||||
"run-async": "^2.2.0",
|
||||
"rxjs": "^5.5.2",
|
||||
"string-width": "^2.1.0",
|
||||
"strip-ansi": "^4.0.0",
|
||||
"through": "^2.3.6"
|
||||
}
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
@@ -396,19 +394,18 @@
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
|
||||
"integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
|
||||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||
"requires": {
|
||||
"argparse": "1.0.10",
|
||||
"esprima": "4.0.0"
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
|
||||
"optional": true
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
@@ -437,16 +434,16 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.5",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
|
||||
"integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
||||
"integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
|
||||
"requires": {
|
||||
"chalk": "2.3.1"
|
||||
"chalk": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
@@ -459,7 +456,7 @@
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
|
||||
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
|
||||
"requires": {
|
||||
"mime-db": "1.33.0"
|
||||
"mime-db": "~1.33.0"
|
||||
}
|
||||
},
|
||||
"mimic-fn": {
|
||||
@@ -472,27 +469,20 @@
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.11"
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
}
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mute-stream": {
|
||||
@@ -510,7 +500,7 @@
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
|
||||
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
|
||||
"requires": {
|
||||
"mimic-fn": "1.2.0"
|
||||
"mimic-fn": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ora": {
|
||||
@@ -518,12 +508,12 @@
|
||||
"resolved": "https://registry.npmjs.org/ora/-/ora-2.0.0.tgz",
|
||||
"integrity": "sha512-g+IR0nMUXq1k4nE3gkENbN4wkF0XsVZFyxznTF6CdmwQ9qeTGONGpSR9LM5//1l0TVvJoJF3MkMtJp6slUsWFg==",
|
||||
"requires": {
|
||||
"chalk": "2.3.1",
|
||||
"cli-cursor": "2.1.0",
|
||||
"cli-spinners": "1.1.0",
|
||||
"log-symbols": "2.2.0",
|
||||
"strip-ansi": "4.0.0",
|
||||
"wcwidth": "1.0.1"
|
||||
"chalk": "^2.3.1",
|
||||
"cli-cursor": "^2.1.0",
|
||||
"cli-spinners": "^1.1.0",
|
||||
"log-symbols": "^2.2.0",
|
||||
"strip-ansi": "^4.0.0",
|
||||
"wcwidth": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"os-homedir": {
|
||||
@@ -546,11 +536,11 @@
|
||||
"resolved": "https://registry.npmjs.org/preferences/-/preferences-1.0.2.tgz",
|
||||
"integrity": "sha512-cRjA8Galk1HDDBOKjx6DhTwfy5+FVZtH7ogg6rgTLX8Ak4wi55RaS4uRztJuVPd+md1jZo99bH/h1Q9bQQK8bg==",
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"js-yaml": "3.10.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"os-homedir": "1.0.2",
|
||||
"write-file-atomic": "1.3.4"
|
||||
"graceful-fs": "^4.1.2",
|
||||
"js-yaml": "^3.10.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"os-homedir": "^1.0.1",
|
||||
"write-file-atomic": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"punycode": {
|
||||
@@ -559,9 +549,9 @@
|
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
|
||||
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz",
|
||||
"integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ=="
|
||||
},
|
||||
"recursive-readdir": {
|
||||
"version": "2.2.2",
|
||||
@@ -576,28 +566,35 @@
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
|
||||
"integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==",
|
||||
"requires": {
|
||||
"aws-sign2": "0.7.0",
|
||||
"aws4": "1.6.0",
|
||||
"caseless": "0.12.0",
|
||||
"combined-stream": "1.0.6",
|
||||
"extend": "3.0.1",
|
||||
"forever-agent": "0.6.1",
|
||||
"form-data": "2.3.2",
|
||||
"har-validator": "5.0.3",
|
||||
"hawk": "6.0.2",
|
||||
"http-signature": "1.2.0",
|
||||
"is-typedarray": "1.0.0",
|
||||
"isstream": "0.1.2",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"mime-types": "2.1.18",
|
||||
"oauth-sign": "0.8.2",
|
||||
"performance-now": "2.1.0",
|
||||
"qs": "6.5.1",
|
||||
"safe-buffer": "5.1.1",
|
||||
"stringstream": "0.0.5",
|
||||
"tough-cookie": "2.3.4",
|
||||
"tunnel-agent": "0.6.0",
|
||||
"uuid": "3.2.1"
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.6.0",
|
||||
"caseless": "~0.12.0",
|
||||
"combined-stream": "~1.0.5",
|
||||
"extend": "~3.0.1",
|
||||
"forever-agent": "~0.6.1",
|
||||
"form-data": "~2.3.1",
|
||||
"har-validator": "~5.0.3",
|
||||
"hawk": "~6.0.2",
|
||||
"http-signature": "~1.2.0",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.17",
|
||||
"oauth-sign": "~0.8.2",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.1",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"stringstream": "~0.0.5",
|
||||
"tough-cookie": "~2.3.3",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"restore-cursor": {
|
||||
@@ -605,8 +602,8 @@
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
|
||||
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
|
||||
"requires": {
|
||||
"onetime": "2.0.1",
|
||||
"signal-exit": "3.0.2"
|
||||
"onetime": "^2.0.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"run-async": {
|
||||
@@ -614,7 +611,7 @@
|
||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
|
||||
"integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
|
||||
"requires": {
|
||||
"is-promise": "2.1.0"
|
||||
"is-promise": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"rxjs": {
|
||||
@@ -630,6 +627,11 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
||||
@@ -645,7 +647,7 @@
|
||||
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
|
||||
"integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
|
||||
"requires": {
|
||||
"hoek": "4.2.1"
|
||||
"hoek": "4.x.x"
|
||||
}
|
||||
},
|
||||
"sprintf-js": {
|
||||
@@ -654,18 +656,19 @@
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
|
||||
"integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
|
||||
"requires": {
|
||||
"asn1": "0.2.3",
|
||||
"assert-plus": "1.0.0",
|
||||
"bcrypt-pbkdf": "1.0.1",
|
||||
"dashdash": "1.14.1",
|
||||
"ecc-jsbn": "0.1.1",
|
||||
"getpass": "0.1.7",
|
||||
"jsbn": "0.1.1",
|
||||
"tweetnacl": "0.14.5"
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
"bcrypt-pbkdf": "^1.0.0",
|
||||
"dashdash": "^1.12.0",
|
||||
"ecc-jsbn": "~0.1.1",
|
||||
"getpass": "^0.1.1",
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
@@ -673,8 +676,8 @@
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "2.0.0",
|
||||
"strip-ansi": "4.0.0"
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"stringstream": {
|
||||
@@ -687,7 +690,7 @@
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"requires": {
|
||||
"ansi-regex": "3.0.0"
|
||||
"ansi-regex": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
@@ -695,7 +698,7 @@
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz",
|
||||
"integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==",
|
||||
"requires": {
|
||||
"has-flag": "3.0.0"
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"symbol-observable": {
|
||||
@@ -713,7 +716,7 @@
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||
"requires": {
|
||||
"os-tmpdir": "1.0.2"
|
||||
"os-tmpdir": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
@@ -721,7 +724,7 @@
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
|
||||
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
|
||||
"requires": {
|
||||
"punycode": "1.4.1"
|
||||
"punycode": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
@@ -729,14 +732,13 @@
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"optional": true
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.8.3",
|
||||
@@ -753,9 +755,9 @@
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "1.3.0"
|
||||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"wcwidth": {
|
||||
@@ -763,7 +765,7 @@
|
||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||
"integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
|
||||
"requires": {
|
||||
"defaults": "1.0.3"
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"write-file-atomic": {
|
||||
@@ -771,9 +773,9 @@
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz",
|
||||
"integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=",
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"imurmurhash": "0.1.4",
|
||||
"slide": "1.1.6"
|
||||
"graceful-fs": "^4.1.11",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"slide": "^1.1.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "teedy-importer",
|
||||
"version": "1.5.1",
|
||||
"version": "1.9.0",
|
||||
"description": "Import files to Teedy",
|
||||
"bin": "main.js",
|
||||
"scripts": {
|
||||
@@ -11,6 +11,9 @@
|
||||
"url": "git+https://github.com/sismics/docs.git"
|
||||
},
|
||||
"author": "Benjamin Gamard",
|
||||
"contributors": [
|
||||
"Cornelius Hoffmann <coding@hoffmn.de>"
|
||||
],
|
||||
"license": "GPL-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/sismics/docs/issues"
|
||||
@@ -18,10 +21,12 @@
|
||||
"homepage": "https://github.com/sismics/docs#readme",
|
||||
"dependencies": {
|
||||
"inquirer": "^5.1.0",
|
||||
"minimist": "^1.2.0",
|
||||
"minimist": "^1.2.5",
|
||||
"ora": "^2.0.0",
|
||||
"preferences": "^1.0.2",
|
||||
"qs": "^6.9.4",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"minimatch": "^3.0.4",
|
||||
"request": "^2.83.0",
|
||||
"underscore": "^1.8.3"
|
||||
}
|
||||
|
||||
11
docs-importer/pref
Normal file
11
docs-importer/pref
Normal file
@@ -0,0 +1,11 @@
|
||||
importer:
|
||||
daemon: true
|
||||
path: import
|
||||
tag: 'env1'
|
||||
addtags: 'env2'
|
||||
lang: 'env3'
|
||||
baseUrl: 'env4'
|
||||
username: 'env5'
|
||||
password: 'env6'
|
||||
copyFolder: 'env7'
|
||||
fileFilter: 'env8'
|
||||
@@ -1,81 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-parent</artifactId>
|
||||
<version>1.7</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>docs-stress</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Docs Stress</name>
|
||||
|
||||
<dependencies>
|
||||
<!-- Dependencies to Jersey -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.media</groupId>
|
||||
<artifactId>jersey-media-multipart</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Depenedencies to Docs -->
|
||||
<dependency>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-web-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-web-common</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
|
||||
<!-- Other external dependencies -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,135 +0,0 @@
|
||||
package com.sismics.docs.stress;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.Resources;
|
||||
import com.sismics.docs.rest.util.ClientUtil;
|
||||
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||
import org.glassfish.jersey.client.ClientResponse;
|
||||
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
|
||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
|
||||
import org.junit.Assert;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.json.JsonObject;
|
||||
import javax.ws.rs.client.*;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* Stress app for Teedy.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class Main {
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger log = LoggerFactory.getLogger(Main.class);
|
||||
|
||||
private static final String API_URL = "http://localhost:9999/docs-web/api/";
|
||||
private static final int USER_COUNT = 50;
|
||||
private static final int DOCUMENT_PER_USER_COUNT = 2000;
|
||||
private static final int TAG_PER_USER_COUNT = 20;
|
||||
private static final int FILE_PER_DOCUMENT_COUNT = 10;
|
||||
|
||||
private static Client client = ClientBuilder.newClient();
|
||||
|
||||
private static Set<User> userSet = Sets.newHashSet();
|
||||
|
||||
/**
|
||||
* Entry point.
|
||||
*
|
||||
* @param args Args
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
log.info("Starting stress test...");
|
||||
|
||||
WebTarget resource = client.target(API_URL);
|
||||
ClientUtil clientUtil = new ClientUtil(resource);
|
||||
|
||||
// Create users
|
||||
for (int i = 0; i < USER_COUNT; i++) {
|
||||
String username = generateString();
|
||||
clientUtil.createUser(username);
|
||||
userSet.add(new User(username, (clientUtil.login(username))));
|
||||
log.info("Created user " + (i + 1) + "/" + USER_COUNT);
|
||||
}
|
||||
|
||||
// Create tags for each user
|
||||
int tagCreatedCount = 1;
|
||||
for (User user : userSet) {
|
||||
Invocation.Builder tagResource = resource.path("/tag").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, user.authToken);
|
||||
|
||||
for (int j = 0; j < TAG_PER_USER_COUNT; j++) {
|
||||
Form form = new Form();
|
||||
String name = generateString();
|
||||
form.param("name", name);
|
||||
form.param("color", "#ff0000");
|
||||
JsonObject json = tagResource.put(Entity.form(form), JsonObject.class);
|
||||
user.tagList.add(json.getString("id"));
|
||||
log.info("Created tag " + (tagCreatedCount++) + "/" + TAG_PER_USER_COUNT * USER_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
// Create documents for each user
|
||||
int documentCreatedCount = 1;
|
||||
for (User user : userSet) {
|
||||
for (int i = 0; i < DOCUMENT_PER_USER_COUNT; i++) {
|
||||
long createDate = new Date().getTime();
|
||||
Form form = new Form()
|
||||
.param("title", generateString())
|
||||
.param("description", generateString())
|
||||
.param("tags", user.tagList.get(ThreadLocalRandom.current().nextInt(user.tagList.size()))) // Random tag
|
||||
.param("language", "eng")
|
||||
.param("create_date", Long.toString(createDate));
|
||||
JsonObject json = resource.path("/document").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, user.authToken)
|
||||
.put(Entity.form(form), JsonObject.class);
|
||||
String documentId = json.getString("id");
|
||||
log.info("Created document " + (documentCreatedCount++) + "/" + DOCUMENT_PER_USER_COUNT * USER_COUNT + " for user: " + user.username);
|
||||
|
||||
// Add files for each document
|
||||
for (int j = 0; j < FILE_PER_DOCUMENT_COUNT; j++) {
|
||||
try (InputStream is = Resources.getResource("empty.png").openStream()) {
|
||||
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "empty.png");
|
||||
@SuppressWarnings("resource")
|
||||
ClientResponse response = resource
|
||||
.register(MultiPartFeature.class)
|
||||
.path("/file").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, user.authToken)
|
||||
.put(Entity.entity(new FormDataMultiPart().field("id", documentId).bodyPart(streamDataBodyPart),
|
||||
MediaType.MULTIPART_FORM_DATA_TYPE), ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateString() {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
private static class User {
|
||||
String username;
|
||||
List<String> tagList = Lists.newArrayList();
|
||||
String authToken;
|
||||
|
||||
User(String username, String authToken) {
|
||||
this.username = username;
|
||||
this.authToken = authToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 921 B |
@@ -1,6 +0,0 @@
|
||||
log4j.rootCategory=WARN, CONSOLE
|
||||
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.CONSOLE.layout.ConversionPattern=%d{DATE} %p %l %m %n
|
||||
|
||||
log4j.logger.com.sismics=DEBUG
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-parent</artifactId>
|
||||
<version>1.7</version>
|
||||
<version>1.10</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -68,6 +68,11 @@
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
|
||||
@@ -111,7 +111,18 @@ public class ValidationUtil {
|
||||
public static void validateHexColor(String s, String name, boolean nullable) throws ClientException {
|
||||
ValidationUtil.validateLength(s, name, 7, 7, nullable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate a tag name.
|
||||
*
|
||||
* @param name Name of the tag
|
||||
*/
|
||||
public static void validateTagName(String name) throws ClientException {
|
||||
if (name.contains(" ") || name.contains(":")) {
|
||||
throw new ClientException("IllegalTagName", "Spaces and colons are not allowed in tag name");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the provided string matches an URL with HTTP or HTTPS scheme.
|
||||
*
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.apache.log4j.PatternLayout;
|
||||
import org.apache.log4j.RollingFileAppender;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityTransaction;
|
||||
@@ -57,6 +58,8 @@ public class RequestContextFilter implements Filter {
|
||||
fileAppender.setMaxBackupIndex(5);
|
||||
fileAppender.activateOptions();
|
||||
org.apache.log4j.Logger.getRootLogger().addAppender(fileAppender);
|
||||
SLF4JBridgeHandler.removeHandlersForRootLogger();
|
||||
SLF4JBridgeHandler.install();
|
||||
|
||||
// Initialize the application context
|
||||
TransactionUtil.handle(AppContext::getInstance);
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
|
||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.json.JsonObject;
|
||||
import javax.ws.rs.client.Entity;
|
||||
@@ -113,7 +114,8 @@ public class ClientUtil {
|
||||
.param("username", username)
|
||||
.param("password", password)
|
||||
.param("remember", remember.toString())));
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
return getAuthenticationCookie(response);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-parent</artifactId>
|
||||
<version>1.7</version>
|
||||
<version>1.10</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -26,25 +26,6 @@
|
||||
<artifactId>docs-web-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JDK 11 JAXB dependencies -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-core</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Dependencies to Jersey -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.containers</groupId>
|
||||
@@ -157,7 +138,7 @@
|
||||
<artifactId>greenmail</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -228,54 +209,6 @@
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- Stress profile -->
|
||||
<profile>
|
||||
<id>stress</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>env</name>
|
||||
<value>stress</value>
|
||||
</property>
|
||||
</activation>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/stress/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
<excludes>
|
||||
<exclude>**/config.properties</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/stress/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>**/config.properties</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<systemProperty>
|
||||
<name>application.mode</name>
|
||||
<value>dev</value>
|
||||
</systemProperty>
|
||||
</systemProperties>
|
||||
<webApp>
|
||||
<contextPath>/docs-web</contextPath>
|
||||
</webApp>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- Production profile -->
|
||||
<profile>
|
||||
<id>prod</id>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=24
|
||||
db.version=27
|
||||
@@ -6,3 +6,6 @@ log4j.appender.MEMORY=com.sismics.util.log4j.MemoryAppender
|
||||
log4j.appender.MEMORY.size=1000
|
||||
|
||||
log4j.logger.com.sismics=DEBUG
|
||||
log4j.logger.org.apache.pdfbox=ERROR
|
||||
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
|
||||
log4j.logger.org.apache.directory=ERROR
|
||||
@@ -14,6 +14,7 @@ import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.service.InboxService;
|
||||
import com.sismics.docs.core.util.ConfigUtil;
|
||||
import com.sismics.docs.core.util.DirectoryUtil;
|
||||
import com.sismics.docs.core.util.authentication.LdapAuthenticationHandler;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
@@ -311,6 +312,7 @@ public class AppResource extends BaseResource {
|
||||
* @apiSuccess {String} port IMAP port
|
||||
* @apiSuccess {String} username IMAP username
|
||||
* @apiSuccess {String} password IMAP password
|
||||
* @apiSuccess {String} folder IMAP folder
|
||||
* @apiSuccess {String} tag Tag for created documents
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiPermission admin
|
||||
@@ -328,14 +330,19 @@ public class AppResource extends BaseResource {
|
||||
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
Boolean enabled = ConfigUtil.getConfigBooleanValue(ConfigType.INBOX_ENABLED);
|
||||
Boolean autoTags = ConfigUtil.getConfigBooleanValue(ConfigType.INBOX_AUTOMATIC_TAGS);
|
||||
Boolean deleteImported = ConfigUtil.getConfigBooleanValue(ConfigType.INBOX_DELETE_IMPORTED);
|
||||
Config hostnameConfig = configDao.getById(ConfigType.INBOX_HOSTNAME);
|
||||
Config portConfig = configDao.getById(ConfigType.INBOX_PORT);
|
||||
Config usernameConfig = configDao.getById(ConfigType.INBOX_USERNAME);
|
||||
Config passwordConfig = configDao.getById(ConfigType.INBOX_PASSWORD);
|
||||
Config folderConfig = configDao.getById(ConfigType.INBOX_FOLDER);
|
||||
Config tagConfig = configDao.getById(ConfigType.INBOX_TAG);
|
||||
JsonObjectBuilder response = Json.createObjectBuilder();
|
||||
|
||||
response.add("enabled", enabled);
|
||||
response.add("autoTagsEnabled", autoTags);
|
||||
response.add("deleteImported", deleteImported);
|
||||
if (hostnameConfig == null) {
|
||||
response.addNull("hostname");
|
||||
} else {
|
||||
@@ -356,6 +363,11 @@ public class AppResource extends BaseResource {
|
||||
} else {
|
||||
response.add("password", passwordConfig.getValue());
|
||||
}
|
||||
if (folderConfig == null) {
|
||||
response.addNull("folder");
|
||||
} else {
|
||||
response.add("folder", folderConfig.getValue());
|
||||
}
|
||||
if (tagConfig == null) {
|
||||
response.addNull("tag");
|
||||
} else {
|
||||
@@ -388,6 +400,7 @@ public class AppResource extends BaseResource {
|
||||
* @apiParam {Integer} port IMAP port
|
||||
* @apiParam {String} username IMAP username
|
||||
* @apiParam {String} password IMAP password
|
||||
* @apiParam {String} folder IMAP folder
|
||||
* @apiParam {String} tag Tag for created documents
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiError (client) ValidationError Validation error
|
||||
@@ -399,16 +412,20 @@ public class AppResource extends BaseResource {
|
||||
* @param portStr IMAP port
|
||||
* @param username IMAP username
|
||||
* @param password IMAP password
|
||||
* @param folder IMAP folder
|
||||
* @param tag Tag for created documents
|
||||
* @return Response
|
||||
*/
|
||||
@POST
|
||||
@Path("config_inbox")
|
||||
public Response configInbox(@FormParam("enabled") Boolean enabled,
|
||||
@FormParam("autoTagsEnabled") Boolean autoTagsEnabled,
|
||||
@FormParam("deleteImported") Boolean deleteImported,
|
||||
@FormParam("hostname") String hostname,
|
||||
@FormParam("port") String portStr,
|
||||
@FormParam("username") String username,
|
||||
@FormParam("password") String password,
|
||||
@FormParam("folder") String folder,
|
||||
@FormParam("tag") String tag) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
@@ -422,6 +439,8 @@ public class AppResource extends BaseResource {
|
||||
// Just update the changed configuration
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
configDao.update(ConfigType.INBOX_ENABLED, enabled.toString());
|
||||
configDao.update(ConfigType.INBOX_AUTOMATIC_TAGS, autoTagsEnabled.toString());
|
||||
configDao.update(ConfigType.INBOX_DELETE_IMPORTED, deleteImported.toString());
|
||||
if (!Strings.isNullOrEmpty(hostname)) {
|
||||
configDao.update(ConfigType.INBOX_HOSTNAME, hostname);
|
||||
}
|
||||
@@ -434,6 +453,9 @@ public class AppResource extends BaseResource {
|
||||
if (!Strings.isNullOrEmpty(password)) {
|
||||
configDao.update(ConfigType.INBOX_PASSWORD, password);
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(folder)) {
|
||||
configDao.update(ConfigType.INBOX_FOLDER, folder);
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(tag)) {
|
||||
configDao.update(ConfigType.INBOX_TAG, tag);
|
||||
}
|
||||
@@ -688,4 +710,138 @@ public class AppResource extends BaseResource {
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LDAP authentication configuration.
|
||||
*
|
||||
* @api {get} /app/config_ldap Get the LDAP authentication configuration
|
||||
* @apiName GetAppConfigLdap
|
||||
* @apiGroup App
|
||||
* @apiSuccess {Boolean} enabled LDAP authentication enabled
|
||||
* @apiSuccess {String} host LDAP server host
|
||||
* @apiSuccess {Integer} port LDAP server port
|
||||
* @apiSuccess {String} admin_dn Admin DN
|
||||
* @apiSuccess {String} admin_password Admin password
|
||||
* @apiSuccess {String} base_dn Base DN
|
||||
* @apiSuccess {String} filter LDAP filter
|
||||
* @apiSuccess {String} default_email LDAP default email
|
||||
* @apiSuccess {Integer} default_storage LDAP default storage
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiPermission admin
|
||||
* @apiVersion 1.9.0
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
@GET
|
||||
@Path("config_ldap")
|
||||
public Response getConfigLdap() {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
Config enabled = configDao.getById(ConfigType.LDAP_ENABLED);
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder();
|
||||
if (enabled != null && Boolean.parseBoolean(enabled.getValue())) {
|
||||
// LDAP enabled
|
||||
response.add("enabled", true)
|
||||
.add("host", ConfigUtil.getConfigStringValue(ConfigType.LDAP_HOST))
|
||||
.add("port", ConfigUtil.getConfigIntegerValue(ConfigType.LDAP_PORT))
|
||||
.add("admin_dn", ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_DN))
|
||||
.add("admin_password", ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_PASSWORD))
|
||||
.add("base_dn", ConfigUtil.getConfigStringValue(ConfigType.LDAP_BASE_DN))
|
||||
.add("filter", ConfigUtil.getConfigStringValue(ConfigType.LDAP_FILTER))
|
||||
.add("default_email", ConfigUtil.getConfigStringValue(ConfigType.LDAP_DEFAULT_EMAIL))
|
||||
.add("default_storage", ConfigUtil.getConfigLongValue(ConfigType.LDAP_DEFAULT_STORAGE));
|
||||
} else {
|
||||
// LDAP disabled
|
||||
response.add("enabled", false);
|
||||
}
|
||||
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the LDAP authentication.
|
||||
*
|
||||
* @api {post} /app/config_ldap Configure the LDAP authentication
|
||||
* @apiName PostAppConfigLdap
|
||||
* @apiGroup App
|
||||
* @apiParam {Boolean} enabled LDAP authentication enabled
|
||||
* @apiParam {String} host LDAP server host
|
||||
* @apiParam {Integer} port LDAP server port
|
||||
* @apiParam {String} admin_dn Admin DN
|
||||
* @apiParam {String} admin_password Admin password
|
||||
* @apiParam {String} base_dn Base DN
|
||||
* @apiParam {String} filter LDAP filter
|
||||
* @apiParam {String} default_email LDAP default email
|
||||
* @apiParam {Integer} default_storage LDAP default storage
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiError (client) ValidationError Validation error
|
||||
* @apiPermission admin
|
||||
* @apiVersion 1.9.0
|
||||
*
|
||||
* @param enabled LDAP authentication enabled
|
||||
* @param host LDAP server host
|
||||
* @param portStr LDAP server port
|
||||
* @param adminDn Admin DN
|
||||
* @param adminPassword Admin password
|
||||
* @param baseDn Base DN
|
||||
* @param filter LDAP filter
|
||||
* @param defaultEmail LDAP default email
|
||||
* @param defaultStorageStr LDAP default storage
|
||||
* @return Response
|
||||
*/
|
||||
@POST
|
||||
@Path("config_ldap")
|
||||
public Response configLdap(@FormParam("enabled") Boolean enabled,
|
||||
@FormParam("host") String host,
|
||||
@FormParam("port") String portStr,
|
||||
@FormParam("admin_dn") String adminDn,
|
||||
@FormParam("admin_password") String adminPassword,
|
||||
@FormParam("base_dn") String baseDn,
|
||||
@FormParam("filter") String filter,
|
||||
@FormParam("default_email") String defaultEmail,
|
||||
@FormParam("default_storage") String defaultStorageStr) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
|
||||
if (enabled != null && enabled) {
|
||||
// LDAP enabled, validate everything
|
||||
ValidationUtil.validateLength(host, "host", 1, 250);
|
||||
ValidationUtil.validateInteger(portStr, "port");
|
||||
ValidationUtil.validateLength(adminDn, "admin_dn", 1, 250);
|
||||
ValidationUtil.validateLength(adminPassword, "admin_password", 1, 250);
|
||||
ValidationUtil.validateLength(baseDn, "base_dn", 1, 250);
|
||||
ValidationUtil.validateLength(filter, "filter", 1, 250);
|
||||
if (!filter.contains("USERNAME")) {
|
||||
throw new ClientException("ValidationError", "'filter' must contains 'USERNAME'");
|
||||
}
|
||||
ValidationUtil.validateLength(defaultEmail, "default_email", 1, 250);
|
||||
ValidationUtil.validateLong(defaultStorageStr, "default_storage");
|
||||
configDao.update(ConfigType.LDAP_ENABLED, Boolean.TRUE.toString());
|
||||
configDao.update(ConfigType.LDAP_HOST, host);
|
||||
configDao.update(ConfigType.LDAP_PORT, portStr);
|
||||
configDao.update(ConfigType.LDAP_ADMIN_DN, adminDn);
|
||||
configDao.update(ConfigType.LDAP_ADMIN_PASSWORD, adminPassword);
|
||||
configDao.update(ConfigType.LDAP_BASE_DN, baseDn);
|
||||
configDao.update(ConfigType.LDAP_FILTER, filter);
|
||||
configDao.update(ConfigType.LDAP_DEFAULT_EMAIL, defaultEmail);
|
||||
configDao.update(ConfigType.LDAP_DEFAULT_STORAGE, defaultStorageStr);
|
||||
} else {
|
||||
// LDAP disabled
|
||||
configDao.update(ConfigType.LDAP_ENABLED, Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
// Reset the LDAP pool to reconnect with the new configuration
|
||||
LdapAuthenticationHandler.reset();
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import com.sismics.security.UserPrincipal;
|
||||
import com.sismics.util.filter.SecurityFilter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -19,6 +21,7 @@ import java.util.Set;
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public abstract class BaseResource {
|
||||
/**
|
||||
* @apiDefine admin Admin
|
||||
|
||||
@@ -104,6 +104,7 @@ public class DocumentResource extends BaseResource {
|
||||
* @apiSuccess {String="READ","WRITE"} inherited_acls.perm Permission
|
||||
* @apiSuccess {String} inherited_acls.source_id Source ID
|
||||
* @apiSuccess {String} inherited_acls.source_name Source name
|
||||
* @apiSuccess {String} inherited_acls.source_color The color of the Source
|
||||
* @apiSuccess {String} inherited_acls.id ID
|
||||
* @apiSuccess {String} inherited_acls.name Target name
|
||||
* @apiSuccess {String="USER","GROUP","SHARE"} inherited_acls.type Target type
|
||||
@@ -196,6 +197,7 @@ public class DocumentResource extends BaseResource {
|
||||
.add("perm", aclDto.getPerm().name())
|
||||
.add("source_id", tagDto.getId())
|
||||
.add("source_name", tagDto.getName())
|
||||
.add("source_color", tagDto.getColor())
|
||||
.add("id", aclDto.getTargetId())
|
||||
.add("name", JsonUtil.nullable(aclDto.getTargetName()))
|
||||
.add("type", aclDto.getTargetType()));
|
||||
@@ -455,8 +457,8 @@ public class DocumentResource extends BaseResource {
|
||||
for (String criteria : criteriaList) {
|
||||
String[] params = criteria.split(":");
|
||||
if (params.length != 2 || Strings.isNullOrEmpty(params[0]) || Strings.isNullOrEmpty(params[1])) {
|
||||
// This is not a special criteria
|
||||
query.add(criteria);
|
||||
// This is not a special criteria, do a fulltext search on it
|
||||
fullQuery.add(criteria);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -569,6 +571,10 @@ public class DocumentResource extends BaseResource {
|
||||
documentCriteria.setLanguage(UUID.randomUUID().toString());
|
||||
}
|
||||
break;
|
||||
case "mime":
|
||||
// New mime type criteria
|
||||
documentCriteria.setMimeType(params[1]);
|
||||
break;
|
||||
case "by":
|
||||
// New creator criteria
|
||||
User user = userDao.getActiveByUsername(params[1]);
|
||||
@@ -584,12 +590,20 @@ public class DocumentResource extends BaseResource {
|
||||
// New shared state criteria
|
||||
documentCriteria.setActiveRoute(params[1].equals("me"));
|
||||
break;
|
||||
case "simple":
|
||||
// New simple search criteria
|
||||
query.add(params[1]);
|
||||
break;
|
||||
case "full":
|
||||
// New full content search criteria
|
||||
// New fulltext search criteria
|
||||
fullQuery.add(params[1]);
|
||||
break;
|
||||
case "title":
|
||||
// New title criteria
|
||||
documentCriteria.setTitle(params[1]);
|
||||
break;
|
||||
default:
|
||||
query.add(criteria);
|
||||
fullQuery.add(criteria);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -723,7 +737,7 @@ public class DocumentResource extends BaseResource {
|
||||
// Raise a document created event
|
||||
DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent();
|
||||
documentCreatedAsyncEvent.setUserId(principal.getId());
|
||||
documentCreatedAsyncEvent.setDocument(document);
|
||||
documentCreatedAsyncEvent.setDocumentId(document.getId());
|
||||
ThreadLocalContext.get().addAsyncEvent(documentCreatedAsyncEvent);
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
@@ -940,7 +954,7 @@ public class DocumentResource extends BaseResource {
|
||||
// Raise a document created event
|
||||
DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent();
|
||||
documentCreatedAsyncEvent.setUserId(principal.getId());
|
||||
documentCreatedAsyncEvent.setDocument(document);
|
||||
documentCreatedAsyncEvent.setDocumentId(document.getId());
|
||||
ThreadLocalContext.get().addAsyncEvent(documentCreatedAsyncEvent);
|
||||
|
||||
// Add files to the document
|
||||
@@ -1009,7 +1023,7 @@ public class DocumentResource extends BaseResource {
|
||||
// Raise file deleted event
|
||||
FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent();
|
||||
fileDeletedAsyncEvent.setUserId(principal.getId());
|
||||
fileDeletedAsyncEvent.setFile(file);
|
||||
fileDeletedAsyncEvent.setFileId(file.getId());
|
||||
ThreadLocalContext.get().addAsyncEvent(fileDeletedAsyncEvent);
|
||||
}
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ public class FileResource extends BaseResource {
|
||||
FileUpdatedAsyncEvent fileUpdatedAsyncEvent = new FileUpdatedAsyncEvent();
|
||||
fileUpdatedAsyncEvent.setUserId(principal.getId());
|
||||
fileUpdatedAsyncEvent.setLanguage(documentDto.getLanguage());
|
||||
fileUpdatedAsyncEvent.setFile(file);
|
||||
fileUpdatedAsyncEvent.setFileId(file.getId());
|
||||
fileUpdatedAsyncEvent.setUnencryptedFile(unencryptedFile);
|
||||
ThreadLocalContext.get().addAsyncEvent(fileUpdatedAsyncEvent);
|
||||
|
||||
@@ -310,7 +310,7 @@ public class FileResource extends BaseResource {
|
||||
FileUpdatedAsyncEvent event = new FileUpdatedAsyncEvent();
|
||||
event.setUserId(principal.getId());
|
||||
event.setLanguage(documentDto.getLanguage());
|
||||
event.setFile(file);
|
||||
event.setFileId(file.getId());
|
||||
event.setUnencryptedFile(unencryptedFile);
|
||||
ThreadLocalContext.get().addAsyncEvent(event);
|
||||
} catch (Exception e) {
|
||||
@@ -548,7 +548,7 @@ public class FileResource extends BaseResource {
|
||||
// Raise a new file deleted event
|
||||
FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent();
|
||||
fileDeletedAsyncEvent.setUserId(principal.getId());
|
||||
fileDeletedAsyncEvent.setFile(file);
|
||||
fileDeletedAsyncEvent.setFileId(file.getId());
|
||||
ThreadLocalContext.get().addAsyncEvent(fileDeletedAsyncEvent);
|
||||
|
||||
if (file.getDocumentId() != null) {
|
||||
@@ -608,7 +608,7 @@ public class FileResource extends BaseResource {
|
||||
if (size != null) {
|
||||
if (size.equals("content")) {
|
||||
return Response.ok(Strings.nullToEmpty(file.getContent()))
|
||||
.header(HttpHeaders.CONTENT_TYPE, "text/plain")
|
||||
.header(HttpHeaders.CONTENT_TYPE, "text/plain; charset=utf-8")
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,9 @@ public class RouteResource extends BaseResource {
|
||||
routeStepDao.endRouteStep(routeStepDto.getId(), routeStepTransition, comment, principal.getId());
|
||||
RouteStepDto newRouteStep = routeStepDao.getCurrentStep(documentId);
|
||||
RoutingUtil.updateAcl(documentId, newRouteStep, routeStepDto, principal.getId());
|
||||
RoutingUtil.sendRouteStepEmail(documentId, routeStepDto);
|
||||
if (newRouteStep != null) {
|
||||
RoutingUtil.sendRouteStepEmail(documentId, newRouteStep);
|
||||
}
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("readable", aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(null)));
|
||||
|
||||
@@ -55,7 +55,7 @@ public class ShareResource extends BaseResource {
|
||||
public Response add(
|
||||
@FormParam("id") String documentId,
|
||||
@FormParam("name") String name) {
|
||||
if (!authenticate()) {
|
||||
if (!authenticate() || principal.isGuest()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ public class ShareResource extends BaseResource {
|
||||
@Path("{id: [a-z0-9\\-]+}")
|
||||
public Response delete(
|
||||
@PathParam("id") String id) {
|
||||
if (!authenticate()) {
|
||||
if (!authenticate() || principal.isGuest()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ public class TagResource extends BaseResource {
|
||||
* @apiSuccess {String} id Tag ID
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiError (client) ValidationError Validation error
|
||||
* @apiError (client) SpacesNotAllowed Spaces are not allowed in tag name
|
||||
* @apiError (client) IllegalTagName Spaces and colons are not allowed in tag name
|
||||
* @apiError (client) ParentNotFound Parent not found
|
||||
* @apiPermission user
|
||||
* @apiVersion 1.5.0
|
||||
@@ -177,12 +177,8 @@ public class TagResource extends BaseResource {
|
||||
// Validate input data
|
||||
name = ValidationUtil.validateLength(name, "name", 1, 36, false);
|
||||
ValidationUtil.validateHexColor(color, "color", true);
|
||||
|
||||
// Don't allow spaces
|
||||
if (name.contains(" ")) {
|
||||
throw new ClientException("SpacesNotAllowed", "Spaces are not allowed in tag name");
|
||||
}
|
||||
|
||||
ValidationUtil.validateTagName(name);
|
||||
|
||||
// Check the parent
|
||||
if (StringUtils.isEmpty(parentId)) {
|
||||
parentId = null;
|
||||
@@ -237,7 +233,7 @@ public class TagResource extends BaseResource {
|
||||
* @apiSuccess {String} id Tag ID
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiError (client) ValidationError Validation error
|
||||
* @apiError (client) SpacesNotAllowed Spaces are not allowed in tag name
|
||||
* @apiError (client) IllegalTagName Spaces and colons are not allowed in tag name
|
||||
* @apiError (client) ParentNotFound Parent not found
|
||||
* @apiError (client) CircularReference Circular reference in parent tag
|
||||
* @apiError (client) NotFound Tag not found
|
||||
@@ -263,12 +259,8 @@ public class TagResource extends BaseResource {
|
||||
// Validate input data
|
||||
name = ValidationUtil.validateLength(name, "name", 1, 36, true);
|
||||
ValidationUtil.validateHexColor(color, "color", true);
|
||||
|
||||
// Don't allow spaces
|
||||
if (name.contains(" ")) {
|
||||
throw new ClientException("SpacesNotAllowed", "Spaces are not allowed in tag name");
|
||||
}
|
||||
|
||||
ValidationUtil.validateTagName(name);
|
||||
|
||||
// Check permission
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(id, PermType.WRITE, getTargetIdList(null))) {
|
||||
|
||||
@@ -366,7 +366,7 @@ public class UserResource extends BaseResource {
|
||||
AuthenticationToken authenticationToken = new AuthenticationToken()
|
||||
.setUserId(user.getId())
|
||||
.setLongLasted(longLasted)
|
||||
.setIp(ip)
|
||||
.setIp(StringUtils.abbreviate(ip, 45))
|
||||
.setUserAgent(StringUtils.abbreviate(request.getHeader("user-agent"), 1000));
|
||||
String token = authenticationTokenDao.create(authenticationToken);
|
||||
|
||||
@@ -482,7 +482,7 @@ public class UserResource extends BaseResource {
|
||||
for (File file : fileList) {
|
||||
FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent();
|
||||
fileDeletedAsyncEvent.setUserId(principal.getId());
|
||||
fileDeletedAsyncEvent.setFile(file);
|
||||
fileDeletedAsyncEvent.setFileId(file.getId());
|
||||
ThreadLocalContext.get().addAsyncEvent(fileDeletedAsyncEvent);
|
||||
}
|
||||
|
||||
@@ -564,7 +564,7 @@ public class UserResource extends BaseResource {
|
||||
for (File file : fileList) {
|
||||
FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent();
|
||||
fileDeletedAsyncEvent.setUserId(principal.getId());
|
||||
fileDeletedAsyncEvent.setFile(file);
|
||||
fileDeletedAsyncEvent.setFileId(file.getId());
|
||||
ThreadLocalContext.get().addAsyncEvent(fileDeletedAsyncEvent);
|
||||
}
|
||||
|
||||
|
||||
735
docs-web/src/main/webapp/package-lock.json
generated
735
docs-web/src/main/webapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -244,6 +244,15 @@ angular.module('docs',
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('settings.ldap', {
|
||||
url: '/ldap',
|
||||
views: {
|
||||
'settings': {
|
||||
templateUrl: 'partial/docs/settings.ldap.html',
|
||||
controller: 'SettingsLdap'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('document', {
|
||||
url: '/document',
|
||||
abstract: true,
|
||||
@@ -347,7 +356,7 @@ angular.module('docs',
|
||||
}
|
||||
})
|
||||
.state('login', {
|
||||
url: '/login',
|
||||
url: '/login?redirectState&redirectParams',
|
||||
views: {
|
||||
'page': {
|
||||
templateUrl: 'partial/docs/login.html',
|
||||
@@ -415,17 +424,20 @@ angular.module('docs',
|
||||
|
||||
// Configuring Angular Translate
|
||||
$translateProvider
|
||||
.useSanitizeValueStrategy(null)
|
||||
.useSanitizeValueStrategy('escapeParameters')
|
||||
.useStaticFilesLoader({
|
||||
prefix: 'locale/',
|
||||
suffix: '.json?@build.date@'
|
||||
})
|
||||
.registerAvailableLanguageKeys(['en', 'es', 'fr', 'de', 'ru', 'zh_CN', 'zh_TW'], {
|
||||
'ru_*': 'ru',
|
||||
.registerAvailableLanguageKeys(['en', 'es', 'fr', 'de', 'el', 'ru', 'it', 'pl', 'zh_CN', 'zh_TW'], {
|
||||
'en_*': 'en',
|
||||
'es_*': 'es',
|
||||
'fr_*': 'fr',
|
||||
'de_*': 'de',
|
||||
'el_*': 'el',
|
||||
'ru_*': 'ru',
|
||||
'it_*': 'it',
|
||||
'pl_*': 'pl',
|
||||
'*': 'en'
|
||||
})
|
||||
.fallbackLanguage('en');
|
||||
@@ -436,6 +448,9 @@ angular.module('docs',
|
||||
} else {
|
||||
// Or else determine the language based on the user's browser
|
||||
$translateProvider.determinePreferredLanguage();
|
||||
if (!$translateProvider.use()) {
|
||||
$translateProvider.use('en');
|
||||
}
|
||||
}
|
||||
|
||||
// Configuring Timago
|
||||
@@ -447,6 +462,9 @@ angular.module('docs',
|
||||
// Configuring $http to act like jQuery.ajax
|
||||
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
|
||||
$httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
|
||||
$httpProvider.defaults.headers.delete = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
|
||||
};
|
||||
$httpProvider.defaults.transformRequest = [function(data) {
|
||||
var param = function(obj) {
|
||||
var query = '';
|
||||
@@ -520,7 +538,14 @@ angular.module('docs',
|
||||
{ key: 'kor', label: '한국어' },
|
||||
{ key: 'nld', label: 'Nederlands' },
|
||||
{ key: 'tur', label: 'Türkçe' },
|
||||
{ key: 'heb', label: 'עברית' }
|
||||
{ key: 'heb', label: 'עברית' },
|
||||
{ key: 'hun', label: 'Magyar' },
|
||||
{ key: 'fin', label: 'Suomi' },
|
||||
{ key: 'swe', label: 'Svenska' },
|
||||
{ key: 'lav', label: 'Latviešu' },
|
||||
{ key: 'dan', label: 'Dansk' },
|
||||
{ key: 'nor', label: 'Norsk' },
|
||||
{ key: 'vie', label: 'Tiếng Việt' }
|
||||
];
|
||||
})
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Login controller.
|
||||
*/
|
||||
angular.module('docs').controller('Login', function(Restangular, $scope, $rootScope, $state, $dialog, User, $translate, $uibModal) {
|
||||
angular.module('docs').controller('Login', function(Restangular, $scope, $rootScope, $state, $stateParams, $dialog, User, $translate, $uibModal) {
|
||||
$scope.codeRequired = false;
|
||||
|
||||
// Get the app configuration
|
||||
@@ -26,7 +26,15 @@ angular.module('docs').controller('Login', function(Restangular, $scope, $rootSc
|
||||
User.userInfo(true).then(function(data) {
|
||||
$rootScope.userInfo = data;
|
||||
});
|
||||
$state.go('document.default');
|
||||
|
||||
if($stateParams.redirectState !== undefined && $stateParams.redirectParams !== undefined) {
|
||||
$state.go($stateParams.redirectState, JSON.parse($stateParams.redirectParams))
|
||||
.catch(function() {
|
||||
$state.go('document.default');
|
||||
});
|
||||
} else {
|
||||
$state.go('document.default');
|
||||
}
|
||||
}, function(data) {
|
||||
if (data.data.type === 'ValidationCodeRequired') {
|
||||
// A TOTP validation code is required to login
|
||||
|
||||
@@ -3,13 +3,18 @@
|
||||
/**
|
||||
* Navigation controller.
|
||||
*/
|
||||
angular.module('docs').controller('Navigation', function($scope, $state, $rootScope, User) {
|
||||
angular.module('docs').controller('Navigation', function($scope, $state, $stateParams, $rootScope, User) {
|
||||
User.userInfo().then(function(data) {
|
||||
$rootScope.userInfo = data;
|
||||
if (data.anonymous) {
|
||||
$state.go('login', {}, {
|
||||
location: 'replace'
|
||||
});
|
||||
if($state.current.name !== 'login') {
|
||||
$state.go('login', {
|
||||
redirectState: $state.current.name,
|
||||
redirectParams: JSON.stringify($stateParams),
|
||||
}, {
|
||||
location: 'replace'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -173,7 +173,10 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
||||
$scope.startSearch = function () {
|
||||
var search = '';
|
||||
if (!_.isEmpty($scope.advsearch.search_simple)) {
|
||||
search += $scope.advsearch.search_simple + ' ';
|
||||
var simplesearch = _.map($scope.advsearch.search_simple.split(/\s+/), function (simple) {
|
||||
return 'simple:' + simple
|
||||
});
|
||||
search += simplesearch.join(' ') + ' ';
|
||||
}
|
||||
if (!_.isEmpty($scope.advsearch.search_fulltext)) {
|
||||
var fulltext = _.map($scope.advsearch.search_fulltext.split(/\s+/), function (full) {
|
||||
|
||||
@@ -13,7 +13,6 @@ angular.module('docs').controller('DocumentDefault', function ($scope, $rootScop
|
||||
$scope.loadFiles = function () {
|
||||
Restangular.one('file/list').get().then(function (data) {
|
||||
$scope.files = data.files;
|
||||
// TODO Keep currently uploading files
|
||||
});
|
||||
};
|
||||
$scope.loadFiles();
|
||||
@@ -121,7 +120,7 @@ angular.module('docs').controller('DocumentDefault', function ($scope, $rootScop
|
||||
}
|
||||
|
||||
Restangular.withConfig(function (RestangularConfigurer) {
|
||||
RestangularConfigurer.setBaseUrl('https://api.sismicsdocs.com');
|
||||
RestangularConfigurer.setBaseUrl('https://api.teedy.io');
|
||||
}).one('api').post('feedback', {
|
||||
content: content
|
||||
}).then(function () {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Document view controller.
|
||||
*/
|
||||
angular.module('docs').controller('DocumentView', function ($scope, $state, $stateParams, $location, $dialog, $uibModal, Restangular, $translate) {
|
||||
angular.module('docs').controller('DocumentView', function ($scope, $rootScope, $state, $stateParams, $location, $dialog, $uibModal, Restangular, $translate) {
|
||||
// Load document data from server
|
||||
Restangular.one('document', $stateParams.id).get().then(function (data) {
|
||||
$scope.document = data;
|
||||
@@ -111,10 +111,13 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
||||
var title = $translate.instant('document.view.shared_document_title');
|
||||
var msg = $translate.instant('document.view.shared_document_message', { link: link });
|
||||
var btns = [
|
||||
{result: 'unshare', label: $translate.instant('unshare'), cssClass: 'btn-danger'},
|
||||
{result: 'close', label: $translate.instant('close')}
|
||||
];
|
||||
|
||||
if ($rootScope.userInfo.username !== 'guest') {
|
||||
btns.unshift({result: 'unshare', label: $translate.instant('unshare'), cssClass: 'btn-danger'});
|
||||
}
|
||||
|
||||
$dialog.messageBox(title, msg, btns, function (result) {
|
||||
if (result === 'unshare') {
|
||||
// Unshare this document and update the local shares
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
angular.module('docs').controller('DocumentViewContent', function ($scope, $rootScope, $stateParams, Restangular, $dialog, $state, Upload, $translate, $uibModal) {
|
||||
$scope.displayMode = _.isUndefined(localStorage.fileDisplayMode) ? 'grid' : localStorage.fileDisplayMode;
|
||||
$scope.openedFile = undefined;
|
||||
|
||||
/**
|
||||
* Watch for display mode change.
|
||||
@@ -45,7 +46,6 @@ angular.module('docs').controller('DocumentViewContent', function ($scope, $root
|
||||
$scope.loadFiles = function () {
|
||||
Restangular.one('file/list').get({ id: $stateParams.id }).then(function (data) {
|
||||
$scope.files = data.files;
|
||||
// TODO Keep currently uploading files
|
||||
});
|
||||
};
|
||||
$scope.loadFiles();
|
||||
@@ -55,7 +55,8 @@ angular.module('docs').controller('DocumentViewContent', function ($scope, $root
|
||||
*/
|
||||
$scope.openFile = function (file, $event) {
|
||||
if ($($event.target).parents('.currently-dragging').length === 0) {
|
||||
$state.go('document.view.content.file', {id: $stateParams.id, fileId: file.id})
|
||||
$scope.openedFile = file;
|
||||
$state.go('document.view.content.file', { id: $stateParams.id, fileId: file.id });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Settings LDAP page controller.
|
||||
*/
|
||||
angular.module('docs').controller('SettingsLdap', function($scope, Restangular, $translate, $timeout) {
|
||||
$scope.ldap = {
|
||||
enabled: false
|
||||
};
|
||||
|
||||
// Get the LDAP configuration
|
||||
Restangular.one('app/config_ldap').get().then(function (data) {
|
||||
$scope.ldap = data;
|
||||
if ($scope.ldap.default_storage) {
|
||||
$scope.ldap.default_storage /= 1000000;
|
||||
}
|
||||
});
|
||||
|
||||
// Edit SMTP config
|
||||
$scope.saveResult = undefined;
|
||||
$scope.save = function () {
|
||||
var ldap = angular.copy($scope.ldap);
|
||||
if (ldap.default_storage) {
|
||||
ldap.default_storage *= 1000000;
|
||||
}
|
||||
Restangular.one('app').post('config_ldap', ldap).then(function () {
|
||||
$scope.saveResult = $translate.instant('settings.ldap.saved');
|
||||
$timeout(function() {
|
||||
$scope.saveResult = undefined;
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Add space between element directive.
|
||||
*/
|
||||
angular.module('docs').directive('addSpaceBetween', function () {
|
||||
return function (scope, element) {
|
||||
if(!scope.$last) {
|
||||
element.after(' ');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Invert text color for more legibility directive.
|
||||
*/
|
||||
angular.module('docs').directive('invertTextColor', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
attrs.$observe('invertTextColor', function(hex) {
|
||||
if (!hex || hex.length !== 7) {
|
||||
return;
|
||||
}
|
||||
|
||||
hex = hex.slice(1);
|
||||
var r = parseInt(hex.slice(0, 2), 16),
|
||||
g = parseInt(hex.slice(2, 4), 16),
|
||||
b = parseInt(hex.slice(4, 6), 16);
|
||||
element.css('color', (r * 0.299 + g * 0.587 + b * 0.114) > 186
|
||||
? '#000000'
|
||||
: '#FFFFFF');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -9,6 +9,7 @@ angular.module('docs').directive('selectRelation', function() {
|
||||
templateUrl: 'partial/docs/directive.selectrelation.html',
|
||||
replace: true,
|
||||
scope: {
|
||||
id: '=',
|
||||
relations: '=',
|
||||
ref: '@',
|
||||
ngDisabled: '='
|
||||
@@ -18,21 +19,12 @@ angular.module('docs').directive('selectRelation', function() {
|
||||
* Add a relation.
|
||||
*/
|
||||
$scope.addRelation = function($item) {
|
||||
// Does the new relation is already in the model
|
||||
var duplicate = _.find($scope.relations, function(relation) {
|
||||
if ($item.id === relation.id) {
|
||||
return relation;
|
||||
}
|
||||
});
|
||||
|
||||
// Add the new relation
|
||||
if (!duplicate) {
|
||||
$scope.relations.push({
|
||||
id: $item.id,
|
||||
title: $item.title,
|
||||
source: true
|
||||
});
|
||||
}
|
||||
$scope.relations.push({
|
||||
id: $item.id,
|
||||
title: $item.title,
|
||||
source: true
|
||||
});
|
||||
$scope.input = '';
|
||||
};
|
||||
|
||||
@@ -42,11 +34,11 @@ angular.module('docs').directive('selectRelation', function() {
|
||||
$scope.deleteRelation = function(deleteRelation) {
|
||||
$scope.relations = _.reject($scope.relations, function(relation) {
|
||||
return relation.id === deleteRelation.id;
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise for typeahead title.
|
||||
* Returns a promise for typeahead document.
|
||||
*/
|
||||
$scope.getDocumentTypeahead = function($viewValue) {
|
||||
var deferred = $q.defer();
|
||||
@@ -57,8 +49,16 @@ angular.module('docs').directive('selectRelation', function() {
|
||||
asc: true,
|
||||
search: $viewValue
|
||||
}).then(function(data) {
|
||||
deferred.resolve(data.documents);
|
||||
});
|
||||
deferred.resolve(_.reject(data.documents, function(document) {
|
||||
var duplicate = _.find($scope.relations, function(relation) {
|
||||
if (document.id === relation.id) {
|
||||
return relation;
|
||||
}
|
||||
});
|
||||
|
||||
return document.id === $scope.id || duplicate;
|
||||
}));
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
},
|
||||
|
||||
@@ -56,17 +56,20 @@ angular.module('share',
|
||||
|
||||
// Configuring Angular Translate
|
||||
$translateProvider
|
||||
.useSanitizeValueStrategy(null)
|
||||
.useSanitizeValueStrategy('escapeParameters')
|
||||
.useStaticFilesLoader({
|
||||
prefix: 'locale/',
|
||||
suffix: '.json?@build.date@'
|
||||
})
|
||||
.registerAvailableLanguageKeys(['en', 'es', 'fr', 'de', 'ru', 'zh_CN', 'zh_TW'], {
|
||||
'ru_*': 'ru',
|
||||
.registerAvailableLanguageKeys(['en', 'es', 'fr', 'de', 'el', 'ru', 'it', 'pl', 'zh_CN', 'zh_TW'], {
|
||||
'en_*': 'en',
|
||||
'es_*': 'es',
|
||||
'fr_*': 'fr',
|
||||
'de_*': 'de',
|
||||
'el_*': 'el',
|
||||
'ru_*': 'ru',
|
||||
'it_*': 'it',
|
||||
'pl_*': 'pl',
|
||||
'*': 'en'
|
||||
})
|
||||
.fallbackLanguage('en');
|
||||
@@ -85,6 +88,9 @@ angular.module('share',
|
||||
// Configuring $http to act like jQuery.ajax
|
||||
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
|
||||
$httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
|
||||
$httpProvider.defaults.headers.delete = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
|
||||
};
|
||||
$httpProvider.defaults.transformRequest = [function(data) {
|
||||
var param = function(obj) {
|
||||
var query = '';
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
<script src="app/docs/controller/settings/SettingsGroupEdit.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/settings/SettingsVocabulary.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/settings/SettingsMetadata.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/settings/SettingsLdap.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/usergroup/UserGroup.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/usergroup/UserProfile.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/usergroup/GroupProfile.js" type="text/javascript"></script>
|
||||
@@ -106,6 +107,8 @@
|
||||
<script src="app/docs/directive/Acl.js" type="text/javascript"></script>
|
||||
<script src="app/docs/directive/AclEdit.js" type="text/javascript"></script>
|
||||
<script src="app/docs/directive/Pell.js" type="text/javascript"></script>
|
||||
<script src="app/docs/directive/AddSpaceBetween.js" type="text/javascript"></script>
|
||||
<script src="app/docs/directive/InvertTextColor.js" type="text/javascript"></script>
|
||||
<!-- endref -->
|
||||
</head>
|
||||
<body translate-cloak ng-cloak>
|
||||
@@ -181,8 +184,11 @@
|
||||
<span ng-switch-when="en">English</span>
|
||||
<span ng-switch-when="fr">Français</span>
|
||||
<span ng-switch-when="de">Deutsch</span>
|
||||
<span ng-switch-when="it">Italiano</span>
|
||||
<span ng-switch-when="es">Española</span>
|
||||
<span ng-switch-when="ru">русский</span>
|
||||
<span ng-switch-when="el">Ελληνικά</span>
|
||||
<span ng-switch-when="ru">Pусский</span>
|
||||
<span ng-switch-when="pl">Polski</span>
|
||||
<span ng-switch-when="zh_CN">简体中文</span>
|
||||
<span ng-switch-when="zh_TW">繁體中文</span>
|
||||
</span>
|
||||
@@ -192,9 +198,12 @@
|
||||
<li><a href ng-click="changeLanguage('en')" ng-class="{ 'bg-info': currentLang == 'en' }">English</a></li>
|
||||
<li><a href ng-click="changeLanguage('fr')" ng-class="{ 'bg-info': currentLang == 'fr' }">Français</a></li>
|
||||
<li><a href ng-click="changeLanguage('de')" ng-class="{ 'bg-info': currentLang == 'de' }">Deutsch</a></li>
|
||||
<li><a href ng-click="changeLanguage('it')" ng-class="{ 'bg-info': currentLang == 'it' }">Italiano</a></li>
|
||||
<li><a href ng-click="changeLanguage('es')" ng-class="{ 'bg-info': currentLang == 'es' }">Española</a></li>
|
||||
<li><a href ng-click="changeLanguage('ru')" ng-class="{ 'bg-info': currentLang == 'ru' }">русский</a></li>
|
||||
<li><a href ng-click="changeLanguage('zh_CN')" ng-class="{ 'bg-info': currentLang == 'zh_CN' }">简体中文</a></li>
|
||||
<li><a href ng-click="changeLanguage('el')" ng-class="{ 'bg-info': currentLang == 'el' }">Ελληνικά</a></li>
|
||||
<li><a href ng-click="changeLanguage('ru')" ng-class="{ 'bg-info': currentLang == 'ru' }">Pусский</a></li>
|
||||
<li><a href ng-click="changeLanguage('pl')" ng-class="{ 'bg-info': currentLang == 'pl' }">Polski</a></li>
|
||||
<li><a href ng-click="changeLanguage('zh_CN')" ng-class="{ 'bg-info': currentLang == 'zh_CN' }">简体中文</a></li>
|
||||
<li><a href ng-click="changeLanguage('zh_TW')" ng-class="{ 'bg-info': currentLang == 'zh_TW' }">繁體中文</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -34,6 +34,29 @@ angular.module('yaru22.angular-timeago').config(["timeAgoSettings", function(tim
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('yaru22.angular-timeago').config(["timeAgoSettings", function(timeAgoSettings) {
|
||||
timeAgoSettings.strings['el'] = {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: 'πριν',
|
||||
suffixFromNow: 'από τώρα',
|
||||
seconds: 'λιγότερο από ένα λεπτό',
|
||||
minute: 'περίπου ένα λεπτό',
|
||||
minutes: '%d λεπτά',
|
||||
hour: 'περίπου μια ώρα',
|
||||
hours: 'περίπου %d ώρες',
|
||||
day: 'μια μέρα',
|
||||
days: '%d μέρες',
|
||||
month: 'περίπου ένα μήνα',
|
||||
months: '%d μήνες',
|
||||
year: 'περίπου ένα χρόνο',
|
||||
years: '%d χρόνια',
|
||||
numbers: []
|
||||
};
|
||||
}]);
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('yaru22.angular-timeago').config(["timeAgoSettings", function(timeAgoSettings) {
|
||||
|
||||
/**
|
||||
@@ -288,7 +311,7 @@ angular.module('yaru22.angular-timeago').config(["timeAgoSettings", function(tim
|
||||
'use strict';
|
||||
|
||||
angular.module('yaru22.angular-timeago').config(["timeAgoSettings", function(timeAgoSettings) {
|
||||
timeAgoSettings.strings['it_IT'] = {
|
||||
timeAgoSettings.strings['it'] = {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: 'fa',
|
||||
@@ -356,6 +379,29 @@ angular.module('yaru22.angular-timeago').config(["timeAgoSettings", function(tim
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('yaru22.angular-timeago').config(["timeAgoSettings", function(timeAgoSettings) {
|
||||
timeAgoSettings.strings['pl'] = {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: 'temu',
|
||||
suffixFromNow: 'od teraz',
|
||||
seconds: 'mniej niż minuta',
|
||||
minute: 'około minuty',
|
||||
minutes: '%d minut',
|
||||
hour: 'około godziny',
|
||||
hours: 'około %d godzin',
|
||||
day: 'dzień',
|
||||
days: '%d dni',
|
||||
month: 'około miesiąca',
|
||||
months: '%d miesięcy',
|
||||
year: 'około roku',
|
||||
years: '%d lat',
|
||||
numbers: []
|
||||
};
|
||||
}]);
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('yaru22.angular-timeago').config(["timeAgoSettings", function(timeAgoSettings) {
|
||||
timeAgoSettings.strings['pt_BR'] = {
|
||||
prefixAgo: null,
|
||||
|
||||
@@ -104,7 +104,7 @@ $provide.value("$locale", {
|
||||
"mediumDate": "dd.MM.y",
|
||||
"mediumTime": "HH:mm:ss",
|
||||
"short": "dd.MM.yy HH:mm",
|
||||
"shortDate": "dd.MM.yy",
|
||||
"shortDate": "dd.MM.yyyy",
|
||||
"shortTime": "HH:mm"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
|
||||
125
docs-web/src/main/webapp/src/locale/angular-locale_el.js
vendored
Normal file
125
docs-web/src/main/webapp/src/locale/angular-locale_el.js
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
'use strict';
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": [
|
||||
"\u03c0.\u03bc.",
|
||||
"\u03bc.\u03bc."
|
||||
],
|
||||
"DAY": [
|
||||
"\u039a\u03c5\u03c1\u03b9\u03b1\u03ba\u03ae",
|
||||
"\u0394\u03b5\u03c5\u03c4\u03ad\u03c1\u03b1",
|
||||
"\u03a4\u03c1\u03af\u03c4\u03b7",
|
||||
"\u03a4\u03b5\u03c4\u03ac\u03c1\u03c4\u03b7",
|
||||
"\u03a0\u03ad\u03bc\u03c0\u03c4\u03b7",
|
||||
"\u03a0\u03b1\u03c1\u03b1\u03c3\u03ba\u03b5\u03c5\u03ae",
|
||||
"\u03a3\u03ac\u03b2\u03b2\u03b1\u03c4\u03bf"
|
||||
],
|
||||
"ERANAMES": [
|
||||
"\u03c0\u03c1\u03bf \u03a7\u03c1\u03b9\u03c3\u03c4\u03bf\u03cd",
|
||||
"\u03bc\u03b5\u03c4\u03ac \u03a7\u03c1\u03b9\u03c3\u03c4\u03cc\u03bd"
|
||||
],
|
||||
"ERAS": [
|
||||
"\u03c0.\u03a7.",
|
||||
"\u03bc.\u03a7."
|
||||
],
|
||||
"FIRSTDAYOFWEEK": 0,
|
||||
"MONTH": [
|
||||
"\u0399\u03b1\u03bd\u03bf\u03c5\u03b1\u03c1\u03af\u03bf\u03c5",
|
||||
"\u03a6\u03b5\u03b2\u03c1\u03bf\u03c5\u03b1\u03c1\u03af\u03bf\u03c5",
|
||||
"\u039c\u03b1\u03c1\u03c4\u03af\u03bf\u03c5",
|
||||
"\u0391\u03c0\u03c1\u03b9\u03bb\u03af\u03bf\u03c5",
|
||||
"\u039c\u03b1\u0390\u03bf\u03c5",
|
||||
"\u0399\u03bf\u03c5\u03bd\u03af\u03bf\u03c5",
|
||||
"\u0399\u03bf\u03c5\u03bb\u03af\u03bf\u03c5",
|
||||
"\u0391\u03c5\u03b3\u03bf\u03cd\u03c3\u03c4\u03bf\u03c5",
|
||||
"\u03a3\u03b5\u03c0\u03c4\u03b5\u03bc\u03b2\u03c1\u03af\u03bf\u03c5",
|
||||
"\u039f\u03ba\u03c4\u03c9\u03b2\u03c1\u03af\u03bf\u03c5",
|
||||
"\u039d\u03bf\u03b5\u03bc\u03b2\u03c1\u03af\u03bf\u03c5",
|
||||
"\u0394\u03b5\u03ba\u03b5\u03bc\u03b2\u03c1\u03af\u03bf\u03c5"
|
||||
],
|
||||
"SHORTDAY": [
|
||||
"\u039a\u03c5\u03c1",
|
||||
"\u0394\u03b5\u03c5",
|
||||
"\u03a4\u03c1\u03af",
|
||||
"\u03a4\u03b5\u03c4",
|
||||
"\u03a0\u03ad\u03bc",
|
||||
"\u03a0\u03b1\u03c1",
|
||||
"\u03a3\u03ac\u03b2"
|
||||
],
|
||||
"SHORTMONTH": [
|
||||
"\u0399\u03b1\u03bd",
|
||||
"\u03a6\u03b5\u03b2",
|
||||
"\u039c\u03b1\u03c1",
|
||||
"\u0391\u03c0\u03c1",
|
||||
"\u039c\u03b1\u0390",
|
||||
"\u0399\u03bf\u03c5\u03bd",
|
||||
"\u0399\u03bf\u03c5\u03bb",
|
||||
"\u0391\u03c5\u03b3",
|
||||
"\u03a3\u03b5\u03c0",
|
||||
"\u039f\u03ba\u03c4",
|
||||
"\u039d\u03bf\u03b5",
|
||||
"\u0394\u03b5\u03ba"
|
||||
],
|
||||
"STANDALONEMONTH": [
|
||||
"\u0399\u03b1\u03bd\u03bf\u03c5\u03ac\u03c1\u03b9\u03bf\u03c2",
|
||||
"\u03a6\u03b5\u03b2\u03c1\u03bf\u03c5\u03ac\u03c1\u03b9\u03bf\u03c2",
|
||||
"\u039c\u03ac\u03c1\u03c4\u03b9\u03bf\u03c2",
|
||||
"\u0391\u03c0\u03c1\u03af\u03bb\u03b9\u03bf\u03c2",
|
||||
"\u039c\u03ac\u03b9\u03bf\u03c2",
|
||||
"\u0399\u03bf\u03cd\u03bd\u03b9\u03bf\u03c2",
|
||||
"\u0399\u03bf\u03cd\u03bb\u03b9\u03bf\u03c2",
|
||||
"\u0391\u03cd\u03b3\u03bf\u03c5\u03c3\u03c4\u03bf\u03c2",
|
||||
"\u03a3\u03b5\u03c0\u03c4\u03ad\u03bc\u03b2\u03c1\u03b9\u03bf\u03c2",
|
||||
"\u039f\u03ba\u03c4\u03ce\u03b2\u03c1\u03b9\u03bf\u03c2",
|
||||
"\u039d\u03bf\u03ad\u03bc\u03b2\u03c1\u03b9\u03bf\u03c2",
|
||||
"\u0394\u03b5\u03ba\u03ad\u03bc\u03b2\u03c1\u03b9\u03bf\u03c2"
|
||||
],
|
||||
"WEEKENDRANGE": [
|
||||
5,
|
||||
6
|
||||
],
|
||||
"fullDate": "EEEE, d MMMM y",
|
||||
"longDate": "d MMMM y",
|
||||
"medium": "d MMM y h:mm:ss a",
|
||||
"mediumDate": "d MMM y",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "d/M/yy h:mm a",
|
||||
"shortDate": "d/M/yy",
|
||||
"shortTime": "h:mm a"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "\u20ac",
|
||||
"DECIMAL_SEP": ",",
|
||||
"GROUP_SEP": ".",
|
||||
"PATTERNS": [
|
||||
{
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
{
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "\u00a0\u00a4",
|
||||
"posPre": "",
|
||||
"posSuf": "\u00a0\u00a4"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "el",
|
||||
"localeID": "el",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
@@ -103,8 +103,8 @@ $provide.value("$locale", {
|
||||
"medium": "MMM d, y h:mm:ss a",
|
||||
"mediumDate": "MMM d, y",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "M/d/yy h:mm a",
|
||||
"shortDate": "M/d/yy",
|
||||
"short": "yy/M/d h:mm a",
|
||||
"shortDate": "yyyy/MM/dd",
|
||||
"shortTime": "h:mm a"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user