1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-09 23:45:28 +02:00

Compare commits

...

165 commits

Author SHA1 Message Date
Harvey Kandola
efb092ef8f Prep v5.13.0 release 2024-12-31 13:09:30 -05:00
Harvey Kandola
3fc0a15f87 es-AR 2024-12-31 12:05:21 -05:00
Harvey Kandola
c841c85478 Add en-AR i18n 2024-12-30 13:49:39 -05:00
Harvey Kandola
2dae03332b Prep release 2024-06-18 11:53:23 -04:00
Harvey Kandola
44b1f263cd Restore activity and audit logs 2024-06-18 11:53:15 -04:00
Harvey Kandola
982e16737e Bump version 2024-02-19 11:55:21 -05:00
Harvey Kandola
f641e42434 Remove default conversion service URL 2024-02-19 11:55:15 -05:00
Harvey Kandola
8895db56af Skip restore of user logs 2024-02-19 11:54:50 -05:00
Harvey Kandola
acb59e1b43 Bump Go deps 2024-02-19 11:54:27 -05:00
Harvey Kandola
f2ba294be8 Prep 5.11.3 2024-02-16 10:53:02 -05:00
Harvey Kandola
69940cb7f1
Merge pull request #398 from mb3m/fix-win-build
Fix Windows build script
2024-02-02 06:44:23 -05:00
Harvey Kandola
6bfdda7178
Merge pull request #399 from mb3m/fix-french
French translation updates
2024-02-02 06:42:35 -05:00
Thomas Bolon
027fdf108c french translation updates
- uppercase with accent (E => É)
- Label => Étiquette
- misc terms
- space before punctuations ("?" => " ?")
2024-02-02 11:44:50 +01:00
Thomas Bolon
1f12df76aa typo 2024-02-02 11:17:49 +01:00
Thomas Bolon
d811b88896 fix windows build
- add a cd .. after ember build: the script moves to gui\ directory but
  does not move back, despite the rest of the script expecting running
  from the root directory.

- add multiple echo commands to help debug error

- fix robocopy wrong usage where glob patterns are specified on the
  source path instead of a specific third argument
2024-02-02 11:15:47 +01:00
Harvey Kandola
20fb853907 Implement Azure SQL database compatibility checks 2024-01-16 12:47:44 -05:00
Harvey Kandola
599c53a971 Loosen Microsoft SQL Server database compat. checks 2024-01-15 16:48:21 -05:00
Harvey Kandola
1f462ed4f7 Support Microsoft SQL Azure v12+ 2024-01-15 14:43:27 -05:00
Harvey Kandola
9f122fa79b Bump version 2024-01-11 11:15:43 -05:00
Harvey Kandola
4210caca48 Improve document transaction scopes 2024-01-10 15:54:56 -05:00
Harvey Kandola
c62fa4612b Upgrade Go deps 2024-01-10 14:47:49 -05:00
Harvey Kandola
510e1bd0bd Bump version to 5.11.0 2024-01-10 14:47:40 -05:00
Harvey Kandola
a32510b8e6 Add additional SQL Server indexes 2024-01-10 14:47:08 -05:00
Harvey Kandola
589f3f581f Prep 5.10.0 release 2023-09-18 12:27:18 -04:00
Harvey Kandola
20bba4cd7e Add performance indexes for MS SQL Server database provider 2023-09-18 12:27:09 -04:00
Harvey Kandola
cbf5f4be7d
Merge pull request #397 from armando-marra/master
Modified i18n.js and Readme.md
2023-09-08 12:15:28 -04:00
Armando Marra
dc63639c99
Update README.md
Added italian to languages list
2023-09-08 09:10:05 +02:00
Armando Marra
26f435bdc9
Merge branch 'documize:master' into master 2023-09-08 09:08:21 +02:00
Armando Marra
a8a82963fa
Merge pull request #1 from armando-marra/patch-1-it-lang
Update i18n.js
2023-09-08 09:07:42 +02:00
Armando Marra
ab8582e807
Update i18n.js
Added Italian language support
2023-09-08 09:06:48 +02:00
Harvey Kandola
4fa0566274
Merge pull request #396 from armando-marra/master
Added Italian translation
2023-08-23 09:09:45 -04:00
armando-marra
f4b45d2aa7
Update localize.go
Added Italian language
2023-08-23 14:22:47 +02:00
armando-marra
1abc5d3e52
Italian language JSON file 2023-08-23 14:14:38 +02:00
Harvey Kandola
6e463ff2f4 Bump version 2023-08-21 12:20:53 -04:00
Harvey Kandola
f80b3f3d10 5.8.0 2023-07-24 11:11:46 -04:00
Harvey Kandola
6c218cf087 Add additional indexes for SQL Server 2023-07-24 11:11:39 -04:00
Harvey Kandola
3d1c8a6c54 Remove archived versions from version selector 2023-07-24 11:11:25 -04:00
Harvey Kandola
576fd5e604
Merge pull request #395 from xadammr/patch-1
Add support for SQL Server 2022
2023-07-17 17:07:09 -04:00
Adam Roe
62407a28b4
Add support for SQL Server 2022 2023-07-15 18:27:20 +10:00
Harvey Kandola
0adf6d5dc8 Update permission.go 2023-06-12 13:24:47 -04:00
Harvey Kandola
15f8a64c86 Bump Go & improve SQL layer 2023-05-15 13:47:22 -04:00
Harvey Kandola
95c67acaa0
Merge pull request #394 from uchida-nunet/master
Minor corrections Japanese translation
2023-03-04 08:45:39 -05:00
Harvey Kandola
d8f66b5ffb Bump version 2023-02-27 13:30:34 -05:00
Uchida, Yasuhiro
c051e81a99 Update ja-JP.json 2023-02-24 01:08:43 +09:00
Uchida, Yasuhiro
1d86b98949
Update ja-JP.json 2023-02-24 00:59:06 +09:00
Harvey Kandola
0a1cc86907
Merge pull request #393 from uchida-nunet/master
add Japanese language support
2023-02-11 10:28:58 -05:00
uchida-nunet
a49869d35d Update ja-JP.json 2023-02-11 18:09:27 +09:00
uchida-nunet
848afd3263 change gui/app/services/i18n.js 2023-02-11 12:51:59 +09:00
uchida-nunet
b9cb99e3bb mod 2023-02-11 12:43:32 +09:00
uchida-nunet
64261ffcf5 Merge branch 'master' of https://github.com/uchida-nunet/documize-community 2023-02-11 12:30:12 +09:00
uchida-nunet
0030418707 add Japanese language support 2023-02-11 12:29:43 +09:00
uchida-nunet
0f91ee518e add Japanese language support 2023-02-11 12:06:29 +09:00
Harvey Kandola
5de1b7a92e
Merge pull request #392 from Xhelliom/dev
Minor corrections french translation
2023-02-07 11:04:17 -05:00
Stephane Wetterwald
a2524f785e Minor corrections 2023-02-07 15:57:25 +01:00
Harvey Kandola
f16b9f3810 Build prep 2023-02-06 14:01:19 -05:00
Harvey Kandola
1c09771c33 Bump version 2023-02-06 11:46:10 -05:00
Harvey Kandola
13fc5b5015 Test for user permissions before setting them 2023-02-06 11:46:03 -05:00
Harvey Kandola
76c777acc1 French localization 2023-02-06 11:45:47 -05:00
Harvey Kandola
ea9ff78411
Merge pull request #391 from Xhelliom/dev
Translate to french
2023-02-06 11:19:35 -05:00
Stephane Wetterwald
4a9dd47894 translate to french 2023-02-01 21:40:34 +01:00
Harvey Kandola
7565779ef1
Update docker-compose.yaml
Revert broken command
2023-01-11 20:04:00 -05:00
Stephane Wetterwald
c07e7b6afc more translations 2022-12-29 16:50:00 +01:00
Harvey Kandola
88bdafcb1b
Merge pull request #390 from rursache/patch-1
Updated docker-compose.yaml
2022-12-17 13:09:34 -05:00
Radu Ursache
5a3cb1b226
Updated docker-compose.yaml
Updated `docker-compose.yaml` to read the correct arch from device so arm64 devices can run Documize as well
2022-12-17 20:07:23 +02:00
Stephane Wetterwald
6ee8e6c7b4 translated to line 200 2022-12-15 12:32:35 +01:00
Stephane Wetterwald
599c464d2d add fr 2022-12-15 12:18:14 +01:00
Harvey Kandola
ae77fa2275
Merge pull request #389 from nemunaire/b/dockerfile
Dockerfile: Force node 16 + update go and alpine tags
2022-11-22 08:54:19 -05:00
Pierre-Olivier Mercier
610367aac5 Force node 16 + update go and alpine tags 2022-11-22 10:11:41 +01:00
Harvey Kandola
be2c2a7a2c v5.4.2 prep 2022-10-31 18:32:59 -04:00
Harvey Kandola
0d28b7ee79 Fix org reset during restore 2022-10-31 18:32:46 -04:00
Harvey Kandola
aa8b473018 Bump version to 5.4.1 2022-10-21 11:13:13 -04:00
Harvey Kandola
6993dc678f Fix regression for robots.txt and sitemap.xml 2022-10-21 11:13:03 -04:00
Harvey Kandola
e0e3f0c141 Fix middleware for favicon/sitemap 2022-10-21 11:12:46 -04:00
Harvey Kandola
4c031fe7e4 Set TLS min to 1.3 2022-10-21 11:12:17 -04:00
Harvey Kandola
e4025bee42 Bump to v5.4.0 2022-10-10 17:40:45 -04:00
Harvey Kandola
876775b395 Support TLS version selection
Allow config file and ENV variables to define minimum TLS version used for SSL connections.

tlsversion=1.3
2022-10-10 17:40:36 -04:00
Harvey Kandola
828c01d189 Update README.md 2022-09-18 15:40:10 -04:00
Harvey Kandola
a69bcc0af6 Update README.md 2022-09-18 15:39:16 -04:00
Harvey Kandola
5ec911dce2 Dotcom pricing landing page changes 2022-09-18 15:25:57 -04:00
Harvey Kandola
ce07d4d147 Bump version 2022-09-18 14:29:38 -04:00
Harvey Kandola
f3ef83162e
Update README.md 2022-09-16 12:25:02 -04:00
Harvey Kandola
f1a01ec195 Bump version to 5.2.2 2022-09-12 12:35:51 -04:00
Harvey Kandola
01e53c3d27 Fix sanitization of document title 2022-09-12 12:35:40 -04:00
Harvey Kandola
2cf21a7bea Fix i18n issue 2022-09-08 16:45:28 -04:00
Harvey Kandola
d4c606760c Bump version 2022-08-01 10:13:21 -04:00
Harvey Kandola
9343d77b26 Bump PDF viewer dep 2022-08-01 10:13:15 -04:00
Harvey Kandola
30aa8aadb6 Fix i18n bug for PDF viewer 2022-08-01 10:13:03 -04:00
Harvey Kandola
29bc2677a8 v5.2.0 2022-07-13 13:28:31 -04:00
Harvey Kandola
d9827df440
Merge pull request #387 from kmkzif/master
Add portuguese language support
2022-07-13 13:21:24 -04:00
KMKZ
cfd7ebd2bf Add portuguese language support 2022-07-12 22:14:26 -03:00
Harvey Kandola
b510615691 Update community.go 2022-07-10 15:49:34 -04:00
Harvey Kandola
e8641405cf Update package.json 2022-07-10 15:48:21 -04:00
Harvey Kandola
209f1b667e Bump version 2022-07-10 15:38:37 -04:00
Harvey Kandola
e70019d73b
Merge pull request #386 from 569258yin/master
Add chinese language support
2022-07-10 15:32:39 -04:00
manwang
dc26f063c8 add chinese language support 2022-07-09 08:50:35 +08:00
Harvey Kandola
68d067ef7b Update en-US.json 2022-05-16 16:30:17 -04:00
Harvey Kandola
0d52f434d5 Localized string updates 2022-05-16 16:23:19 -04:00
Harvey Kandola
ce22c78dac Bump version 2022-05-16 13:22:02 -04:00
Harvey Kandola
f976ea36f6 Fix version control translation strings 2022-05-16 13:12:55 -04:00
Harvey Kandola
1734963693 Remove unnecessary permission check 2022-05-12 12:27:06 -04:00
Harvey Kandola
247a2b2c03 Remove back tick 2022-04-15 15:53:47 -04:00
Harvey Kandola
38a790dd04 v5 release
Supported locales: English and German.
2022-04-13 13:54:27 -04:00
Harvey Kandola
b77b4abdc2 Add de-DE locale 2022-04-08 11:59:08 -04:00
Harvey Kandola
6b498a74c6
Merge pull request #383 from lafriks-fork/fix/docker_missing_i18n
Fix missing i18n files in docker build
2022-04-05 11:50:25 -04:00
Lauris BH
f6dd872782
Fix missing i18n files in docker build 2022-04-05 15:54:03 +03:00
Harvey Kandola
9473ecba9a Update flowchart.go 2022-03-24 13:13:11 -04:00
Harvey Kandola
1a909dd046 Support custom URLS for Diagrams.net 2022-03-24 13:05:45 -04:00
Harvey Kandola
607a2d5797 Create howto-localize.png 2022-03-23 12:35:32 -04:00
Harvey Kandola
037dfc40cd Update en-US.json 2022-03-22 13:18:32 -04:00
Harvey Kandola
65348eee28 More i18n strings 2022-03-22 13:13:57 -04:00
Harvey Kandola
78932fb8c7 Fix i18n strings 2022-03-22 12:14:31 -04:00
Harvey Kandola
6c8b10753d i18n fixes 2022-03-21 19:14:10 -04:00
Harvey Kandola
e56263564c
Merge pull request #382 from documize/i18n
Localization support
2022-03-21 13:18:44 -04:00
Harvey Kandola
22b6a4fb78 Update i18n.js 2022-03-21 13:15:53 -04:00
Harvey Kandola
7e26c003d6 Select user language before server language 2022-03-21 13:14:55 -04:00
Harvey Kandola
e81cbad385 Select language for server and per user 2022-03-21 12:43:45 -04:00
Harvey Kandola
4494ace0a2 Set org and per user locale 2022-03-19 18:07:38 -04:00
Harvey Kandola
23abcf1585 i18n fixes 2022-03-17 16:42:15 -04:00
Harvey Kandola
67070c3bfc Require sub check 2022-03-17 16:31:11 -04:00
Harvey Kandola
77c767a351 Update en-US.json 2022-03-17 16:17:37 -04:00
Harvey Kandola
17162ce336 i18n mail template strings 2022-03-17 13:03:04 -04:00
Harvey Kandola
7255eb4f56 i18n server-side strings 2022-03-16 17:39:01 -04:00
Harvey Kandola
df534f72fa i18n server-side strings 2022-03-16 16:58:42 -04:00
Harvey Kandola
f4a1350a41 i18n server-side loading and setup 2022-03-16 13:32:48 -04:00
Harvey Kandola
cd15c393fe Go 1.18 & tidy deps 2022-03-16 13:32:27 -04:00
McMatts
7f66977ac1 i18n notify strings 2022-03-14 14:49:10 -04:00
McMatts
33a9cbb5b0 Update licensing links 2022-03-14 14:33:54 -04:00
McMatts
716343680a i18n Enterprise feature-set 2022-03-14 14:09:50 -04:00
McMatts
5db5f4d63b i18n continuation 2022-03-14 12:01:46 -04:00
McMatts
3d3d50762e i18n section stings 2022-03-10 16:35:59 -05:00
McMatts
20c9168140 i18n section strings 2022-03-10 15:21:25 -05:00
McMatts
ce9c635fb4 i18n section strings 2022-03-10 13:49:19 -05:00
McMatts
f735ae1278 i18n section types 2022-03-10 12:10:39 -05:00
McMatts
bca7794c00 i18n doc strings 2022-03-09 15:20:16 -05:00
McMatts
371706fb49 i18n doc strings 2022-03-09 14:04:17 -05:00
McMatts
a236cbb01c i18n doc strings 2022-03-09 13:36:48 -05:00
McMatts
93b6f26365 i18n space strings 2022-03-08 17:05:12 -05:00
McMatts
5e687f5ef4 i18n space strings 2022-03-08 13:05:30 -05:00
McMatts
97c4c927ac i18n space strings 2022-03-08 12:50:34 -05:00
McMatts
4885a1b380 i18n space strings 2022-03-08 12:16:36 -05:00
McMatts
e0805d7131 i18n search strings 2022-03-08 11:35:59 -05:00
McMatts
6d735e8579 i18n page strings 2022-03-04 18:31:23 -05:00
McMatts
073ef81e80 i18n strings
Pods completed
2022-03-04 13:46:22 -05:00
McMatts
38c9a94a9c i18n strings 2022-03-04 13:28:36 -05:00
McMatts
59dc6ea991 i18n page strings 2022-03-04 13:07:11 -05:00
McMatts
4ab48cc67d i18n admin strings 2022-03-03 19:42:37 -05:00
McMatts
53297f7627 i18n admin strings 2022-03-03 19:10:06 -05:00
McMatts
4ed2b3902c JS build tweaks 2022-03-03 18:21:16 -05:00
McMatts
6968581e5b i18n admin strings 2022-03-03 14:38:27 -05:00
McMatts
c09a116e56 i18n admin strings 2022-03-03 14:03:48 -05:00
McMatts
7cf672646a i18n admin strings 2022-03-02 20:52:59 -05:00
McMatts
29447a2784 i18n admin strings 2022-03-02 20:30:39 -05:00
McMatts
479d03ba70 i18n admin sections 2022-03-02 19:55:52 -05:00
McMatts
a7dac6911c i18n strings 2022-03-01 22:59:56 -05:00
McMatts
08f21346c1 [WIP] i18n constant strings 2022-03-01 22:40:51 -05:00
McMatts
ce4f62d346 [WIP] i18n strings 2022-03-01 22:03:18 -05:00
McMatts
8a25509019 Implement basic i18n client-side foundation 2022-03-01 20:01:06 -05:00
McMatts
59c929d251 New logo 2022-03-01 20:00:33 -05:00
McMatts
245c538990 Initial i18n experiment 2022-03-01 16:22:53 -05:00
HarveyKandola
32a9528e6d Update README.md 2022-02-27 12:48:26 -05:00
HarveyKandola
a15f0c8eb6 Update README.md 2022-02-27 12:48:04 -05:00
HarveyKandola
eb9fbd25b9 Update README.md 2022-02-27 12:47:09 -05:00
Harvey Kandola
dbef758035
Merge pull request #381 from typkrft/master
Updated URL
2022-02-17 11:07:03 -05:00
Brandon
dea25a2b85
Updated URL
The current URL doesn't point to the actual download.
2022-02-17 10:59:25 -05:00
HarveyKandola
fcf38d8af9 Change downloads asset location 2022-02-14 14:23:10 -05:00
979 changed files with 100107 additions and 81822 deletions

View file

@ -1,10 +1,10 @@
FROM node:lts-alpine as frontbuilder FROM node:16-alpine as frontbuilder
WORKDIR /go/src/github.com/documize/community/gui WORKDIR /go/src/github.com/documize/community/gui
COPY ./gui /go/src/github.com/documize/community/gui COPY ./gui /go/src/github.com/documize/community/gui
RUN npm --network-timeout=100000 install RUN npm --network-timeout=100000 install
RUN npm run build -- --environment=production --output-path dist-prod --suppress-sizes true RUN npm run build -- --environment=production --output-path dist-prod --suppress-sizes true
FROM golang:1.17-alpine as builder FROM golang:1.21-alpine as builder
WORKDIR /go/src/github.com/documize/community WORKDIR /go/src/github.com/documize/community
COPY . /go/src/github.com/documize/community COPY . /go/src/github.com/documize/community
COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/assets /go/src/github.com/documize/community/edition/static/public/assets COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/assets /go/src/github.com/documize/community/edition/static/public/assets
@ -13,7 +13,9 @@ COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/pri
COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/sections /go/src/github.com/documize/community/edition/static/public/sections COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/sections /go/src/github.com/documize/community/edition/static/public/sections
COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/tinymce /go/src/github.com/documize/community/edition/static/public/tinymce COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/tinymce /go/src/github.com/documize/community/edition/static/public/tinymce
COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/pdfjs /go/src/github.com/documize/community/edition/static/public/pdfjs COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/pdfjs /go/src/github.com/documize/community/edition/static/public/pdfjs
COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/i18n /go/src/github.com/documize/community/edition/static/public/i18n
COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/*.* /go/src/github.com/documize/community/edition/static/ COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/*.* /go/src/github.com/documize/community/edition/static/
COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/i18n/*.json /go/src/github.com/documize/community/edition/static/i18n/
COPY domain/mail/*.html /go/src/github.com/documize/community/edition/static/mail/ COPY domain/mail/*.html /go/src/github.com/documize/community/edition/static/mail/
COPY core/database/templates/*.html /go/src/github.com/documize/community/edition/static/ COPY core/database/templates/*.html /go/src/github.com/documize/community/edition/static/
COPY core/database/scripts/mysql/*.sql /go/src/github.com/documize/community/edition/static/scripts/mysql/ COPY core/database/scripts/mysql/*.sql /go/src/github.com/documize/community/edition/static/scripts/mysql/
@ -23,7 +25,7 @@ COPY domain/onboard/*.json /go/src/github.com/documize/community/edition/static/
RUN env GODEBUG=tls13=1 go build -mod=vendor -o bin/documize-community ./edition/community.go RUN env GODEBUG=tls13=1 go build -mod=vendor -o bin/documize-community ./edition/community.go
# build release image # build release image
FROM alpine:3.14 FROM alpine:3.16
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates
COPY --from=builder /go/src/github.com/documize/community/bin/documize-community /documize COPY --from=builder /go/src/github.com/documize/community/bin/documize-community /documize
EXPOSE 5001 EXPOSE 5001

View file

@ -1,10 +1,10 @@
Documize Community is an open source modern, lightweight alternative to Confluence and other similar solutions. Documize Community is an open source, modern, self-hosted, enterprise-grade knowledge management solution.
- Built for technical and non-technical users - Built for technical and non-technical users
- Designed to unify both customer-facing and internal documentation - Designed to unify both customer-facing and internal documentation
- Organization through labels, spaces and categories - Organization through labels, spaces and categories
It's built with Golang + EmberJS and compiled down to a single executable binary available for Linux, Windows and Mac. It's built with Golang + EmberJS and compiled down to a single executable binary that is available for Linux, Windows and Mac.
All you need to provide is your database -- PostgreSQL, Microsoft SQL Server or any MySQL variant. All you need to provide is your database -- PostgreSQL, Microsoft SQL Server or any MySQL variant.
@ -12,11 +12,11 @@ All you need to provide is your database -- PostgreSQL, Microsoft SQL Server or
## Latest Release ## Latest Release
[Community edition: v4.2.2](https://github.com/documize/community/releases) [Community edition: v5.13.0](https://github.com/documize/community/releases)
[Community+ edition: v4.2.2](https://www.documize.com/community/downloads) [Community+ edition: v5.13.0](https://www.documize.com/community/get-started)
The Community+ edition is the "Enterprise" offering with advanced capabilities and customer support: The Community+ edition is the "enterprise" offering with advanced capabilities and customer support:
- content approval workflows - content approval workflows
- content organization by label, space and category - content organization by label, space and category
@ -30,6 +30,8 @@ The Community+ edition is the "Enterprise" offering with advanced capabilities a
- actions assignments - actions assignments
- product support - product support
The Community+ edition is [free](https://www.documize.com/community/get-started) for the first five users -- thereafter pricing starts at just $900 annually for 100 users.
## OS Support ## OS Support
- Linux - Linux
@ -41,10 +43,11 @@ Support for AMD and ARM 64 bit architectures.
## Database Support ## Database Support
For all database types, Full-Text Search support (FTS) is mandatory. For all database types, Full-Text Search (FTS) support is mandatory.
- PostgreSQL (v9.6+) - PostgreSQL (v9.6+)
- Microsoft SQL Server (2016+ with FTS) - Microsoft SQL Server (2016+ with FTS)
- Microsoft SQL Azure (v12+)
- MySQL (v5.7.10+ and v8.0.12+) - MySQL (v5.7.10+ and v8.0.12+)
- Percona (v5.7.16-10+) - Percona (v5.7.16-10+)
- MariaDB (10.3.0+) - MariaDB (10.3.0+)
@ -54,14 +57,14 @@ For all database types, Full-Text Search support (FTS) is mandatory.
- Firefox - Firefox
- Chrome - Chrome
- Safari - Safari
- Microsoft Edge (v42+) - Microsoft Edge
- Brave - Brave
- Vivaldi - Vivaldi
- Opera - Opera
## Technology Stack ## Technology Stack
- Go (v1.17.6) - Go (v1.23.4)
- Ember JS (v3.12.0) - Ember JS (v3.12.0)
## Authentication Options ## Authentication Options
@ -75,18 +78,33 @@ Besides email/password login, you can also authenticate via:
When using LDAP/Active Directory, you can enable dual-authentication with email/password. When using LDAP/Active Directory, you can enable dual-authentication with email/password.
## Localization
Languages supported out-of-the-box:
- English
- German
- French
- Chinese (中文)
- Portuguese (Brazil) (Português - Brasil)
- Japanese (日本語)
- Italian
- Spanish Argentinian
PR's welcome for additional languages.
## Product/Technical Support ## Product/Technical Support
For both Community and Community+ editions, please contact our help desk for product help, suggestions and other enquiries. For both Community and Community+ editions, please contact our help desk for product help, suggestions and other enquiries.
<support@documize.com> <support@documize.com>
We aim to respond within two working days! We aim to respond within two working days.
## The Legal Bit ## The Legal Bit
<https://www.documize.com> <https://www.documize.com>
This software (Documize Community Edition) is licensed under GNU AGPL v3 <http://www.gnu.org/licenses/agpl-3.0.en.html>. You can operate outside the AGPL restrictions by purchasing Documize Community+ edition and obtaining a commercial license by contacting <sales@documize.com>. Documize® is a registered trademark of Documize Inc. This software (Documize Community edition) is licensed under GNU AGPL v3 <http://www.gnu.org/licenses/agpl-3.0.en.html>.
Documize uses other open source components and we acknowledge them in [NOTICES](NOTICES.md) Documize Community uses other open source components and we acknowledge them in [NOTICES](NOTICES.md)

View file

@ -7,6 +7,7 @@ echo "Building Ember assets..."
cd gui cd gui
call ember b -o dist-prod/ --environment=production call ember b -o dist-prod/ --environment=production
::Call allows the rest of the file to run ::Call allows the rest of the file to run
cd ..
rd /s /q edition\static\public rd /s /q edition\static\public
mkdir edition\static\public mkdir edition\static\public
@ -22,16 +23,30 @@ echo "Copying Ember pdfjs folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\pdfjs edition\static\public\pdfjs robocopy /e /NFL /NDL /NJH gui\dist-prod\pdfjs edition\static\public\pdfjs
echo "Copying Ember sections folder" echo "Copying Ember sections folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\sections edition\static\public\sections robocopy /e /NFL /NDL /NJH gui\dist-prod\sections edition\static\public\sections
echo "Copying i18n folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\i18n edition\static\public\i18n
echo "Copying static files"
copy gui\dist-prod\*.* edition\static copy gui\dist-prod\*.* edition\static
echo "Copying favicon.ico"
copy gui\dist-prod\favicon.ico edition\static\public copy gui\dist-prod\favicon.ico edition\static\public
echo "Copying manifest.json"
copy gui\dist-prod\manifest.json edition\static\public copy gui\dist-prod\manifest.json edition\static\public
echo "Copying mail templates"
rd /s /q edition\static\mail rd /s /q edition\static\mail
mkdir edition\static\mail mkdir edition\static\mail
copy domain\mail\*.html edition\static\mail copy domain\mail\*.html edition\static\mail
echo "Copying database templates"
copy core\database\templates\*.html edition\static copy core\database\templates\*.html edition\static
rd /s /q edition\static\i18n
mkdir edition\static\i18n
robocopy /e /NFL /NDL /NJH gui\dist-prod\i18n edition\static\i18n *.json
rd /s /q edition\static\scripts rd /s /q edition\static\scripts
mkdir edition\static\scripts mkdir edition\static\scripts
mkdir edition\static\scripts\mysql mkdir edition\static\scripts\mysql
@ -45,16 +60,8 @@ robocopy /e /NFL /NDL /NJH core\database\scripts\sqlserver edition\static\script
rd /s /q edition\static\onboard rd /s /q edition\static\onboard
mkdir edition\static\onboard mkdir edition\static\onboard
robocopy /e /NFL /NDL /NJH domain\onboard\*.json edition\static\onboard robocopy /e /NFL /NDL /NJH domain\onboard edition\static\onboard *.json
echo "Compiling Windows" echo "Compiling Windows"
set GOOS=windows set GOOS=windows
go build -mod=vendor -gcflags="all=-trimpath=$GOPATH" -o bin/documize-community-windows-amd64.exe edition/community.go go build -mod=vendor -trimpath -gcflags="all=-trimpath=$GOPATH" -o bin/documize-community-windows-amd64.exe edition/community.go
echo "Compiling Linux"
set GOOS=linux
go build -mod=vendor -gcflags="all=-trimpath=$GOPATH" -o bin/documize-community-linux-amd64 edition/community.go
echo "Compiling Darwin"
set GOOS=darwin
go build -mod=vendor -gcflags="all=-trimpath=$GOPATH" -o bin/documize-community-darwin-amd64 edition/community.go

View file

@ -8,6 +8,7 @@ echo "Build process started $NOW"
echo "Building Ember assets..." echo "Building Ember assets..."
cd gui cd gui
# export NODE_OPTIONS=--openssl-legacy-provider
ember build ---environment=production --output-path dist-prod --suppress-sizes true ember build ---environment=production --output-path dist-prod --suppress-sizes true
cd .. cd ..
@ -20,6 +21,7 @@ cp -r gui/dist-prod/prism edition/static/public/prism
cp -r gui/dist-prod/sections edition/static/public/sections cp -r gui/dist-prod/sections edition/static/public/sections
cp -r gui/dist-prod/tinymce edition/static/public/tinymce cp -r gui/dist-prod/tinymce edition/static/public/tinymce
cp -r gui/dist-prod/pdfjs edition/static/public/pdfjs cp -r gui/dist-prod/pdfjs edition/static/public/pdfjs
cp -r gui/dist-prod/i18n edition/static/public/i18n
cp gui/dist-prod/*.* edition/static cp gui/dist-prod/*.* edition/static
cp gui/dist-prod/favicon.ico edition/static/public cp gui/dist-prod/favicon.ico edition/static/public
cp gui/dist-prod/manifest.json edition/static/public cp gui/dist-prod/manifest.json edition/static/public
@ -29,6 +31,10 @@ mkdir -p edition/static/mail
cp domain/mail/*.html edition/static/mail cp domain/mail/*.html edition/static/mail
cp core/database/templates/*.html edition/static cp core/database/templates/*.html edition/static
rm -rf edition/static/i18n
mkdir -p edition/static/i18n
cp -r gui/dist-prod/i18n/*.json edition/static/i18n
rm -rf edition/static/scripts rm -rf edition/static/scripts
mkdir -p edition/static/scripts mkdir -p edition/static/scripts
mkdir -p edition/static/scripts/mysql mkdir -p edition/static/scripts/mysql

View file

@ -19,8 +19,8 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"context"
api "github.com/documize/community/core/convapi" api "github.com/documize/community/core/convapi"
"golang.org/x/net/context"
) )
// Msword type provides a peg to hang the Convert method on. // Msword type provides a peg to hang the Convert method on.

View file

@ -19,7 +19,7 @@ import (
"github.com/documize/community/core/api/plugins" "github.com/documize/community/core/api/plugins"
api "github.com/documize/community/core/convapi" api "github.com/documize/community/core/convapi"
"golang.org/x/net/context" "context"
) )
// Convert provides the entry-point into the document conversion process. // Convert provides the entry-point into the document conversion process.

View file

@ -16,7 +16,7 @@ import (
api "github.com/documize/community/core/convapi" api "github.com/documize/community/core/convapi"
"golang.org/x/net/context" "context"
) )
// Convert provides the standard interface for conversion of a ".documizeapi" json document. // Convert provides the standard interface for conversion of a ".documizeapi" json document.

View file

@ -16,9 +16,9 @@ import (
"fmt" "fmt"
"strings" "strings"
"context"
api "github.com/documize/community/core/convapi" api "github.com/documize/community/core/convapi"
"github.com/documize/community/core/stringutil" "github.com/documize/community/core/stringutil"
"golang.org/x/net/context"
"golang.org/x/net/html" "golang.org/x/net/html"
"golang.org/x/net/html/atom" "golang.org/x/net/html/atom"
) )

View file

@ -16,7 +16,7 @@ import (
"github.com/documize/blackfriday" "github.com/documize/blackfriday"
"golang.org/x/net/context" "context"
) )
// Convert provides the standard interface for conversion of a Markdown document. // Convert provides the standard interface for conversion of a Markdown document.

View file

@ -1,61 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package plugins
import (
"os"
"testing"
)
func TestSetup(t *testing.T) {
err := LibSetup()
if err == nil {
//t.Error("should error on non-existent config file")
//t.Fail()
}
ssc, err := Lib.Actions("Convert")
if err != nil {
t.Error(err)
}
// TODO(Elliott) review for empty database
//if len(ssc) > 3 {
// t.Errorf("extra convert formats:%v", ssc)
//}
/* this code leaves plugins still running */
err = os.Chdir("../../..")
if err != nil {
t.Error(err)
}
err = LibSetup()
if err != nil {
t.Error(err)
}
ssc, err = Lib.Actions("Convert")
if err != nil {
t.Error(err)
}
if len(ssc) == 0 {
t.Error("no extra convert formats (defined)")
}
err = os.Chdir("documize/api/plugins")
if err != nil {
t.Error(err)
}
err = Lib.KillSubProcs()
if err != nil {
t.Error(err)
}
}

View file

@ -82,7 +82,7 @@ func Check(runtime *env.Runtime) bool {
return false return false
} }
if len(flds) == 0 { if len(flds) <= 5 {
runtime.Log.Info("Database: starting setup mode for empty database") runtime.Log.Info("Database: starting setup mode for empty database")
runtime.Flags.SiteMode = env.SiteModeSetup runtime.Flags.SiteMode = env.SiteModeSetup
return false return false

View file

@ -1,110 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package database
// import (
// "crypto/rand"
// "time"
// "github.com/documize/community/core/env"
// "github.com/jmoiron/sqlx"
// )
// // Lock will try to lock the database instance to the running process.
// // Uses a "random" delay as a por man's database cluster-aware process.
// // We skip delay if there are no scripts to process.
// func Lock(runtime *env.Runtime, scriptsToProcess int) (bool, error) {
// // Wait for random period of time.
// b := make([]byte, 2)
// _, err := rand.Read(b)
// if err != nil {
// return false, err
// }
// wait := ((time.Duration(b[0]) << 8) | time.Duration(b[1])) * time.Millisecond / 10 // up to 6.5 secs wait
// // Why delay if nothing to process?
// if scriptsToProcess > 0 {
// time.Sleep(wait)
// }
// // Start transaction fotr lock process.
// tx, err := runtime.Db.Beginx()
// if err != nil {
// runtime.Log.Error("Database: unable to start transaction", err)
// return false, err
// }
// // Lock the database.
// _, err = tx.Exec(runtime.StoreProvider.QueryStartLock())
// if err != nil {
// runtime.Log.Error("Database: unable to lock tables", err)
// return false, err
// }
// // Unlock the database at the end of this function.
// defer func() {
// _, err = tx.Exec(runtime.StoreProvider.QueryFinishLock())
// if err != nil {
// runtime.Log.Error("Database: unable to unlock tables", err)
// }
// tx.Commit()
// }()
// // Try to record this process as leader of database migration process.
// _, err = tx.Exec(runtime.StoreProvider.QueryInsertProcessID())
// if err != nil {
// runtime.Log.Info("Database: marked as slave process awaiting upgrade")
// return false, nil
// }
// // We are the leader!
// runtime.Log.Info("Database: marked as database upgrade process leader")
// return true, err
// }
// // Unlock completes process that was started with Lock().
// func Unlock(runtime *env.Runtime, tx *sqlx.Tx, err error, amLeader bool) error {
// if amLeader {
// defer func() {
// doUnlock(runtime)
// }()
// if tx != nil {
// if err == nil {
// tx.Commit()
// runtime.Log.Info("Database: is ready")
// return nil
// }
// tx.Rollback()
// }
// runtime.Log.Error("Database: install/upgrade failed", err)
// return err
// }
// return nil // not the leader, so ignore errors
// }
// // Helper method for defer function called from Unlock().
// func doUnlock(runtime *env.Runtime) error {
// tx, err := runtime.Db.Beginx()
// if err != nil {
// return err
// }
// _, err = tx.Exec(runtime.StoreProvider.QueryDeleteProcessID())
// if err != nil {
// return err
// }
// return tx.Commit()
// }

View file

@ -1,3 +1,3 @@
/* community edition */ /* community edition */
ALTER TABLE organization ADD COLUMN `service` VARCHAR(100) NOT NULL DEFAULT 'https://api.documize.com' AFTER `domain`; ALTER TABLE organization ADD COLUMN `service` VARCHAR(100) NOT NULL DEFAULT '' AFTER `domain`;

View file

@ -36,7 +36,7 @@ ALTER TABLE dmz_org
CHANGE `title` `c_title` VARCHAR(500) NOT NULL, CHANGE `title` `c_title` VARCHAR(500) NOT NULL,
CHANGE `message` `c_message` VARCHAR(500) NOT NULL, CHANGE `message` `c_message` VARCHAR(500) NOT NULL,
CHANGE `domain` `c_domain` VARCHAR(200) NOT NULL DEFAULT '', CHANGE `domain` `c_domain` VARCHAR(200) NOT NULL DEFAULT '',
CHANGE `service` `c_service` VARCHAR(200) NOT NULL DEFAULT 'https://api.documize.com', CHANGE `service` `c_service` VARCHAR(200) NOT NULL DEFAULT '',
CHANGE `email` `c_email` VARCHAR(500) NOT NULL DEFAULT '', CHANGE `email` `c_email` VARCHAR(500) NOT NULL DEFAULT '',
CHANGE `allowanonymousaccess` `c_anonaccess` BOOL NOT NULL DEFAULT 0, CHANGE `allowanonymousaccess` `c_anonaccess` BOOL NOT NULL DEFAULT 0,
CHANGE `authprovider` `c_authprovider` CHAR(20) NOT NULL DEFAULT 'documize', CHANGE `authprovider` `c_authprovider` CHAR(20) NOT NULL DEFAULT 'documize',

View file

@ -0,0 +1,5 @@
/* Community Edition */
-- Local aware.
ALTER TABLE dmz_org ADD COLUMN `c_locale` VARCHAR(20) NOT NULL DEFAULT 'en-US';
ALTER TABLE dmz_user ADD COLUMN `c_locale` VARCHAR(20) NOT NULL DEFAULT 'en-US';

View file

@ -228,7 +228,7 @@ CREATE TABLE dmz_org (
c_title varchar(500) COLLATE ucs_basic NOT NULL, c_title varchar(500) COLLATE ucs_basic NOT NULL,
c_message varchar(500) COLLATE ucs_basic NOT NULL, c_message varchar(500) COLLATE ucs_basic NOT NULL,
c_domain varchar(200) COLLATE ucs_basic NOT NULL DEFAULT '', c_domain varchar(200) COLLATE ucs_basic NOT NULL DEFAULT '',
c_service varchar(200) COLLATE ucs_basic NOT NULL DEFAULT 'https://api.documize.com', c_service varchar(200) COLLATE ucs_basic NOT NULL DEFAULT '',
c_email varchar(500) COLLATE ucs_basic NOT NULL DEFAULT '', c_email varchar(500) COLLATE ucs_basic NOT NULL DEFAULT '',
c_anonaccess bool NOT NULL DEFAULT '0', c_anonaccess bool NOT NULL DEFAULT '0',
c_authprovider varchar(20) COLLATE ucs_basic NOT NULL DEFAULT 'documize', c_authprovider varchar(20) COLLATE ucs_basic NOT NULL DEFAULT 'documize',

View file

@ -0,0 +1,5 @@
/* Community Edition */
-- Local aware.
ALTER TABLE dmz_org ADD COLUMN c_locale VARCHAR(20) NOT NULL DEFAULT 'en-US';
ALTER TABLE dmz_user ADD COLUMN c_locale VARCHAR(20) NOT NULL DEFAULT 'en-US';

View file

@ -212,7 +212,7 @@ CREATE TABLE dmz_org (
c_title NVARCHAR(500) COLLATE Latin1_General_CS_AS NOT NULL, c_title NVARCHAR(500) COLLATE Latin1_General_CS_AS NOT NULL,
c_message NVARCHAR(500) COLLATE Latin1_General_CS_AS NOT NULL, c_message NVARCHAR(500) COLLATE Latin1_General_CS_AS NOT NULL,
c_domain NVARCHAR(200) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT '', c_domain NVARCHAR(200) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT '',
c_service NVARCHAR(200) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT 'https://api.documize.com', c_service NVARCHAR(200) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT '',
c_email NVARCHAR(500) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT '', c_email NVARCHAR(500) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT '',
c_anonaccess BIT NOT NULL DEFAULT '0', c_anonaccess BIT NOT NULL DEFAULT '0',
c_authprovider NVARCHAR(20) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT 'documize', c_authprovider NVARCHAR(20) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT 'documize',

View file

@ -0,0 +1,5 @@
/* Community edition */
-- Local aware.
ALTER TABLE dmz_org ADD c_locale NVARCHAR(20) NOT NULL DEFAULT 'en-US';
ALTER TABLE dmz_user ADD c_locale NVARCHAR(20) NOT NULL DEFAULT 'en-US';

View file

@ -0,0 +1,4 @@
/* Community edition */
-- Performance indexes
CREATE INDEX idx_action_5 ON dmz_action (c_orgid,c_userid,c_docid,c_actiontype,c_iscomplete,c_reftype,c_reftypeid);

View file

@ -0,0 +1,8 @@
/* Community edition */
-- Performance indexes
CREATE INDEX idx_action_6 ON dmz_action (c_orgid,c_reftypeid,c_reftype);
CREATE INDEX idx_action_7 ON dmz_action (c_orgid,c_refid);
CREATE INDEX idx_section_5 ON dmz_section (c_orgid,c_refid);

View file

@ -0,0 +1,6 @@
/* Community edition */
-- Performance indexes
CREATE INDEX idx_action_8 ON dmz_action (c_orgid,c_docid);
CREATE INDEX idx_user_3 ON dmz_user (c_refid);

View file

@ -102,7 +102,7 @@
<body> <body>
<div class="container"> <div class="container">
<div class="logo"> <div class="logo">
<img src="/assets/img/setup/logo.png" alt="Documize"> <img src="/assets/img/setup/logo.png" alt="Documize Community">
</div> </div>
<div class="content clearfix"> <div class="content clearfix">
<div class="image"> <div class="image">
@ -110,7 +110,7 @@
</div> </div>
<div class="text"> <div class="text">
<h1>Database Error</h1> <h1>Database Error</h1>
<p>There seems to be a problem with the Documize database: <strong>{{.DBname}}</strong></p> <p>There seems to be a problem with the Documize Community database: <strong>{{.DBname}}</strong></p>
<p><em>{{.Issue}}</em></p> <p><em>{{.Issue}}</em></p>
</div> </div>
</div> </div>

File diff suppressed because one or more lines are too long

2
core/env/flags.go vendored
View file

@ -21,6 +21,7 @@ type Flags struct {
ForceHTTPPort2SSL string // (optional) HTTP that should be redirected to HTTPS ForceHTTPPort2SSL string // (optional) HTTP that should be redirected to HTTPS
SSLCertFile string // (optional) name of SSL certificate PEM file SSLCertFile string // (optional) name of SSL certificate PEM file
SSLKeyFile string // (optional) name of SSL key PEM file SSLKeyFile string // (optional) name of SSL key PEM file
TLSVersion string // (optional) minimum TLS version for SSL connections
SiteMode string // (optional) if 1 then serve offline web page SiteMode string // (optional) if 1 then serve offline web page
Location string // reserved Location string // reserved
ConfigSource string // tells us if configuration info was obtained from command line or config file ConfigSource string // tells us if configuration info was obtained from command line or config file
@ -43,6 +44,7 @@ type httpConfig struct {
ForceSSLPort int ForceSSLPort int
Cert string Cert string
Key string Key string
TLSVersion string
} }
type databaseConfig struct { type databaseConfig struct {

13
core/env/parser.go vendored
View file

@ -84,8 +84,13 @@ func configFile() (f Flags, ok bool) {
f.ForceHTTPPort2SSL = strconv.Itoa(ct.HTTP.ForceSSLPort) f.ForceHTTPPort2SSL = strconv.Itoa(ct.HTTP.ForceSSLPort)
f.SSLCertFile = ct.HTTP.Cert f.SSLCertFile = ct.HTTP.Cert
f.SSLKeyFile = ct.HTTP.Key f.SSLKeyFile = ct.HTTP.Key
f.TLSVersion = ct.HTTP.TLSVersion
f.Location = strings.ToLower(ct.Install.Location) f.Location = strings.ToLower(ct.Install.Location)
if len(f.TLSVersion) == 0 {
f.TLSVersion = "1.3"
}
ok = true ok = true
return return
} }
@ -93,7 +98,7 @@ func configFile() (f Flags, ok bool) {
// commandLineEnv loads command line and OS environment variables required by the program to function. // commandLineEnv loads command line and OS environment variables required by the program to function.
func commandLineEnv() (f Flags, ok bool) { func commandLineEnv() (f Flags, ok bool) {
ok = true ok = true
var dbConn, dbType, jwtKey, siteMode, port, certFile, keyFile, forcePort2SSL, location string var dbConn, dbType, jwtKey, siteMode, port, certFile, keyFile, forcePort2SSL, TLSVersion, location string
// register(&configFile, "salt", false, "the salt string used to encode JWT tokens, if not set a random value will be generated") // register(&configFile, "salt", false, "the salt string used to encode JWT tokens, if not set a random value will be generated")
register(&jwtKey, "salt", false, "the salt string used to encode JWT tokens, if not set a random value will be generated") register(&jwtKey, "salt", false, "the salt string used to encode JWT tokens, if not set a random value will be generated")
@ -101,6 +106,7 @@ func commandLineEnv() (f Flags, ok bool) {
register(&keyFile, "key", false, "the key.pem file used for https") register(&keyFile, "key", false, "the key.pem file used for https")
register(&port, "port", false, "http/https port number") register(&port, "port", false, "http/https port number")
register(&forcePort2SSL, "forcesslport", false, "redirect given http port number to TLS") register(&forcePort2SSL, "forcesslport", false, "redirect given http port number to TLS")
register(&TLSVersion, "tlsversion", false, "select minimum TLS: 1.0, 1.1, 1.2, 1.3")
register(&siteMode, "offline", false, "set to '1' for OFFLINE mode") register(&siteMode, "offline", false, "set to '1' for OFFLINE mode")
register(&dbType, "dbtype", true, "specify the database provider: mysql|percona|mariadb|postgresql|sqlserver") register(&dbType, "dbtype", true, "specify the database provider: mysql|percona|mariadb|postgresql|sqlserver")
register(&dbConn, "db", true, `'database specific connection string for example "user:password@tcp(localhost:3306)/dbname"`) register(&dbConn, "db", true, `'database specific connection string for example "user:password@tcp(localhost:3306)/dbname"`)
@ -118,9 +124,14 @@ func commandLineEnv() (f Flags, ok bool) {
f.SiteMode = siteMode f.SiteMode = siteMode
f.SSLCertFile = certFile f.SSLCertFile = certFile
f.SSLKeyFile = keyFile f.SSLKeyFile = keyFile
f.TLSVersion = TLSVersion
f.Location = strings.ToLower(location) f.Location = strings.ToLower(location)
f.ConfigSource = "flags/environment" f.ConfigSource = "flags/environment"
if len(f.TLSVersion) == 0 {
f.TLSVersion = "1.3"
}
return f, ok return f, ok
} }

91
core/i18n/localize.go Normal file
View file

@ -0,0 +1,91 @@
package i18n
import (
"embed"
"encoding/json"
"fmt"
"strings"
"github.com/documize/community/core/asset"
"github.com/pkg/errors"
)
const (
DefaultLocale = "en-US"
)
var localeMap map[string]map[string]string
// SupportedLocales returns array of locales.
func SupportedLocales() (locales []string) {
locales = append(locales, "en-US")
locales = append(locales, "de-DE")
locales = append(locales, "zh-CN")
locales = append(locales, "pt-BR")
locales = append(locales, "fr-FR")
locales = append(locales, "ja-JP")
locales = append(locales, "it-IT")
locales = append(locales, "es-AR")
return
}
// Intialize will load language files
func Initialize(e embed.FS) (err error) {
localeMap = make(map[string]map[string]string)
locales := SupportedLocales()
for i := range locales {
content, _, err := asset.FetchStatic(e, "i18n/"+locales[i]+".json")
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("missing locale %s", locales[i]))
return err
}
var payload interface{}
json.Unmarshal([]byte(content), &payload)
m := payload.(map[string]interface{})
translations := make(map[string]string)
for j := range m {
translations[j] = m[j].(string)
}
localeMap[locales[i]] = translations
}
return nil
}
// Localize will returns string value for given key using specified locale).
// e.g. locale = "en-US", key = "admin_billing"
//
// Replacements are for replacing string placeholders ({1} {2} {3}) with
// replacement text.
// e.g. "This is {1} example" --> replacements[0] will replace {1}
func Localize(locale string, key string, replacements ...string) (s string) {
l, ok := localeMap[locale]
if !ok {
// fallback
l = localeMap[DefaultLocale]
}
s, ok = l[key]
if !ok {
// missing translation key is echo'ed back
s = fmt.Sprintf("!! %s !!", key)
}
// placeholders are one-based: {1} {2} {3}
// replacements array is zero-based hence the +1 below
if len(replacements) > 0 {
for i := range replacements {
s = strings.Replace(s, fmt.Sprintf("{%d}", i+1), replacements[i], 1)
}
}
return
}

View file

@ -38,7 +38,7 @@ func CommandWithTimeout(command *exec.Cmd, timeout time.Duration) ([]byte, error
select { select {
case <-time.After(timeout): case <-time.After(timeout):
if err := command.Process.Kill(); err != nil { if err := command.Process.Kill(); err != nil {
fmt.Errorf("failed to kill: ", err) fmt.Printf("failed to kill: %s", err.Error())
} }
<-done // prevent memory leak <-done // prevent memory leak
//fmt.Println("DEBUG timeout") //fmt.Println("DEBUG timeout")

View file

@ -114,3 +114,17 @@ func WriteJSON(w http.ResponseWriter, v interface{}) {
j, _ := json.Marshal(v) j, _ := json.Marshal(v)
w.Write(j) w.Write(j)
} }
// WriteText to HTTP response
func WriteText(w http.ResponseWriter, data []byte) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write(data)
}
// WriteXML to HTTP response
func WriteXML(w http.ResponseWriter, data []byte) {
w.Header().Set("Content-Type", "application/xml; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write(data)
}

View file

@ -1,6 +1,6 @@
# This Docker Compose file will start up Documize with PostgreSQL. # This Docker Compose file will start up Documize with PostgreSQL.
# #
# Use 'documize-enterprise-linux-amd64' for Enterprise Edition (default). # Use 'documize-community-plus-linux-amd64' for Community+ Edition (default).
# Use 'documize-community-linux-amd64' for Community Edition. # Use 'documize-community-linux-amd64' for Community Edition.
# #
# You can move between editions anytime without any data loss # You can move between editions anytime without any data loss
@ -29,7 +29,7 @@ services:
app: app:
image: debian:latest image: debian:latest
command: /bin/sh -c "apt-get -qq update && apt-get -qq install -y wget && wget https://documize.s3-eu-west-1.amazonaws.com/downloads/documize-community-plus-linux-amd64 && chmod 777 ./documize-community-plus-linux-amd64 && ./documize-community-plus-linux-amd64" command: /bin/sh -c "apt-get -qq update && apt-get -qq install -y wget && wget https://community-downloads.s3.us-east-2.amazonaws.com/documize-community-plus-linux-amd64 && chmod 777 ./documize-community-plus-linux-amd64 && ./documize-community-plus-linux-amd64"
depends_on: depends_on:
- db - db
ports: ports:

View file

@ -13,7 +13,6 @@ package activity
import ( import (
"database/sql" "database/sql"
"fmt"
"time" "time"
"github.com/documize/community/domain" "github.com/documize/community/domain"
@ -77,8 +76,10 @@ func (s Store) GetDocumentActivity(ctx domain.RequestContext, id string) (a []ac
// DeleteDocumentChangeActivity removes all entries for document changes (add, remove, update). // DeleteDocumentChangeActivity removes all entries for document changes (add, remove, update).
func (s Store) DeleteDocumentChangeActivity(ctx domain.RequestContext, documentID string) (rows int64, err error) { func (s Store) DeleteDocumentChangeActivity(ctx domain.RequestContext, documentID string) (rows int64, err error) {
rows, err = s.DeleteWhere(ctx.Transaction, _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_user_activity WHERE c_orgid=? AND c_docid=? AND (c_activitytype=1 OR c_activitytype=2 OR c_activitytype=3 OR c_activitytype=4 OR c_activitytype=7)"), ctx.OrgID, documentID)
fmt.Sprintf("DELETE FROM dmz_user_activity WHERE c_orgid='%s' AND c_docid='%s' AND (c_activitytype=1 OR c_activitytype=2 OR c_activitytype=3 OR c_activitytype=4 OR c_activitytype=7)", ctx.OrgID, documentID)) if err == sql.ErrNoRows {
err = nil
}
return return
} }

View file

@ -13,7 +13,6 @@ package attachment
import ( import (
"database/sql" "database/sql"
"fmt"
"strings" "strings"
"time" "time"
@ -147,8 +146,10 @@ func (s Store) Delete(ctx domain.RequestContext, id string) (rows int64, err err
// DeleteSection removes all attachments agasinst a section. // DeleteSection removes all attachments agasinst a section.
func (s Store) DeleteSection(ctx domain.RequestContext, sectionID string) (rows int64, err error) { func (s Store) DeleteSection(ctx domain.RequestContext, sectionID string) (rows int64, err error) {
rows, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_attachment WHERE c_orgid='%s' AND c_sectionid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_attachment WHERE c_orgid=? AND c_sectionid=?"), ctx.OrgID, sectionID)
ctx.OrgID, sectionID)) if err == sql.ErrNoRows {
err = nil
}
return return
} }

View file

@ -48,6 +48,7 @@ func AddExternalUser(ctx domain.RequestContext, rt *env.Runtime, store *store.St
if addUser { if addUser {
userID = uniqueid.Generate() userID = uniqueid.Generate()
u.RefID = userID u.RefID = userID
u.Locale = ctx.OrgLocale
err = store.User.Add(ctx, u) err = store.User.Add(ctx, u)
if err != nil { if err != nil {

View file

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
@ -57,7 +58,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Org contains raw auth provider config // Org contains raw auth provider config
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID) org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil { if err != nil {
result.Message = "Error: unable to get organization record" result.Message = i18n.Localize(ctx.Locale, "server_err_org")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -66,7 +67,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Exit if not using Keycloak // Exit if not using Keycloak
if org.AuthProvider != ath.AuthProviderKeycloak { if org.AuthProvider != ath.AuthProviderKeycloak {
result.Message = "Error: skipping user sync with Keycloak as it is not the configured option" result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error1")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)
@ -77,7 +78,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
c := ath.KeycloakConfig{} c := ath.KeycloakConfig{}
err = json.Unmarshal([]byte(org.AuthConfig), &c) err = json.Unmarshal([]byte(org.AuthConfig), &c)
if err != nil { if err != nil {
result.Message = "Error: unable read Keycloak configuration data" result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error2")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -87,7 +88,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// User list from Keycloak // User list from Keycloak
kcUsers, err := Fetch(c) kcUsers, err := Fetch(c)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Keycloak users: " + err.Error() result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error3", err.Error())
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -97,7 +98,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// User list from Documize // User list from Documize
dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999) dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Documize users" result.Message = i18n.Localize(ctx.Locale, "server_error_user")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -135,8 +136,8 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
} }
} }
result.Message = fmt.Sprintf("Keycloak sync found %d users, %d new users added, %d users with missing data ignored", result.Message = i18n.Localize(ctx.Locale, "server_keycloak_summary",
len(kcUsers), len(insert), missing) fmt.Sprintf("%d", len(kcUsers)), fmt.Sprintf("%d", len(insert)), fmt.Sprintf("%d", missing))
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)

View file

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
@ -146,7 +147,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Org contains raw auth provider config // Org contains raw auth provider config
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID) org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil { if err != nil {
result.Message = "Error: unable to get organization record" result.Message = i18n.Localize(ctx.Locale, "server_error_org")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -155,7 +156,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Exit if not using LDAP // Exit if not using LDAP
if org.AuthProvider != ath.AuthProviderLDAP { if org.AuthProvider != ath.AuthProviderLDAP {
result.Message = "Error: skipping user sync with LDAP as it is not the configured option" result.Message = i18n.Localize(ctx.Locale, "server_ldap_error1")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)
@ -166,7 +167,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
c := lm.LDAPConfig{} c := lm.LDAPConfig{}
err = json.Unmarshal([]byte(org.AuthConfig), &c) err = json.Unmarshal([]byte(org.AuthConfig), &c)
if err != nil { if err != nil {
result.Message = "Error: unable read LDAP configuration data" result.Message = i18n.Localize(ctx.Locale, "server_ldap_error2")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -176,7 +177,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Get user list from LDAP. // Get user list from LDAP.
ldapUsers, err := fetchUsers(c) ldapUsers, err := fetchUsers(c)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch LDAP users: " + err.Error() result.Message = i18n.Localize(ctx.Locale, "server_ldap_error3", err.Error())
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -186,7 +187,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Get user list from Documize // Get user list from Documize
dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999) dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Documize users" result.Message = i18n.Localize(ctx.Locale, "server_error_user")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -223,10 +224,8 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
} }
result.IsError = false result.IsError = false
result.Message = "Sync complete with LDAP server" result.Message = i18n.Localize(ctx.Locale, "server_ldap_complete")
result.Message = fmt.Sprintf( result.Message = i18n.Localize(ctx.Locale, "server_ldap_summary", fmt.Sprintf("%d", len(ldapUsers)), fmt.Sprintf("%d", len(insert)), fmt.Sprintf("%d", missing))
"LDAP sync found %d users, %d new users added, %d users with missing data ignored",
len(ldapUsers), len(insert), missing)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)

View file

@ -244,7 +244,7 @@ func (b backerHandler) dmzOrg(files *[]backupItem) (err error) {
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider, c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_sub,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS subscription, coalesce(c_sub,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS subscription,
coalesce(c_authconfig,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS authconfig, c_maxtags AS maxtags, coalesce(c_authconfig,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS authconfig, c_maxtags AS maxtags,
c_theme AS theme, c_logo AS logo, c_created AS created, c_revised AS revised c_theme AS theme, c_logo AS logo, c_locale as locale, c_created AS created, c_revised AS revised
FROM dmz_org`+w) FROM dmz_org`+w)
if err != nil { if err != nil {
return return
@ -308,7 +308,7 @@ func (b backerHandler) dmzUserAccount(files *[]backupItem) (err error) {
err = b.Runtime.Db.Select(&u, `SELECT u.id, u.c_refid AS refid, err = b.Runtime.Db.Select(&u, `SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u`+w) FROM dmz_user u`+w)
if err != nil { if err != nil {

View file

@ -370,14 +370,14 @@ func (r *restoreHandler) dmzOrg() (err error) {
INSERT INTO dmz_org (c_refid, c_company, c_title, c_message, INSERT INTO dmz_org (c_refid, c_company, c_title, c_message,
c_domain, c_service, c_email, c_anonaccess, c_authprovider, c_authconfig, c_domain, c_service, c_email, c_anonaccess, c_authprovider, c_authconfig,
c_maxtags, c_verified, c_serial, c_sub, c_active, c_maxtags, c_verified, c_serial, c_sub, c_active,
c_theme, c_logo, c_created, c_revised) c_theme, c_logo, c_locale, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
org[i].RefID, org[i].Company, org[i].Title, org[i].Message, org[i].RefID, org[i].Company, org[i].Title, org[i].Message,
strings.ToLower(org[i].Domain), org[i].ConversionEndpoint, strings.ToLower(org[i].Email), strings.ToLower(org[i].Domain), org[i].ConversionEndpoint, strings.ToLower(org[i].Email),
org[i].AllowAnonymousAccess, org[i].AuthProvider, org[i].AuthConfig, org[i].AllowAnonymousAccess, org[i].AuthProvider, org[i].AuthConfig,
org[i].MaxTags, r.Runtime.StoreProvider.IsTrue(), org[i].Serial, org[i].MaxTags, r.Runtime.StoreProvider.IsTrue(), org[i].Serial,
org[i].Subscription, org[i].Active, org[i].Subscription, org[i].Active,
org[i].Theme, org[i].Logo, org[i].Theme, org[i].Logo, org[i].Locale,
org[i].Created, org[i].Revised) org[i].Created, org[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -412,6 +412,7 @@ func (r *restoreHandler) dmzOrg() (err error) {
org[0].Title = r.Spec.Org.Title org[0].Title = r.Spec.Org.Title
org[0].Subscription = r.Spec.Org.Subscription org[0].Subscription = r.Spec.Org.Subscription
org[0].Theme = r.Spec.Org.Theme org[0].Theme = r.Spec.Org.Theme
org[0].Locale = r.Spec.Org.Locale
} }
_, err = r.Context.Transaction.NamedExec(`UPDATE dmz_org SET _, err = r.Context.Transaction.NamedExec(`UPDATE dmz_org SET
@ -425,7 +426,8 @@ func (r *restoreHandler) dmzOrg() (err error) {
c_message=:message, c_message=:message,
c_title=:title, c_title=:title,
c_serial=:serial, c_serial=:serial,
c_sub=:subscription c_sub=:subscription,
c_locale=:locale
WHERE c_refid=:refid`, &org[0]) WHERE c_refid=:refid`, &org[0])
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -1735,11 +1737,11 @@ func (r *restoreHandler) dmzUser() (err error) {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(` _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
INSERT INTO dmz_user INSERT INTO dmz_user
(c_refid, c_firstname, c_lastname, c_email, c_initials, c_globaladmin, (c_refid, c_firstname, c_lastname, c_email, c_initials, c_globaladmin,
c_password, c_salt, c_reset, c_active, c_lastversion, c_created, c_revised) c_password, c_salt, c_reset, c_active, c_lastversion, c_locale, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
r.remapUser(u[i].RefID), u[i].Firstname, u[i].Lastname, strings.ToLower(u[i].Email), u[i].Initials, r.remapUser(u[i].RefID), u[i].Firstname, u[i].Lastname, strings.ToLower(u[i].Email), u[i].Initials,
u[i].GlobalAdmin, u[i].Password, u[i].Salt, u[i].Reset, u[i].Active, u[i].GlobalAdmin, u[i].Password, u[i].Salt, u[i].Reset, u[i].Active,
u[i].LastVersion, u[i].Created, u[i].Revised) u[i].LastVersion, u[i].Locale, u[i].Created, u[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()

View file

@ -176,46 +176,69 @@ func (s Store) AssociateDocument(ctx domain.RequestContext, m category.Member) (
// DisassociateDocument removes document associatation from category. // DisassociateDocument removes document associatation from category.
func (s Store) DisassociateDocument(ctx domain.RequestContext, categoryID, documentID string) (rows int64, err error) { func (s Store) DisassociateDocument(ctx domain.RequestContext, categoryID, documentID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_category_member WHERE c_orgid='%s' AND c_categoryid='%s' AND c_docid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_category_member WHERE c_orgid=? AND c_categoryid=? AND c_docid=?"),
ctx.OrgID, categoryID, documentID) ctx.OrgID, categoryID, documentID)
return s.DeleteWhere(ctx.Transaction, sql) if err == sql.ErrNoRows {
err = nil
}
return
} }
// RemoveCategoryMembership removes all category associations from the store. // RemoveCategoryMembership removes all category associations from the store.
func (s Store) RemoveCategoryMembership(ctx domain.RequestContext, categoryID string) (rows int64, err error) { func (s Store) RemoveCategoryMembership(ctx domain.RequestContext, categoryID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_category_member WHERE c_orgid='%s' AND c_categoryid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_category_member WHERE c_orgid=? AND c_categoryid=?"),
ctx.OrgID, categoryID) ctx.OrgID, categoryID)
return s.DeleteWhere(ctx.Transaction, sql) if err == sql.ErrNoRows {
err = nil
}
return
} }
// RemoveSpaceCategoryMemberships removes all category associations from the store for the space. // RemoveSpaceCategoryMemberships removes all category associations from the store for the space.
func (s Store) RemoveSpaceCategoryMemberships(ctx domain.RequestContext, spaceID string) (rows int64, err error) { func (s Store) RemoveSpaceCategoryMemberships(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_category_member WHERE c_orgid='%s' AND c_spaceid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_category_member WHERE c_orgid=? AND c_spaceid=?"),
ctx.OrgID, spaceID) ctx.OrgID, spaceID)
return s.DeleteWhere(ctx.Transaction, sql) if err == sql.ErrNoRows {
err = nil
}
return
} }
// RemoveDocumentCategories removes all document category associations from the store. // RemoveDocumentCategories removes all document category associations from the store.
func (s Store) RemoveDocumentCategories(ctx domain.RequestContext, documentID string) (rows int64, err error) { func (s Store) RemoveDocumentCategories(ctx domain.RequestContext, documentID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_category_member WHERE c_orgid='%s' AND c_docid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_category_member WHERE c_orgid=? AND c_docid=?"),
ctx.OrgID, documentID) ctx.OrgID, documentID)
return s.DeleteWhere(ctx.Transaction, sql) if err == sql.ErrNoRows {
err = nil
}
return
} }
// DeleteBySpace removes all category and category associations for given space. // DeleteBySpace removes all category and category associations for given space.
func (s Store) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) { func (s Store) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
s1 := fmt.Sprintf("DELETE FROM dmz_category_member WHERE c_orgid='%s' AND c_spaceid='%s'", ctx.OrgID, spaceID) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_category_member WHERE c_orgid=? AND c_spaceid=?"),
_, err = s.DeleteWhere(ctx.Transaction, s1) ctx.OrgID, spaceID)
if err != nil {
return if err == sql.ErrNoRows {
err = nil
} }
s2 := fmt.Sprintf("DELETE FROM dmz_category WHERE c_orgid='%s' AND c_spaceid='%s'", ctx.OrgID, spaceID) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_category WHERE c_orgid=? AND c_spaceid=?"),
return s.DeleteWhere(ctx.Transaction, s2) ctx.OrgID, spaceID)
if err == sql.ErrNoRows {
err = nil
}
return
} }
// GetSpaceCategorySummary returns number of documents and users for space categories. // GetSpaceCategorySummary returns number of documents and users for space categories.

View file

@ -44,6 +44,8 @@ type RequestContext struct {
GlobalAdmin bool GlobalAdmin bool
ViewUsers bool ViewUsers bool
Subscription Subscription Subscription Subscription
Locale string
OrgLocale string
} }
//GetAppURL returns full HTTP url for the app //GetAppURL returns full HTTP url for the app

View file

@ -176,6 +176,7 @@ func processDocument(ctx domain.RequestContext, r *env.Runtime, store *store.Sto
document.UserID = ctx.UserID document.UserID = ctx.UserID
documentID := uniqueid.Generate() documentID := uniqueid.Generate()
document.RefID = documentID document.RefID = documentID
document.Sequence = doc.Unsequenced
if r.Product.Edition == domain.CommunityEdition { if r.Product.Edition == domain.CommunityEdition {
document.Lifecycle = workflow.LifecycleLive document.Lifecycle = workflow.LifecycleLive

View file

@ -66,6 +66,8 @@ func FilterCategoryProtected(docs []doc.Document, cats []category.Category, memb
// CopyDocument clones an existing document // CopyDocument clones an existing document
func CopyDocument(ctx domain.RequestContext, s store.Store, documentID string) (newDocumentID string, err error) { func CopyDocument(ctx domain.RequestContext, s store.Store, documentID string) (newDocumentID string, err error) {
unseq := doc.Unsequenced
doc, err := s.Document.Get(ctx, documentID) doc, err := s.Document.Get(ctx, documentID)
if err != nil { if err != nil {
err = errors.Wrap(err, "unable to fetch existing document") err = errors.Wrap(err, "unable to fetch existing document")
@ -79,6 +81,7 @@ func CopyDocument(ctx domain.RequestContext, s store.Store, documentID string) (
doc.VersionID = "" doc.VersionID = ""
doc.GroupID = "" doc.GroupID = ""
doc.Template = false doc.Template = false
doc.Sequence = unseq
// Duplicate pages and associated meta // Duplicate pages and associated meta
pages, err := s.Page.GetPages(ctx, documentID) pages, err := s.Page.GetPages(ctx, documentID)

View file

@ -43,7 +43,6 @@ import (
"github.com/documize/community/model/space" "github.com/documize/community/model/space"
"github.com/documize/community/model/user" "github.com/documize/community/model/user"
"github.com/documize/community/model/workflow" "github.com/documize/community/model/workflow"
"github.com/microcosm-cc/bluemonday"
) )
// Handler contains the runtime information such as logging and database. // Handler contains the runtime information such as logging and database.
@ -65,6 +64,13 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
return return
} }
var ok bool
ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted)
if !ok {
h.Runtime.Log.Info("unable to start transaction " + method)
return
}
document, err := h.Store.Document.Get(ctx, id) document, err := h.Store.Document.Get(ctx, id)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, id) response.WriteNotFoundError(w, method, id)
@ -83,7 +89,6 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
// draft mode does not record document views // draft mode does not record document views
if document.Lifecycle == workflow.LifecycleLive { if document.Lifecycle == workflow.LifecycleLive {
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err) h.Runtime.Log.Error(method, err)
@ -96,9 +101,10 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
SourceType: activity.SourceTypeDocument, SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypeRead}) ActivityType: activity.TypeRead})
ctx.Transaction.Commit()
} }
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeDocumentView) h.Store.Audit.Record(ctx, audit.EventTypeDocumentView)
response.WriteJSON(w, document) response.WriteJSON(w, document)
@ -271,8 +277,8 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
} }
} }
d.Name = bluemonday.StrictPolicy().Sanitize(d.Name) // d.Name = bluemonday.StrictPolicy().Sanitize(d.Name)
d.Excerpt = bluemonday.StrictPolicy().Sanitize(d.Excerpt) // d.Excerpt = bluemonday.StrictPolicy().Sanitize(d.Excerpt)
err = h.Store.Document.Update(ctx, d) err = h.Store.Document.Update(ctx, d)
if err != nil { if err != nil {
@ -361,6 +367,13 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
return return
} }
var ok bool
ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted)
if !ok {
h.Runtime.Log.Info("unable to start transaction " + method)
return
}
doc, err := h.Store.Document.Get(ctx, documentID) doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
@ -397,13 +410,6 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
return return
} }
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Document.Delete(ctx, documentID) _, err = h.Store.Document.Delete(ctx, documentID)
if err != nil { if err != nil {
ctx.Transaction.Rollback() ctx.Transaction.Rollback()
@ -561,6 +567,13 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
return return
} }
var ok bool
ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted)
if !ok {
h.Runtime.Log.Info("unable to start transaction " + method)
return
}
document, err := h.Store.Document.Get(ctx, id) document, err := h.Store.Document.Get(ctx, id)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, id) response.WriteNotFoundError(w, method, id)
@ -668,23 +681,22 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
// Get version information for this document. // Get version information for this document.
v := []doc.Version{} v := []doc.Version{}
if len(document.GroupID) > 0 { if len(document.GroupID) > 0 {
// Get versions. // Get versions
vt, err := h.Store.Document.GetVersions(ctx, document.GroupID) vt, err := h.Store.Document.GetVersions(ctx, document.GroupID)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err) h.Runtime.Log.Error(method, err)
return return
} }
// What about draft document versions? // Determine which document versions user can see.
if record.DocumentLifecycle {
// We can see and manage document lifecycle so take all versions.
v = vt
} else {
// Only send back LIVE content because user cannot drafts.
for i := range vt { for i := range vt {
// Everyone can see live documents
if vt[i].Lifecycle == workflow.LifecycleLive { if vt[i].Lifecycle == workflow.LifecycleLive {
v = append(v, vt[i]) v = append(v, vt[i])
} }
// Only lifecycle admins can see draft documents
if vt[i].Lifecycle == workflow.LifecycleDraft && record.DocumentLifecycle {
v = append(v, vt[i])
} }
} }
} }
@ -710,13 +722,6 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
data.Versions = v data.Versions = v
data.Attachments = a data.Attachments = a
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if document.Lifecycle == workflow.LifecycleLive { if document.Lifecycle == workflow.LifecycleLive {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{ h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: document.SpaceID, SpaceID: document.SpaceID,
@ -825,10 +830,10 @@ func (h *Handler) Duplicate(w http.ResponseWriter, r *http.Request) {
return return
} }
ctx.Transaction, err = h.Runtime.Db.Beginx() var ok bool
if err != nil { ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted)
response.WriteServerError(w, method, err) if !ok {
h.Runtime.Log.Error(method, err) h.Runtime.Log.Info("unable to start transaction " + method)
return return
} }

View file

@ -254,31 +254,11 @@ func (s Store) MoveActivity(ctx domain.RequestContext, documentID, oldSpaceID, n
// Delete removes the specified document. // Delete removes the specified document.
// Remove document pages, revisions, attachments, updates the search subsystem. // Remove document pages, revisions, attachments, updates the search subsystem.
func (s Store) Delete(ctx domain.RequestContext, documentID string) (rows int64, err error) { func (s Store) Delete(ctx domain.RequestContext, documentID string) (rows int64, err error) {
rows, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_section WHERE c_docid='%s' AND c_orgid='%s'", documentID, ctx.OrgID)) ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_section WHERE c_orgid=? AND c_docid=?"), ctx.OrgID, documentID)
ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_section_revision WHERE c_orgid=? AND c_docid=?"), ctx.OrgID, documentID)
if err != nil { ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_attachment WHERE c_orgid=? AND c_docid=?"), ctx.OrgID, documentID)
return ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_category_member WHERE c_orgid=? AND c_docid=?"), ctx.OrgID, documentID)
} ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_vote WHERE c_orgid=? AND c_docid=?"), ctx.OrgID, documentID)
_, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_section_revision WHERE c_docid='%s' AND c_orgid='%s'", documentID, ctx.OrgID))
if err != nil {
return
}
_, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_attachment WHERE c_docid='%s' AND c_orgid='%s'", documentID, ctx.OrgID))
if err != nil {
return
}
_, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_category_member WHERE c_docid='%s' AND c_orgid='%s'", documentID, ctx.OrgID))
if err != nil {
return
}
_, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_vote WHERE c_docid='%s' AND c_orgid='%s'", documentID, ctx.OrgID))
if err != nil {
return
}
return s.DeleteConstrained(ctx.Transaction, "dmz_doc", ctx.OrgID, documentID) return s.DeleteConstrained(ctx.Transaction, "dmz_doc", ctx.OrgID, documentID)
} }
@ -286,25 +266,10 @@ func (s Store) Delete(ctx domain.RequestContext, documentID string) (rows int64,
// DeleteBySpace removes all documents for given space. // DeleteBySpace removes all documents for given space.
// Remove document pages, revisions, attachments, updates the search subsystem. // Remove document pages, revisions, attachments, updates the search subsystem.
func (s Store) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) { func (s Store) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
rows, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_section WHERE c_docid IN (SELECT c_refid FROM dmz_doc WHERE c_spaceid='%s' AND c_orgid='%s')", spaceID, ctx.OrgID)) ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_section WHERE c_docid IN (SELECT c_refid FROM dmz_doc WHERE c_spaceid=? AND c_orgid=?)"), spaceID, ctx.OrgID)
if err != nil { ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_section_revision WHERE c_docid IN (SELECT c_refid FROM dmz_doc WHERE c_spaceid=? AND c_orgid=?)"), spaceID, ctx.OrgID)
return ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_attachment WHERE c_docid IN (SELECT c_refid FROM dmz_doc WHERE c_spaceid=? AND c_orgid=?)"), spaceID, ctx.OrgID)
} ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_vote WHERE c_docid IN (SELECT c_refid FROM dmz_doc WHERE c_spaceid=? AND c_orgid=?)"), spaceID, ctx.OrgID)
_, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_section_revision WHERE c_docid IN (SELECT c_refid FROM dmz_doc WHERE c_spaceid='%s' AND c_orgid='%s')", spaceID, ctx.OrgID))
if err != nil {
return
}
_, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_attachment WHERE c_docid IN (SELECT c_refid FROM dmz_doc WHERE c_spaceid='%s' AND c_orgid='%s')", spaceID, ctx.OrgID))
if err != nil {
return
}
_, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_vote WHERE c_docid IN (SELECT c_refid FROM dmz_doc WHERE c_spaceid='%s' AND c_orgid='%s')", spaceID, ctx.OrgID))
if err != nil {
return
}
return s.DeleteConstrained(ctx.Transaction, "dmz_doc", ctx.OrgID, spaceID) return s.DeleteConstrained(ctx.Transaction, "dmz_doc", ctx.OrgID, spaceID)
} }

View file

@ -13,7 +13,6 @@ package group
import ( import (
"database/sql" "database/sql"
"fmt"
"time" "time"
"github.com/documize/community/domain" "github.com/documize/community/domain"
@ -104,7 +103,10 @@ func (s Store) Delete(ctx domain.RequestContext, refID string) (rows int64, err
if err != nil { if err != nil {
return return
} }
return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_group_member WHERE c_orgid='%s' AND c_groupid='%s'", ctx.OrgID, refID))
ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_group_member WHERE c_orgid=? AND c_groupid=?"), ctx.OrgID, refID)
return
} }
// GetGroupMembers returns all user associated with given group. // GetGroupMembers returns all user associated with given group.
@ -143,15 +145,8 @@ func (s Store) JoinGroup(ctx domain.RequestContext, groupID, userID string) (err
// LeaveGroup removes user from group. // LeaveGroup removes user from group.
func (s Store) LeaveGroup(ctx domain.RequestContext, groupID, userID string) (err error) { func (s Store) LeaveGroup(ctx domain.RequestContext, groupID, userID string) (err error) {
_, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_group_member WHERE c_orgid='%s' AND c_groupid='%s' AND c_userid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_group_member WHERE c_orgid=? AND c_groupid=? AND c_userid=?"),
ctx.OrgID, groupID, userID)) ctx.OrgID, groupID, userID)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
err = errors.Wrap(err, "clear group member")
}
return return
} }
@ -182,16 +177,8 @@ func (s Store) GetMembers(ctx domain.RequestContext) (r []group.Record, err erro
// RemoveUserGroups remove user from all group. // RemoveUserGroups remove user from all group.
func (s Store) RemoveUserGroups(ctx domain.RequestContext, userID string) (err error) { func (s Store) RemoveUserGroups(ctx domain.RequestContext, userID string) (err error) {
_, err = s.DeleteWhere(ctx.Transaction, _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_group_member WHERE c_orgid=? AND c_userid=?"),
fmt.Sprintf("DELETE FROM dmz_group_member WHERE c_orgid='%s' AND c_userid='%s'", ctx.OrgID, userID)
ctx.OrgID, userID))
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
err = errors.Wrap(err, "RemoveUserGroups")
}
return return
} }

View file

@ -13,7 +13,6 @@ package link
import ( import (
"database/sql" "database/sql"
"fmt"
"strings" "strings"
"time" "time"
@ -156,12 +155,18 @@ func (s Store) MarkOrphanAttachmentLink(ctx domain.RequestContext, attachmentID
// DeleteSourcePageLinks removes saved links for given source. // DeleteSourcePageLinks removes saved links for given source.
func (s Store) DeleteSourcePageLinks(ctx domain.RequestContext, pageID string) (rows int64, err error) { func (s Store) DeleteSourcePageLinks(ctx domain.RequestContext, pageID string) (rows int64, err error) {
return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_link WHERE c_orgid='%s' AND c_sourcesectionid='%s'", ctx.OrgID, pageID)) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_link WHERE c_orgid=? AND c_sourcesectionid=?"),
ctx.OrgID, pageID)
return
} }
// DeleteSourceDocumentLinks removes saved links for given document. // DeleteSourceDocumentLinks removes saved links for given document.
func (s Store) DeleteSourceDocumentLinks(ctx domain.RequestContext, documentID string) (rows int64, err error) { func (s Store) DeleteSourceDocumentLinks(ctx domain.RequestContext, documentID string) (rows int64, err error) {
return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_link WHERE c_orgid='%s' AND c_sourcedocid='%s'", ctx.OrgID, documentID)) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_link WHERE c_orgid=? AND c_sourcedocid=?"),
ctx.OrgID, documentID)
return
} }
// DeleteLink removes saved link from the store. // DeleteLink removes saved link from the store.

View file

@ -61,7 +61,7 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
Document Approval Role Granted {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
@ -69,19 +69,14 @@ background-color: #f6f6f6;
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<p>You are requested to approve all changes to the following document:</p> <p>{{.ActionText}}</p>
<p style="font-weight: bold;">{{.Document}}</p> <p style="font-weight: bold;">{{.Document}}</p>
<p>{{.Inviter}}</p> <p>{{.Inviter}}</p>
</td> </td>1
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">View document</a> <a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">{{.ClickHere}}</a>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -16,6 +16,7 @@ package mail
import ( import (
"fmt" "fmt"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain/smtp" "github.com/documize/community/domain/smtp"
) )
@ -26,32 +27,32 @@ func (m *Mailer) DocumentApprover(recipient, inviterName, inviterEmail, url, doc
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has granted you document approval", inviterName) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_approval", inviterName)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
URL string URL string
Document string Document string
SenderEmail string SenderEmail string
ActionText string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
url, url,
document, document,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_approval_explain"),
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/document-approver.html", parameters) html, err := m.ParseTemplate("mail/document-approver.html", parameters)

View file

@ -1,109 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Your Documize Community Invitation</title>
<style type="text/css">
img {
max-width: 100%;
}
body {
-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6;
}
body {
background-color: #f6f6f6;
}
@media only screen and (max-width: 640px) {
h1 {
font-weight: 600 !important; margin: 20px 0 5px !important;
}
h2 {
font-weight: 600 !important; margin: 20px 0 5px !important;
}
h3 {
font-weight: 600 !important; margin: 20px 0 5px !important;
}
h4 {
font-weight: 600 !important; margin: 20px 0 5px !important;
}
h1 {
font-size: 22px !important;
}
h2 {
font-size: 18px !important;
}
h3 {
font-size: 16px !important;
}
.container {
width: 100% !important;
}
.content {
padding: 10px !important;
}
.content-wrap {
padding: 10px !important;
}
.invoice {
width: 100% !important;
}
}
</style>
</head>
<body style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6; background: #f6f6f6; margin: 0; padding: 0;">
<table class="body-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background: #f6f6f6; margin: 0; padding: 0;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0;" valign="top"></td>
<td class="container" width="600" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto; padding: 0;" valign="top">
<div class="content" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has invited you to use Documize Community
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize Community provides easy access to all your Word documents so everyone can find and edit documents - no more network drives.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<strong style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">Your co-workers are using Documize right now.</strong>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Use your email address as your password ({{.Email}}).
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<a href="{{.Url}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</td>
<td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0;" valign="top"></td>
</tr>
</table>
</body>
</html>

View file

@ -59,25 +59,15 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has invited you to their Documize Community account {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> <td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize Community provides secure and easy access to all your documentation so everyone can find and edit the same thing.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a> <a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">{{.ClickHere}}</a>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -59,35 +59,20 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has invited you to Documize Community {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> <td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize Community provides secure and easy access to all your documentation so everyone can find and edit the same thing.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<strong style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">Your co-workers are using Documize right now.</strong> {{.Password}}
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Your temporary password: {{.Password}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a> <a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">{{.ClickHere}}</a>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -61,30 +61,15 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
Your Documize Community password reset request {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> <td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Someone has requested to reset your Documize Community password. If this was you, then please click below to specify a new password.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<strong style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">If you did not request a password reset, please change your password and contact us.</strong>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to reset your password</a> <a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">{{.ClickHere}}</a>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -61,7 +61,7 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has shared {{.Folder}} with you {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
@ -75,12 +75,7 @@ background-color: #f6f6f6;
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Login to Documize</a> <a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">{{.ClickHere}}</a>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -61,7 +61,7 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has shared {{.Folder}} with you on Documize Community {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
@ -74,19 +74,9 @@ background-color: #f6f6f6;
</strong> </strong>
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize Community provides secure and easy access to all your documentation so everyone can find and edit the same thing.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Go to Documize</a> <a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">{{.ClickHere}}</a>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:team@documize.com" style="color: #7a8184;">Contact Documize</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -14,6 +14,7 @@ package mail
import ( import (
"fmt" "fmt"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain/smtp" "github.com/documize/community/domain/smtp"
) )
@ -24,20 +25,16 @@ func (m *Mailer) ShareSpaceExistingUser(recipient, inviterName, inviterEmail, ur
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has shared %s with you", inviterName, folder) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_shared", inviterName, folder)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
@ -45,6 +42,7 @@ func (m *Mailer) ShareSpaceExistingUser(recipient, inviterName, inviterEmail, ur
Folder string Folder string
Intro string Intro string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
@ -52,6 +50,7 @@ func (m *Mailer) ShareSpaceExistingUser(recipient, inviterName, inviterEmail, ur
folder, folder,
intro, intro,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/share-space-existing-user.html", parameters) html, err := m.ParseTemplate("mail/share-space-existing-user.html", parameters)
@ -77,20 +76,16 @@ func (m *Mailer) ShareSpaceNewUser(recipient, inviterName, inviterEmail, url, sp
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has shared %s with you on Documize Community", inviterName, space) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_invited", inviterName, space)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
@ -98,6 +93,7 @@ func (m *Mailer) ShareSpaceNewUser(recipient, inviterName, inviterEmail, url, sp
Invitation string Invitation string
Folder string Folder string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
@ -105,6 +101,7 @@ func (m *Mailer) ShareSpaceNewUser(recipient, inviterName, inviterEmail, url, sp
invitationMessage, invitationMessage,
space, space,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/share-space-new-user.html", parameters) html, err := m.ParseTemplate("mail/share-space-new-user.html", parameters)

View file

@ -14,6 +14,7 @@ package mail
import ( import (
"fmt" "fmt"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain/smtp" "github.com/documize/community/domain/smtp"
) )
@ -24,20 +25,16 @@ func (m *Mailer) InviteNewUser(recipient, inviterName, inviterEmail, url, userna
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has invited you to Documize Community", inviterName) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_user_invite", inviterName)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
@ -45,13 +42,15 @@ func (m *Mailer) InviteNewUser(recipient, inviterName, inviterEmail, url, userna
Username string Username string
Password string Password string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
url, url,
recipient, recipient,
password, i18n.Localize(m.Context.Locale, "mail_template_password") + " " + password,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/invite-new-user.html", parameters) html, err := m.ParseTemplate("mail/invite-new-user.html", parameters)
@ -77,30 +76,28 @@ func (m *Mailer) InviteExistingUser(recipient, inviterName, inviterEmail, url st
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has invited you to their Documize Community account", inviterName) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_user_existing", inviterName)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
URL string URL string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
url, url,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/invite-existing-user.html", parameters) html, err := m.ParseTemplate("mail/invite-existing-user.html", parameters)
@ -125,22 +122,20 @@ func (m *Mailer) PasswordReset(recipient, url string) {
m.Initialize() m.Initialize()
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = "Documize Community password reset request" em.Subject = i18n.Localize(m.Context.Locale, "mail_template_reset_password")
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
URL string URL string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
url, url,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/password-reset.html", parameters) html, err := m.ParseTemplate("mail/password-reset.html", parameters)

File diff suppressed because one or more lines are too long

View file

@ -52,7 +52,7 @@ func (s Store) GetOrganization(ctx domain.RequestContext, id string) (org org.Or
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider, c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
c_maxtags AS maxtags, c_theme AS theme, c_created AS created, c_revised AS revised c_maxtags AS maxtags, c_theme AS theme, c_locale as locale, c_created AS created, c_revised AS revised
FROM dmz_org FROM dmz_org
WHERE c_refid=?`), WHERE c_refid=?`),
id) id)
@ -84,7 +84,7 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider, c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
c_maxtags AS maxtags, c_created AS created, c_revised AS revised, c_theme AS theme c_maxtags AS maxtags, c_theme AS theme, c_locale as locale, c_created AS created, c_revised AS revised, c_theme AS theme
FROM dmz_org FROM dmz_org
WHERE c_domain=? AND c_active=`+s.IsTrue()), WHERE c_domain=? AND c_active=`+s.IsTrue()),
subdomain) subdomain)
@ -99,7 +99,7 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider, c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
c_maxtags AS maxtags, c_created AS created, c_revised AS revised, c_theme AS theme c_maxtags AS maxtags, c_theme AS theme, c_locale as locale, c_created AS created, c_revised AS revised, c_theme AS theme
FROM dmz_org FROM dmz_org
WHERE c_domain='' AND c_active=`+s.IsTrue())) WHERE c_domain='' AND c_active=`+s.IsTrue()))
@ -116,7 +116,7 @@ func (s Store) UpdateOrganization(ctx domain.RequestContext, org org.Organizatio
_, err = ctx.Transaction.NamedExec(`UPDATE dmz_org SET _, err = ctx.Transaction.NamedExec(`UPDATE dmz_org SET
c_title=:title, c_message=:message, c_service=:conversionendpoint, c_email=:email, c_domain=:domain, c_title=:title, c_message=:message, c_service=:conversionendpoint, c_email=:email, c_domain=:domain,
c_anonaccess=:allowanonymousaccess, c_maxtags=:maxtags, c_theme=:theme, c_revised=:revised c_anonaccess=:allowanonymousaccess, c_maxtags=:maxtags, c_theme=:theme, c_locale=:locale, c_revised=:revised
WHERE c_refid=:refid`, WHERE c_refid=:refid`,
&org) &org)

View file

@ -201,13 +201,12 @@ func (s Store) Update(ctx domain.RequestContext, page page.Page, refID, userID s
// It then propagates that change into the search table, adds a delete the page revisions history, and audits that the page has been removed. // It then propagates that change into the search table, adds a delete the page revisions history, and audits that the page has been removed.
func (s Store) Delete(ctx domain.RequestContext, documentID, pageID string) (rows int64, err error) { func (s Store) Delete(ctx domain.RequestContext, documentID, pageID string) (rows int64, err error) {
rows, err = s.DeleteConstrained(ctx.Transaction, "dmz_section", ctx.OrgID, pageID) rows, err = s.DeleteConstrained(ctx.Transaction, "dmz_section", ctx.OrgID, pageID)
if err == nil {
_, _ = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_section_meta WHERE c_orgid='%s' AND c_sectionid='%s'", ctx.OrgID, pageID))
}
if err == nil { ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_section_meta WHERE c_orgid=? AND c_sectionid=?"),
_, _ = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_action WHERE c_orgid='%s' AND c_reftypeid='%s' AND c_reftype='P'", ctx.OrgID, pageID)) ctx.OrgID, pageID)
}
ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_action WHERE c_orgid=? AND c_reftypeid=? AND c_reftype='P'"),
ctx.OrgID, pageID)
return return
} }
@ -408,8 +407,8 @@ func (s Store) GetDocumentRevisions(ctx domain.RequestContext, documentID string
// DeletePageRevisions deletes all of the page revision records for a given pageID. // DeletePageRevisions deletes all of the page revision records for a given pageID.
func (s Store) DeletePageRevisions(ctx domain.RequestContext, pageID string) (rows int64, err error) { func (s Store) DeletePageRevisions(ctx domain.RequestContext, pageID string) (rows int64, err error) {
rows, err = s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_section_revision WHERE c_orgid='%s' AND c_sectionid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_section_revision WHERE c_orgid=? AND c_sectionid=?"),
ctx.OrgID, pageID)) ctx.OrgID, pageID)
return return
} }

View file

@ -69,7 +69,7 @@ func CanViewDocument(ctx domain.RequestContext, s store.Store, documentID string
return false return false
} }
// CanChangeDocument returns if the clinet has permission to change a given document. // CanChangeDocument returns if the client has permission to change a given document.
func CanChangeDocument(ctx domain.RequestContext, s store.Store, documentID string) bool { func CanChangeDocument(ctx domain.RequestContext, s store.Store, documentID string) bool {
document, err := s.Document.Get(ctx, documentID) document, err := s.Document.Get(ctx, documentID)
@ -98,7 +98,7 @@ func CanChangeDocument(ctx domain.RequestContext, s store.Store, documentID stri
return false return false
} }
// CanDeleteDocument returns if the clinet has permission to change a given document. // CanDeleteDocument returns if the client has permission to change a given document.
func CanDeleteDocument(ctx domain.RequestContext, s store.Store, documentID string) bool { func CanDeleteDocument(ctx domain.RequestContext, s store.Store, documentID string) bool {
document, err := s.Document.Get(ctx, documentID) document, err := s.Document.Get(ctx, documentID)
@ -166,7 +166,6 @@ func CanManageSpace(ctx domain.RequestContext, s store.Store, spaceID string) bo
return false return false
} }
// CanViewSpace returns if the user has permission to view the given spaceID. // CanViewSpace returns if the user has permission to view the given spaceID.
func CanViewSpace(ctx domain.RequestContext, s store.Store, spaceID string) bool { func CanViewSpace(ctx domain.RequestContext, s store.Store, spaceID string) bool {
roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID) roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID)

View file

@ -266,55 +266,56 @@ func (s Store) GetDocumentPermissions(ctx domain.RequestContext, documentID stri
// DeleteDocumentPermissions removes records from dmz_permissions table for given document. // DeleteDocumentPermissions removes records from dmz_permissions table for given document.
func (s Store) DeleteDocumentPermissions(ctx domain.RequestContext, documentID string) (rows int64, err error) { func (s Store) DeleteDocumentPermissions(ctx domain.RequestContext, documentID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_permission WHERE c_orgid='%s' AND c_location='document' AND c_refid='%s'", ctx.OrgID, documentID) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_permission WHERE c_orgid=? AND c_location='document' AND c_refid=?"),
ctx.OrgID, documentID)
return s.DeleteWhere(ctx.Transaction, sql) return
} }
// DeleteSpacePermissions removes records from dmz_permissions table for given space ID. // DeleteSpacePermissions removes records from dmz_permissions table for given space ID.
func (s Store) DeleteSpacePermissions(ctx domain.RequestContext, spaceID string) (rows int64, err error) { func (s Store) DeleteSpacePermissions(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_permission WHERE c_orgid='%s' AND c_location='space' AND c_refid='%s'", ctx.OrgID, spaceID) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid=?"),
ctx.OrgID, spaceID)
return s.DeleteWhere(ctx.Transaction, sql) return
} }
// DeleteUserSpacePermissions removes all roles for the specified user, for the specified space. // DeleteUserSpacePermissions removes all roles for the specified user, for the specified space.
func (s Store) DeleteUserSpacePermissions(ctx domain.RequestContext, spaceID, userID string) (rows int64, err error) { func (s Store) DeleteUserSpacePermissions(ctx domain.RequestContext, spaceID, userID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_permission WHERE c_orgid='%s' AND c_location='space' AND c_refid='%s' AND c_who='user' AND c_whoid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid=? AND c_who='user' AND c_whoid=?"),
ctx.OrgID, spaceID, userID) ctx.OrgID, spaceID, userID)
return s.DeleteWhere(ctx.Transaction, sql) return
} }
// DeleteUserPermissions removes all roles for the specified user, for the specified space. // DeleteUserPermissions removes all roles for the specified user, for the specified space.
func (s Store) DeleteUserPermissions(ctx domain.RequestContext, userID string) (rows int64, err error) { func (s Store) DeleteUserPermissions(ctx domain.RequestContext, userID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_permission WHERE c_orgid='%s' AND c_who='user' AND c_whoid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_permission WHERE c_orgid=? AND c_who='user' AND c_whoid=?"),
ctx.OrgID, userID) ctx.OrgID, userID)
return s.DeleteWhere(ctx.Transaction, sql) return
} }
// DeleteCategoryPermissions removes records from dmz_permissions table for given category ID. // DeleteCategoryPermissions removes records from dmz_permissions table for given category ID.
func (s Store) DeleteCategoryPermissions(ctx domain.RequestContext, categoryID string) (rows int64, err error) { func (s Store) DeleteCategoryPermissions(ctx domain.RequestContext, categoryID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_permission WHERE c_orgid='%s' AND c_location='category' AND c_refid='%s'", ctx.OrgID, categoryID) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_permission WHERE c_orgid=? AND c_location='category' AND c_refid=?"),
ctx.OrgID, categoryID)
return s.DeleteWhere(ctx.Transaction, sql) return
} }
// DeleteSpaceCategoryPermissions removes all category permission for for given space. // DeleteSpaceCategoryPermissions removes all category permission for for given space.
func (s Store) DeleteSpaceCategoryPermissions(ctx domain.RequestContext, spaceID string) (rows int64, err error) { func (s Store) DeleteSpaceCategoryPermissions(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
sql := fmt.Sprintf(` _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_permission WHERE c_orgid=? AND c_location='category' AND c_refid IN (SELECT c_refid FROM dmz_category WHERE c_orgid=? AND c_spaceid=?)"),
DELETE FROM dmz_permission WHERE c_orgid='%s' AND c_location='category'
AND c_refid IN (SELECT c_refid FROM dmz_category WHERE c_orgid='%s' AND c_spaceid='%s')`,
ctx.OrgID, ctx.OrgID, spaceID) ctx.OrgID, ctx.OrgID, spaceID)
return s.DeleteWhere(ctx.Transaction, sql) return
} }
// DeleteGroupPermissions removes all roles for the specified group // DeleteGroupPermissions removes all roles for the specified group
func (s Store) DeleteGroupPermissions(ctx domain.RequestContext, groupID string) (rows int64, err error) { func (s Store) DeleteGroupPermissions(ctx domain.RequestContext, groupID string) (rows int64, err error) {
sql := fmt.Sprintf("DELETE FROM dmz_permission WHERE c_orgid='%s' AND c_who='role' AND c_whoid='%s'", _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_permission WHERE c_orgid=? AND c_who='role' AND c_whoid=?"),
ctx.OrgID, groupID) ctx.OrgID, groupID)
return s.DeleteWhere(ctx.Transaction, sql) return
} }

View file

@ -120,10 +120,16 @@ func (s Store) DeletePin(ctx domain.RequestContext, id string) (rows int64, err
// DeletePinnedSpace removes any pins for specified space. // DeletePinnedSpace removes any pins for specified space.
func (s Store) DeletePinnedSpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) { func (s Store) DeletePinnedSpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_pin WHERE c_orgid='%s' AND c_spaceid='%s'", ctx.OrgID, spaceID)) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_pin WHERE c_orgid=? AND c_spaceid=?"),
ctx.OrgID, spaceID)
return
} }
// DeletePinnedDocument removes any pins for specified document. // DeletePinnedDocument removes any pins for specified document.
func (s Store) DeletePinnedDocument(ctx domain.RequestContext, documentID string) (rows int64, err error) { func (s Store) DeletePinnedDocument(ctx domain.RequestContext, documentID string) (rows int64, err error) {
return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_pin WHERE c_orgid='%s' AND c_docid='%s'", ctx.OrgID, documentID)) _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_pin WHERE c_orgid=? AND c_docid=?"),
ctx.OrgID, documentID)
return
} }

View file

@ -49,6 +49,9 @@ const (
// PlanSelfHost represents privately hosted Documize instance. // PlanSelfHost represents privately hosted Documize instance.
PlanSelfHost Plan = "Self-host" PlanSelfHost Plan = "Self-host"
// SeatsFree is Five free users.
SeatsFree Seats = 5
// Seats0 is 0 users. // Seats0 is 0 users.
Seats0 Seats = 0 Seats0 Seats = 0
@ -105,8 +108,8 @@ func (p *Product) IsValid(ctx RequestContext) bool {
return true return true
} }
} else { } else {
// First 10 is free for Enterprise edition. // First 5 is free for Enterprise edition.
if Seats1 == ctx.Subscription.Seats && time.Now().UTC().Before(ctx.Subscription.End) { if SeatsFree == ctx.Subscription.Seats && time.Now().UTC().Before(ctx.Subscription.End) {
return true return true
} }
} }
@ -159,8 +162,10 @@ type SubscriptionUserAccount struct {
// SubscriptionAsXML returns subscription data as XML document: // SubscriptionAsXML returns subscription data as XML document:
// //
// <DocumizeLicense> // <DocumizeLicense>
//
// <Key>some key</Key> // <Key>some key</Key>
// <Signature>some signature</Signature> // <Signature>some signature</Signature>
//
// </DocumizeLicense> // </DocumizeLicense>
// //
// XML document is empty in case of error. // XML document is empty in case of error.

View file

@ -30,8 +30,8 @@ func (*Provider) Meta() provider.TypeMeta {
section := provider.TypeMeta{} section := provider.TypeMeta{}
section.ID = "d46a18f6-49fb-11e8-842f-0ed5f89f718b" section.ID = "d46a18f6-49fb-11e8-842f-0ed5f89f718b"
section.Title = "Draw.io Diagram" section.Title = "Diagrams.net"
section.Description = "Draw.io powered flowcharts and diagrams" section.Description = "Flowcharts and diagrams"
section.ContentType = "flowchart" section.ContentType = "flowchart"
section.PageType = "tab" section.PageType = "tab"
section.Order = 9991 section.Order = 9991

View file

@ -1,116 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import (
"context"
"encoding/json"
"net/http"
"net/url"
"strings"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/domain/section/provider"
gogithub "github.com/google/go-github/github"
"golang.org/x/oauth2"
)
func clientID(ctx domain.RequestContext, s *domain.Store) string {
c, _ := s.Setting.Get(meta.ConfigHandle(), "clientID")
return c
}
func clientSecret(ctx domain.RequestContext, s *domain.Store) string {
c, _ := s.Setting.Get(meta.ConfigHandle(), "clientSecret")
return c
}
func authorizationCallbackURL(ctx domain.RequestContext, s *domain.Store) string {
// NOTE: URL value must have the path and query "/api/public/validate?section=github"
c, _ := s.Setting.Get(meta.ConfigHandle(), "authorizationCallbackURL")
return c
}
func validateToken(ctx provider.Context, s *domain.Store, ptoken string) error {
// Github authorization check
authClient := gogithub.NewClient((&gogithub.BasicAuthTransport{
Username: clientID(ctx.Request, s),
Password: clientSecret(ctx.Request, s),
}).Client())
_, _, err := authClient.Authorizations.Check(context.Background(), clientID(ctx.Request, s), ptoken)
return err
}
func (*Provider) githubClient(config *githubConfig) *gogithub.Client {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: config.Token},
)
tc := oauth2.NewClient(oauth2.NoContext, ts)
return gogithub.NewClient(tc)
}
// Callback is called by a browser redirect from Github, via the validation endpoint
func Callback(rt *env.Runtime, s *domain.Store, res http.ResponseWriter, req *http.Request) error {
ctx := domain.GetRequestContext(req)
code := req.URL.Query().Get("code")
state := req.URL.Query().Get("state")
ghurl := "https://github.com/login/oauth/access_token"
vals := "client_id=" + clientID(ctx, s)
vals += "&client_secret=" + clientSecret(ctx, s)
vals += "&code=" + code
vals += "&state=" + state
req2, err := http.NewRequest("POST", ghurl+"?"+vals, strings.NewReader(vals))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req2.Header.Set("Accept", "application/json")
res2, err := http.DefaultClient.Do(req2)
if err != nil {
return err
}
var gt githubCallbackT
err = json.NewDecoder(res2.Body).Decode(&gt)
if err != nil {
return err
}
err = res2.Body.Close()
if err != nil {
return err
}
returl, err := url.QueryUnescape(state)
if err != nil {
return err
}
up, err := url.Parse(returl)
if err != nil {
return err
}
target := up.Scheme + "://" + up.Host + up.Path + "?mode=edit&code=" + gt.AccessToken
http.Redirect(res, req, target, http.StatusTemporaryRedirect)
return nil
}

View file

@ -1,315 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize unity Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import (
"context"
"fmt"
"html/template"
"sort"
"time"
gogithub "github.com/google/go-github/github"
)
const commitTimeFormat = "2006-01-02, 15:04"
type githubCommit struct {
Owner string `json:"owner"`
Repo string `json:"repo"`
ShowRepo bool `json:"showRepo"`
Branch string `json:"branch"`
ShowBranch bool `json:"showBranch"`
Date string `json:"date"`
BinDate time.Time `json:"-"` // only used for sorting
ShowDate bool `json:"showDate"`
Login string `json:"login"`
Name string `json:"name"`
Avatar string `json:"avatar"`
Message string `json:"message"`
URL template.URL `json:"url"`
}
type githubAuthorStats struct {
Author string `json:"author"`
Login string `json:"login"`
Avatar string `json:"avatar"`
CommitCount int `json:"commitCount"`
Repos []string `json:"repos"`
OpenIssues int `json:"openIssues"`
ClosedIssues int `json:"closedIssues"`
}
// order commits in a way that makes sense of the table
type orderCommits []githubCommit
func (s orderCommits) Len() int { return len(s) }
func (s orderCommits) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s orderCommits) Less(i, j int) bool {
if s[i].Repo == s[j].Repo {
if s[i].Branch == s[j].Branch {
if s[i].BinDate == s[j].BinDate {
return s[i].Name < s[j].Name
}
return s[i].BinDate.Before(s[j].BinDate)
}
return s[i].Branch < s[j].Branch
}
return s[i].Repo < s[j].Repo
}
// sort stats in order that that should be presented.
type asToSort []githubAuthorStats
func (s asToSort) Len() int { return len(s) }
func (s asToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s asToSort) Less(i, j int) bool {
return s[i].CommitCount > s[j].CommitCount
}
// sort branches in order that that should be presented.
type branchByID []githubBranch
func (s branchByID) Len() int { return len(s) }
func (s branchByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s branchByID) Less(i, j int) bool {
return s[i].ID < s[j].ID
}
const tagCommitsData = "commitsData"
func getCommits(client *gogithub.Client, config *githubConfig) ([]githubCommit, []githubAuthorStats, error) {
if !config.ShowCommits {
return nil, nil, nil
}
// first make sure we've got all the branches
for _, orb := range config.Lists {
if orb.Included {
branches, _, err := client.Repositories.ListBranches(context.Background(), orb.Owner, orb.Repo,
&gogithub.ListOptions{PerPage: 100})
if err == nil {
render := make([]githubBranch, len(branches))
for kc, vb := range branches {
for _, existing := range config.Lists {
if orb.Owner == existing.Owner && orb.Repo == existing.Repo && orb.Name == *vb.Name {
goto found
}
}
render[kc] = githubBranch{
Owner: orb.Owner,
Repo: orb.Repo,
Name: *vb.Name,
ID: fmt.Sprintf("%s:%s:%s", orb.Owner, orb.Repo, *vb.Name),
Included: true,
URL: "https://github.com/" + orb.Owner + "/" + orb.Repo + "/tree/" + *vb.Name,
}
found:
}
config.Lists = append(config.Lists, render...)
}
}
}
sort.Sort(branchByID(config.Lists))
config.UserNames = make(map[string]string)
authorStats := make(map[string]githubAuthorStats)
contribBranch := make(map[string]map[string]struct{})
overall := []githubCommit{}
for _, orb := range config.Lists {
if orb.Included {
opts := &gogithub.CommitsListOptions{
SHA: orb.Name,
ListOptions: gogithub.ListOptions{PerPage: config.BranchLines}}
if config.SincePtr != nil {
opts.Since = *config.SincePtr
}
guff, _, err := client.Repositories.ListCommits(context.Background(), orb.Owner, orb.Repo, opts)
if err != nil {
fmt.Println(err)
return nil, nil, err
}
thisBranch := fmt.Sprintf("%s:%s", orb.Repo, orb.Name)
for _, v := range guff {
var d, m, u string
var bd time.Time
if v.Commit != nil {
if v.Commit.Committer.Date != nil {
d = v.Commit.Committer.Date.Format(commitTimeFormat)
bd = *v.Commit.Committer.Date
}
if v.Commit.Message != nil {
m = *v.Commit.Message
}
}
if v.HTMLURL != nil {
u = *v.HTMLURL
}
// author commits
al, an, aa := "", "", githubGravatar
if v.Author != nil {
if v.Author.Login != nil {
al = *v.Author.Login
an = getUserName(client, config, al)
}
if v.Author.AvatarURL != nil {
aa = *v.Author.AvatarURL
}
}
l := al // use author login
overall = append(overall, githubCommit{
Owner: orb.Owner,
Repo: orb.Repo,
Branch: orb.Name,
Name: an,
Login: l,
Message: m,
Date: d,
BinDate: bd,
Avatar: aa,
URL: template.URL(u),
})
if _, ok := contribBranch[l]; !ok {
contribBranch[l] = make(map[string]struct{})
}
contribBranch[l][thisBranch] = struct{}{}
cum := authorStats[l]
cum.Login = l
cum.Author = an
cum.Avatar = aa
cum.CommitCount++
// TODO review, this code removed as too slow
//cmt, _, err := client.Repositories.GetCommit(orb.Owner, orb.Repo, *v.SHA)
//if err == nil {
// if cmt.Stats != nil {
// if cmt.Stats.Total != nil {
// cum.TotalChanges += (*cmt.Stats.Total)
// }
// }
//}
//
authorStats[l] = cum
}
}
}
sort.Sort(orderCommits(overall))
for k := range overall {
overall[k].ShowRepo = true
overall[k].ShowBranch = true
overall[k].ShowDate = true
if k > 0 {
if overall[k].Repo == overall[k-1].Repo {
overall[k].ShowRepo = false
if overall[k].Branch == overall[k-1].Branch {
overall[k].ShowBranch = false
if overall[k].Date == overall[k-1].Date {
overall[k].ShowDate = false
}
}
}
}
}
retStats := make([]githubAuthorStats, 0, len(authorStats))
for _, v := range authorStats {
repos := contribBranch[v.Login]
v.Repos = make([]string, 0, len(repos))
for r := range repos {
v.Repos = append(v.Repos, r)
}
sort.Strings(v.Repos)
retStats = append(retStats, v)
}
sort.Sort(asToSort(retStats))
return overall, retStats, nil
}
func refreshCommits(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) {
if !config.ShowCommits {
return nil
}
gr.BranchCommits, gr.AuthorStats, err = getCommits(client, config)
if err != nil {
return err
}
return nil
}
func renderCommits(payload *githubRender, c *githubConfig) error {
if !c.ShowCommits {
return nil
}
payload.CommitCount = 0
for range payload.BranchCommits {
payload.CommitCount++
}
payload.HasCommits = payload.CommitCount > 0
for i := range payload.Issues {
var author int
for a := range payload.AuthorStats {
if payload.AuthorStats[a].Login == payload.Issues[i].Name ||
(payload.AuthorStats[a].Login == "" && payload.Issues[i].Name == unassignedIssue) {
author = a
goto found
}
}
// no Author found for issue, so create one
payload.AuthorStats = append(payload.AuthorStats, githubAuthorStats{
Author: payload.Issues[i].Name,
Avatar: payload.Issues[i].Avatar,
})
author = len(payload.AuthorStats) - 1
found:
if payload.Issues[i].IsOpen {
payload.AuthorStats[author].OpenIssues++
} else {
payload.AuthorStats[author].ClosedIssues++
}
}
payload.HasAuthorStats = len(payload.AuthorStats) > 0
sort.Sort(asToSort(payload.AuthorStats))
payload.NumContributors = len(payload.AuthorStats) - 1
return nil
}
func init() {
reports[tagCommitsData] = report{refreshCommits, renderCommits, commitsTemplate}
}

View file

@ -1,99 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
const commitsTemplate = `
<div class="section-github-render">
<!--
{{if .HasAuthorStats}}
<div class="heading">Contributors</div>
<p>
There
{{if eq 1 .NumContributors}}is{{else}}are{{end}}
{{.NumContributors}}
{{if eq 1 .NumContributors}}contributor{{else}}contributors{{end}}
across {{.RepoCount}}
{{if eq 1 .RepoCount}} repository. {{else}} repositories. {{end}}
</p>
<table class="github-table">
<thead>
<tr>
<th class="title">Contributors</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $stats := .AuthorStats}}
<tr>
<td class="no-width">
<img class="github-avatar" alt="@{{$stats.Author}}" src="{{$stats.Avatar}}" />
</td>
<td>
<div class="contributor-name">{{$stats.Author}}</div>
<div class="contributor-meta">
{{if gt $stats.OpenIssues 0}}
assigned {{$stats.OpenIssues}}
{{if eq 1 $stats.OpenIssues}} issue {{else}} issues {{end}}
{{end}}
{{if gt $stats.ClosedIssues 0}}
&middot; {{$stats.ClosedIssues}} closed
{{end}}
{{if gt $stats.CommitCount 0}}
{{if gt $stats.OpenIssues 0}} &middot; {{end}}
{{if gt $stats.ClosedIssues 0}} &middot; {{end}}
made {{$stats.CommitCount}}
{{if eq 1 $stats.CommitCount}} commit {{else}} commits {{end}}
on {{len $stats.Repos}} {{if eq 1 (len $stats.Repos)}} branch {{else}} branches {{end}}
{{range $repo := $stats.Repos}} &middot; {{$repo}} {{end}}
{{end}}
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
-->
{{if .HasCommits}}
<table class="github-table" style="width: 100%;">
<thead>
<tr>
<th class="title">Commits <span>&middot; {{len .BranchCommits}} commits</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $commit := .BranchCommits}}
<tr>
<td>
<a href="{{$commit.URL}}">{{$commit.Message}}</a>
<span class="data"> {{$commit.Branch}}</span>
</td>
<td class="right-column">
<div class="contributor-meta">
{{$commit.Date}}
<img class="github-avatar" title="@{{$commit.Name}}" alt="@{{$commit.Name}}" src="{{$commit.Avatar}}" />
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
`

View file

@ -1,251 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import (
"bytes"
"encoding/json"
"errors"
"html/template"
"io/ioutil"
"net/http"
"strings"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/domain/section/provider"
gogithub "github.com/google/go-github/github"
)
// TODO find a smaller image than the one below
const githubGravatar = "https://i2.wp.com/assets-cdn.github.com/images/gravatars/gravatar-user-420.png"
var meta provider.TypeMeta
func init() {
meta = provider.TypeMeta{}
meta.ID = "38c0e4c5-291c-415e-8a4d-262ee80ba5df"
meta.Title = "GitHub"
meta.Description = "Link code commits and issues"
meta.ContentType = "github"
meta.PageType = "tab"
meta.Callback = Callback
}
// Provider represents GitHub
type Provider struct {
Runtime *env.Runtime
Store *domain.Store
}
// Meta describes us.
func (*Provider) Meta() provider.TypeMeta {
return meta
}
// Command to run the various functions required...
func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
method := query.Get("method")
if len(method) == 0 {
msg := "missing method name"
provider.WriteMessage(w, "gitub", msg)
return
}
if method == "config" {
var ret struct {
CID string `json:"clientID"`
URL string `json:"authorizationCallbackURL"`
}
ret.CID = clientID(ctx.Request, p.Store)
ret.URL = authorizationCallbackURL(ctx.Request, p.Store)
provider.WriteJSON(w, ret)
return
}
defer r.Body.Close() // ignore error
body, err := ioutil.ReadAll(r.Body)
if err != nil {
p.Runtime.Log.Error("bad body", errors.New("Missing body"))
provider.WriteMessage(w, "github", "bad body")
return
}
if method == "saveSecret" { // secret Token update code
// write the new one, direct from JS
if err = ctx.SaveSecrets(string(body), p.Store); err != nil {
p.Runtime.Log.Error("github settoken configuration", err)
provider.WriteError(w, "github", err)
return
}
provider.WriteEmpty(w)
return
}
// load the config from the client-side
config := githubConfig{}
err = json.Unmarshal(body, &config)
if err != nil {
p.Runtime.Log.Error("github Command Unmarshal", err)
provider.WriteError(w, "github", err)
return
}
config.Clean()
// always use DB version of the token
config.Token = ctx.GetSecrets("token", p.Store) // get the secret token in the database
client := p.githubClient(&config)
switch method {
case "checkAuth":
if len(config.Token) == 0 {
err = errors.New("empty github token")
} else {
err = validateToken(*ctx, p.Store, config.Token)
}
if err != nil {
// token now invalid, so wipe it
ctx.SaveSecrets("", p.Store) // ignore error, already in an error state
p.Runtime.Log.Error("github check token validation", err)
provider.WriteError(w, "github", err)
return
}
provider.WriteEmpty(w)
default:
if listFailed(p.Runtime, method, config, client, w) {
gr := githubRender{}
for _, rep := range reports {
rep.refresh(&gr, &config, client)
}
provider.WriteJSON(w, &gr)
}
}
}
// Refresh ... gets the latest version
func (p *Provider) Refresh(ctx *provider.Context, configJSON, data string) string {
var c = githubConfig{}
err := json.Unmarshal([]byte(configJSON), &c)
if err != nil {
p.Runtime.Log.Error("github.Refresh unmarshal", err)
return "internal configuration error '" + err.Error() + "'"
}
c.Clean()
c.Token = ctx.GetSecrets("token", p.Store)
client := p.githubClient(&c)
byts, err := json.Marshal(refreshReportData(&c, client))
if err != nil {
p.Runtime.Log.Error("github.Refresh marshal", err)
return "internal configuration error '" + err.Error() + "'"
}
return string(byts)
}
func refreshReportData(c *githubConfig, client *gogithub.Client) *githubRender {
var gr = githubRender{}
for _, rep := range reports {
rep.refresh(&gr, c, client)
}
return &gr
}
// Render ... just returns the data given, suitably formatted
func (p *Provider) Render(ctx *provider.Context, config, data string) string {
var err error
payload := githubRender{}
var c = githubConfig{}
err = json.Unmarshal([]byte(config), &c)
if err != nil {
return "Please delete and recreate this Github section."
}
c.Clean()
c.Token = ctx.GetSecrets("token", p.Store)
data = strings.TrimSpace(data)
if len(data) == 0 {
p.Runtime.Log.Info("GitHub connector received empty data for rendering")
// TODO review why this error occurs & if it should be reported - seems to occur for new sections
// log.ErrorString(fmt.Sprintf("Rendered empty github JSON payload as '' for owner %s repos %#v", c.Owner, c.Lists))
return ""
}
err = json.Unmarshal([]byte(data), &payload)
if err != nil {
return "Please delete and recreate this Github section."
}
payload.Config = c
payload.Limit = c.BranchLines
payload.List = c.Lists
ret := ""
for _, repID := range c.ReportOrder {
rep, ok := reports[repID]
if !ok {
msg := "github report not found for: " + repID
p.Runtime.Log.Info(msg)
return "Documize internal error: " + msg
}
if err = rep.render(&payload, &c); err != nil {
p.Runtime.Log.Error("unable to render "+repID, err)
return "Documize internal github render " + repID + " error: " + err.Error() + "<BR>" + data
}
t := template.New("github")
t, err = t.Parse(rep.template)
if err != nil {
p.Runtime.Log.Error("github render template.Parse error:", err)
//for k, v := range strings.Split(rep.template, "\n") {
// fmt.Println("DEBUG", k+1, v)
//}
return "Documize internal github template.Parse error: " + err.Error()
}
buffer := new(bytes.Buffer)
err = t.Execute(buffer, payload)
if err != nil {
p.Runtime.Log.Error("github render template.Execute error:", err)
return "Documize internal github template.Execute error: " + err.Error()
}
ret += buffer.String()
}
return ret
}

View file

@ -1,239 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import (
"context"
"html/template"
"sort"
"time"
gogithub "github.com/google/go-github/github"
)
type githubIssue struct {
ID int `json:"id"`
Date string `json:"date"`
Updated string `json:"dated"`
Message string `json:"message"`
URL template.URL `json:"url"`
Name string `json:"name"`
Creator string `json:"creator"`
Avatar string `json:"avatar"`
Labels template.HTML `json:"labels"`
LabelNames []string `json:"labelNames"`
LabelColors []string `json:"labelColors"`
IsOpen bool `json:"isopen"`
Repo string `json:"repo"`
Private bool `json:"private"`
Milestone string `json:"milestone"`
}
type githubSharedLabel struct {
Name string `json:"name"`
Count int `json:"count"`
Color string `json:"color"`
Repos template.HTML `json:"Repos"`
}
// sort issues in order that that should be presented - by date updated.
type issuesToSort []githubIssue
func (s issuesToSort) Len() int { return len(s) }
func (s issuesToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s issuesToSort) Less(i, j int) bool {
if s[i].Milestone != noMilestone && s[j].Milestone == noMilestone {
return true
}
if s[i].Milestone == noMilestone && s[j].Milestone != noMilestone {
return false
}
if s[i].Milestone != s[j].Milestone {
// TODO should this order be by milestone completion?
return s[i].Milestone < s[j].Milestone
}
if !s[i].IsOpen && s[j].IsOpen {
return true
}
if s[i].IsOpen && !s[j].IsOpen {
return false
}
// TODO this seems a very slow approach
iDate, _ := time.Parse(issuesTimeFormat, s[i].Updated)
jDate, _ := time.Parse(issuesTimeFormat, s[j].Updated)
return iDate.Before(jDate)
}
// sort shared labels alphabetically
type sharedLabelsSort []githubSharedLabel
func (s sharedLabelsSort) Len() int { return len(s) }
func (s sharedLabelsSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s sharedLabelsSort) Less(i, j int) bool { return s[i].Name < s[j].Name }
const (
tagIssuesData = "issuesData"
issuesTimeFormat = "January 2 2006, 15:04"
unassignedIssue = "(unassigned)"
)
func init() {
reports[tagIssuesData] = report{refreshIssues, renderIssues, issuesTemplate}
}
func wrapLabels(labels []gogithub.Label) (l string, labelNames []string, labelColors []string) {
labelNames = make([]string, 0, len(labels))
labelColors = make([]string, 0, len(labels))
for _, ll := range labels {
labelNames = append(labelNames, *ll.Name)
labelColors = append(labelColors, *ll.Color)
l += `<span class="issue-label" style="background-color:#` + *ll.Color + `">` + *ll.Name + `</span> `
}
return l, labelNames, labelColors
}
func getIssues(client *gogithub.Client, config *githubConfig) ([]githubIssue, error) {
if !config.ShowIssues {
return nil, nil
}
ret := []githubIssue{}
hadRepo := make(map[string]bool)
for _, orb := range config.Lists {
if orb.Included {
rName := orb.Owner + "/" + orb.Repo
if !hadRepo[rName] {
for _, state := range []string{"open", "closed"} {
opts := &gogithub.IssueListByRepoOptions{
Sort: "updated",
State: state,
ListOptions: gogithub.ListOptions{PerPage: config.BranchLines}}
if config.SincePtr != nil && state == "closed" /* we want all the open ones */ {
opts.Since = *config.SincePtr
}
guff, _, err := client.Issues.ListByRepo(context.Background(), orb.Owner, orb.Repo, opts)
if err != nil {
return ret, err
}
for _, v := range guff {
n := unassignedIssue
av := githubGravatar
ptr := v.Assignee
if ptr != nil {
if ptr.Login != nil {
n = *ptr.Login
av = *ptr.AvatarURL
}
}
ms := noMilestone
if v.Milestone != nil {
if v.Milestone.Title != nil {
ms = *v.Milestone.Title
}
}
l, ln, lc := wrapLabels(v.Labels)
ret = append(ret, githubIssue{
Name: n,
Creator: getUserName(client, config, *v.User.Login),
Avatar: av,
Message: *v.Title,
Date: v.CreatedAt.Format(issuesTimeFormat),
Updated: v.UpdatedAt.Format(issuesTimeFormat),
URL: template.URL(*v.HTMLURL),
Labels: template.HTML(l),
LabelNames: ln,
LabelColors: lc,
ID: *v.Number,
IsOpen: *v.State == "open",
Repo: repoName(rName),
Private: orb.Private,
Milestone: ms,
})
}
}
}
hadRepo[rName] = true
}
}
sort.Sort(issuesToSort(ret))
return ret, nil
}
func refreshIssues(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) {
if !config.ShowIssues {
return nil
}
gr.Issues, err = getIssues(client, config)
if err != nil {
return err
}
gr.OpenIssues = 0
gr.ClosedIssues = 0
sharedLabels := make(map[string][]string)
sharedLabelColors := make(map[string]string)
for _, v := range gr.Issues {
if v.IsOpen {
gr.OpenIssues++
} else {
gr.ClosedIssues++
}
for i, lab := range v.LabelNames {
sharedLabels[lab] = append(sharedLabels[lab], v.Repo)
if _, exists := sharedLabelColors[lab]; !exists { // use the first one we see
sharedLabelColors[lab] = v.LabelColors[i]
}
}
}
gr.HasIssues = (gr.OpenIssues + gr.ClosedIssues) > 0
gr.SharedLabels = make([]githubSharedLabel, 0, len(sharedLabels)) // will usually be too big
for name, repos := range sharedLabels {
if len(repos) > 1 {
thisLab := githubSharedLabel{Name: name, Count: len(repos), Color: sharedLabelColors[name]}
show := ""
for i, r := range repos {
if i > 0 {
show += ", "
}
show += "<a href='https://github.com/" + config.Owner + "/" + r +
"/issues?q=is%3Aissue+label%3A" + name + "'>" + r + "</a>"
}
thisLab.Repos = template.HTML(show)
gr.SharedLabels = append(gr.SharedLabels, thisLab)
}
}
sort.Sort(sharedLabelsSort(gr.SharedLabels))
gr.HasSharedLabels = len(gr.SharedLabels) > 0
return nil
}
func renderIssues(payload *githubRender, c *githubConfig) error {
return nil
}

View file

@ -1,68 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
const (
openIsvg = `
<span class="issue-state" title="Open Issue">
<svg height="16" version="1.1" viewBox="0 0 14 16" width="14" class="color:#6cc644;">
<path d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path>
</svg>
</span>
`
closedIsvg = `
<span class="issue-state" title="Closed Issue">
<svg height="16" version="1.1" viewBox="0 0 16 16" width="16" class="color:#bd2c00;">
<path d="M7 10h2v2H7v-2zm2-6H7v5h2V4zm1.5 1.5l-1 1L12 9l4-4.5-1-1L12 7l-1.5-1.5zM8 13.7A5.71 5.71 0 0 1 2.3 8c0-3.14 2.56-5.7 5.7-5.7 1.83 0 3.45.88 4.5 2.2l.92-.92A6.947 6.947 0 0 0 8 1C4.14 1 1 4.14 1 8s3.14 7 7 7 7-3.14 7-7l-1.52 1.52c-.66 2.41-2.86 4.19-5.48 4.19v-.01z"></path>
</svg>
</span>
`
issuesTemplate = `
<div class="section-github-render">
{{if .HasIssues}}
<table class="github-table" style="width: 100%;">
<thead>
<tr>
<th class="title">
Issues <span>&middot; {{.ClosedIssues}} closed {{if eq 1 .ClosedIssues}}{{else}}issues{{end}} and {{.OpenIssues}} open
{{if eq 1 .OpenIssues}}issue{{else}}{{end}}</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $data := .Issues}}
<tr>
<td>
{{if $data.IsOpen}}
` + openIsvg + `
{{else}}
` + closedIsvg + `
{{end}}
<a href="{{$data.URL}}">{{$data.Message}}</a> <span class="data">#{{$data.ID}}</span>
{{$data.Labels}}
</td>
<td class="right-column">
<div class="milestone-meta">
<span class="meta-milestone">{{$data.Milestone}}</span> &middot;
<span class="meta-creator">{{$data.Creator}}</span> &middot; <span class="meta-date">{{$data.Date}}</span>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
`
)

View file

@ -1,106 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import (
"context"
"fmt"
"net/http"
"github.com/documize/community/core/env"
"github.com/documize/community/domain/section/provider"
gogithub "github.com/google/go-github/github"
)
func listFailed(rt *env.Runtime, method string, config githubConfig, client *gogithub.Client, w http.ResponseWriter) (failed bool) {
switch method { // which list to choose?
case "owners":
me, _, err := client.Users.Get(context.Background(), "")
if err != nil {
rt.Log.Error("github get user details:", err)
provider.WriteError(w, "github", err)
return
}
orgs, _, err := client.Organizations.List(context.Background(), "", nil)
if err != nil {
rt.Log.Error("github get user's organisations:", err)
provider.WriteError(w, "github", err)
return
}
owners := make([]githubOwner, 1+len(orgs))
owners[0] = githubOwner{ID: *me.Login, Name: *me.Login}
for ko, vo := range orgs {
id := 1 + ko
owners[id].ID = *vo.Login
owners[id].Name = *vo.Login
}
owners = sortOwners(owners)
provider.WriteJSON(w, owners)
case "orgrepos":
var render []githubBranch
if config.Owner != "" {
me, _, err := client.Users.Get(context.Background(), "")
if err != nil {
rt.Log.Error("github get user details:", err)
provider.WriteError(w, "github", err)
return
}
var repos []*gogithub.Repository
if config.Owner == *me.Login {
repos, _, err = client.Repositories.List(context.Background(), config.Owner, nil)
} else {
opt := &gogithub.RepositoryListByOrgOptions{
ListOptions: gogithub.ListOptions{PerPage: 100},
}
repos, _, err = client.Repositories.ListByOrg(context.Background(), config.Owner, opt)
}
if err != nil {
rt.Log.Error("github get user/org repositories:", err)
provider.WriteError(w, "github", err)
return
}
for _, vr := range repos {
render = append(render,
githubBranch{
Name: "master",
ID: fmt.Sprintf("%s:%s", config.Owner, *vr.Name),
Owner: config.Owner,
Repo: *vr.Name,
Private: *vr.Private,
Included: false,
URL: *vr.HTMLURL,
})
}
}
render = sortBranches(render)
provider.WriteJSON(w, render)
case "content":
provider.WriteJSON(w, refreshReportData(&config, client))
default:
return true // failed to get a list
}
return
}

View file

@ -1,225 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import (
"context"
"fmt"
"html/template"
"sort"
gogithub "github.com/google/go-github/github"
)
type githubMilestone struct {
Repo string `json:"repo"`
Private bool `json:"private"`
Name string `json:"name"`
URL template.URL `json:"url"`
IsOpen bool `json:"isopen"`
OpenIssues int `json:"openIssues"`
ClosedIssues int `json:"closedIssues"`
CompleteMsg string `json:"completeMsg"`
DueDate string `json:"dueDate"`
UpdatedAt string `json:"updatedAt"`
Progress uint `json:"progress"`
IsMilestone bool `json:"isMilestone"`
}
// sort milestones in order that that should be presented.
type milestonesToSort []githubMilestone
func (s milestonesToSort) Len() int { return len(s) }
func (s milestonesToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s milestonesToSort) Less(i, j int) bool {
if s[i].Repo < s[j].Repo {
return true
}
if s[i].Repo > s[j].Repo {
return false
}
if !s[i].IsOpen && s[j].IsOpen {
return true
}
if s[i].IsOpen && !s[j].IsOpen {
return false
}
if s[i].Name != noMilestone && s[j].Name == noMilestone {
return true
}
if s[i].Name == noMilestone && s[j].Name != noMilestone {
return false
}
if s[i].Progress == s[j].Progress { // order equal progress milestones
return s[i].Name < s[j].Name
}
return s[i].Progress >= s[j].Progress // put more complete milestones first
}
const (
tagMilestonesData = "milestonesData"
milestonesTimeFormat = "January 2 2006"
noMilestone = "no milestone"
)
func init() {
reports[tagMilestonesData] = report{refreshMilestones, renderMilestones, milestonesTemplate}
}
func getMilestones(client *gogithub.Client, config *githubConfig) ([]githubMilestone, error) {
if !config.ShowMilestones {
return nil, nil
}
ret := []githubMilestone{}
hadRepo := make(map[string]bool)
for _, orb := range config.Lists {
if orb.Included {
rName := orb.Owner + "/" + orb.Repo
if !hadRepo[rName] {
for _, state := range []string{"open", "closed"} {
opts := &gogithub.MilestoneListOptions{
Sort: "updated",
State: state,
ListOptions: gogithub.ListOptions{PerPage: config.BranchLines}}
guff, _, err := client.Issues.ListMilestones(context.Background(), orb.Owner, orb.Repo, opts)
if err != nil {
return ret, err
}
for _, v := range guff {
include := true
if state == "closed" {
if config.SincePtr != nil {
if (*config.SincePtr).After(*v.ClosedAt) {
include = false
}
}
}
if include {
dd := "no due date"
if v.DueOn != nil {
// TODO refactor to add message in red if the milestone is overdue
dd = "due " + (*v.DueOn).Format(milestonesTimeFormat) + ""
}
up := ""
if v.UpdatedAt != nil {
up = (*v.UpdatedAt).Format(milestonesTimeFormat)
}
progress := float64(*v.ClosedIssues*100) / float64(*v.OpenIssues+*v.ClosedIssues)
ret = append(ret, githubMilestone{
Repo: repoName(rName),
Private: orb.Private,
Name: *v.Title,
URL: template.URL(fmt.Sprintf(
"https://github.com/%s/%s/milestone/%d",
orb.Owner, orb.Repo, *v.Number)), // *v.HTMLURL does not give the correct value
IsOpen: *v.State == "open",
OpenIssues: *v.OpenIssues,
ClosedIssues: *v.ClosedIssues,
CompleteMsg: fmt.Sprintf("%2.0f%%", progress),
DueDate: dd,
UpdatedAt: up,
Progress: uint(progress),
IsMilestone: true,
})
}
}
}
}
hadRepo[rName] = true
}
}
return ret, nil
}
func refreshMilestones(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) {
if !config.ShowMilestones {
return nil
}
gr.Milestones, err = getMilestones(client, config)
if err != nil {
return err
}
gr.OpenMS = 0
gr.ClosedMS = 0
for _, v := range gr.Milestones {
if v.IsOpen {
gr.OpenMS++
} else {
gr.ClosedMS++
}
}
gr.HasMilestones = (gr.OpenMS + gr.ClosedMS) > 0
return nil
}
func renderMilestones(payload *githubRender, c *githubConfig) error {
if !c.ShowMilestones {
return nil
}
hadRepo := make(map[string]bool)
payload.RepoCount = 0
for _, orb := range payload.List {
rName := orb.Owner + "/" + orb.Repo
if !hadRepo[rName] {
if orb.Included {
payload.RepoCount++
issuesOpen, issuesClosed := 0, 0
for _, iss := range payload.Issues {
if iss.Repo == repoName(rName) {
if iss.Milestone == noMilestone {
if iss.IsOpen {
issuesOpen++
} else {
issuesClosed++
}
}
}
}
if issuesClosed+issuesOpen > 0 {
//payload.Milestones = append(payload.Milestones, githubMilestone{
// Repo: orb.Repo, Private: orb.Private, Name: noMilestone, IsOpen: true,
// OpenIssues: issuesOpen, ClosedIssues: issuesClosed, URL: template.URL(orb.URL),
//})
}
hadRepo[rName] = true
}
}
}
sort.Sort(milestonesToSort(payload.Milestones))
return nil
}

View file

@ -1,75 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
const (
rawMSsvg = `<path d="M8 2H6V0h2v2zm4 5H2c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h10l2 2-2 2zM8 4H6v2h2V4zM6 16h2V8H6v8z"></path>`
openMSsvg = `
<span class="issue-state" title="Open Milestone">
<svg height="16" width="14" version="1.1" viewBox="0 0 14 16">
` + rawMSsvg + `
</svg>
</span>
`
closedMSsvg = `
<span class="issue-state" title="Closed Milestone">
<svg aria-hidden="true" class="octicon octicon-check" height="16" height="14" version="1.1" viewBox="0 0 12 16">
<path d="M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5z"></path>
</svg>
</span>
`
milestonesTemplate = `
<div class="section-github-render">
{{if .HasMilestones}}
<table class="github-table" style="width: 100%;">
<thead>
<tr>
<th class="title">Milestones <span>&middot; {{.ClosedMS}} closed and {{.OpenMS}} open</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $data := .Milestones}}
<tr>
<td>
{{if $data.IsMilestone}}
{{if $data.IsOpen}}
` + openMSsvg + `
{{else}}
` + closedMSsvg + `
{{end}}
{{end}}
<a class="link" href="{{$data.URL}}">{{$data.Name}}</a>
<span class="data"> &middot; {{if $data.IsMilestone}} {{$data.DueDate}}{{end}} </span>
</td>
<td class="right-column">
{{if $data.IsMilestone}}
<span class="bold color-off-black">{{$data.CompleteMsg}}</span> complete
<span class="bold color-off-black">{{$data.OpenIssues}}</span> open
<span class="bold color-off-black">{{$data.ClosedIssues}}</span> closed
{{else}}
<span class="bold color-off-black">{{$data.OpenIssues}}</span> open <span class="bold color-off-black">{{$data.ClosedIssues}}</span> closed
{{end}}
<div class="progress-bar">
<div class="progress" style="width:{{$data.Progress}}%;"></div>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
`
)

View file

@ -1,198 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import (
"context"
"sort"
"strings"
"time"
gogithub "github.com/google/go-github/github"
)
type githubRender struct {
Config githubConfig `json:"config"`
List []githubBranch `json:"list"`
RepoCount int `json:"repoCount"`
ShowList bool `json:"showList"`
ShowIssueNumbers bool `json:"showIssueNumbers"`
BranchCommits []githubCommit `json:"branchCommits"`
HasCommits bool `json:"hasCommits"`
CommitCount int `json:"commitCount"`
Issues []githubIssue `json:"issues"`
HasIssues bool `json:"hasIssues"`
SharedLabels []githubSharedLabel `json:"sharedLabels"`
HasSharedLabels bool `json:"hasSharedLabels"`
OpenIssues int `json:"openIssues"`
ClosedIssues int `json:"closedIssues"`
Limit int `json:"limit"`
Milestones []githubMilestone `json:"milestones"`
HasMilestones bool `json:"hasMilestones"`
OpenMS int `json:"openMS"`
ClosedMS int `json:"closedMS"`
OpenPRs int `json:"openPRs"`
ClosedPRs int `json:"closedPRs"`
AuthorStats []githubAuthorStats `json:"authorStats"`
HasAuthorStats bool `json:"hasAuthorStats"`
NumContributors int `json:"numContributors"`
}
type report struct {
refresh func(*githubRender, *githubConfig, *gogithub.Client) error
render func(*githubRender, *githubConfig) error
template string
}
var reports = make(map[string]report)
type githubOwner struct {
ID string `json:"id"`
Name string `json:"name"`
}
type githubBranch struct {
ID string `json:"id"`
Owner string `json:"owner"`
Repo string `json:"repo"`
Name string `json:"name"`
Included bool `json:"included"`
URL string `json:"url"`
Color string `json:"color,omitempty"`
Comma bool `json:"comma"`
Private bool `json:"private"`
}
type githubLabel struct {
ID string `json:"id"`
Owner string `json:"owner"`
Repo string `json:"repo"`
Name string `json:"name"`
Included bool `json:"included"`
URL string `json:"url"`
Color string `json:"color,omitempty"`
}
type githubConfig struct {
Token string `json:"-"` // NOTE very important that the secret Token is not leaked to the client side, so "-"
UserID string `json:"userId"`
PageID string `json:"pageId"`
Owner string `json:"owner_name"`
BranchSince string `json:"branchSince,omitempty"`
SincePtr *time.Time `json:"-"`
Since string `json:"-"`
BranchLines int `json:"branchLines,omitempty,string"`
OwnerInfo githubOwner `json:"owner"`
ClientID string `json:"clientId"`
CallbackURL string `json:"callbackUrl"`
Lists []githubBranch `json:"lists,omitempty"`
ReportOrder []string `json:"-"`
DateMessage string `json:"-"`
UserNames map[string]string `json:"UserNames"`
ShowMilestones bool `json:"showMilestones,omitempty"`
ShowIssues bool `json:"showIssues,omitempty"`
ShowCommits bool `json:"showCommits,omitempty"`
}
func (c *githubConfig) Clean() {
c.Owner = c.OwnerInfo.Name
if len(c.BranchSince) >= len("yyyy/mm/dd hh:ss") {
var since time.Time
tt := []byte("yyyy-mm-ddThh:mm:00Z")
for _, i := range []int{0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15} {
tt[i] = c.BranchSince[i]
}
err := since.UnmarshalText(tt)
if err != nil {
} else {
c.SincePtr = &since
}
}
if c.SincePtr == nil {
c.DateMessage = " (the last 7 days)"
since := time.Now().AddDate(0, 0, -7)
c.SincePtr = &since
} else {
c.DateMessage = ""
}
c.Since = (*c.SincePtr).Format(issuesTimeFormat)
c.ReportOrder = []string{tagSummaryData}
if c.ShowMilestones {
c.ReportOrder = append(c.ReportOrder, tagMilestonesData)
}
if c.ShowIssues {
c.ReportOrder = append(c.ReportOrder, tagIssuesData)
}
if c.ShowCommits {
c.ReportOrder = append(c.ReportOrder, tagCommitsData)
}
c.BranchLines = 100 // overide any existing value with maximum allowable in one call
sort.Sort(branchesToSort(c.Lists)) // get the configured branches in a sensible order for display
lastItem := 0
for i := range c.Lists {
c.Lists[i].Comma = true
if c.Lists[i].Included {
lastItem = i
}
}
if lastItem < len(c.Lists) {
c.Lists[lastItem].Comma = false
}
if c.UserNames == nil {
c.UserNames = make(map[string]string)
}
}
type githubCallbackT struct {
AccessToken string `json:"access_token"`
}
func repoName(branchName string) string {
bits := strings.Split(branchName, "/")
if len(bits) != 2 {
return branchName + "?repo"
}
pieces := strings.Split(bits[1], ":")
if len(pieces) == 0 {
return branchName + "?repo:?branch"
}
return pieces[0]
}
func getUserName(client *gogithub.Client, config *githubConfig, login string) (fullName string) {
an := login
if content, found := config.UserNames[login]; found {
if len(content) > 0 {
an = content
}
} else {
usr, _, err := client.Users.Get(context.Background(), login)
if err == nil {
if usr.Name != nil {
if len(*usr.Name) > 0 {
config.UserNames[login] = *usr.Name
an = *usr.Name
}
}
} else {
config.UserNames[login] = login // don't look again for a missing name
}
}
return an
}

View file

@ -1,36 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import "sort"
// sort owners in order that that should be presented.
type ownersToSort []githubOwner
func (s ownersToSort) Len() int { return len(s) }
func (s ownersToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ownersToSort) Less(i, j int) bool {
return s[i].Name < s[j].Name
}
func sortOwners(in []githubOwner) []githubOwner {
sts := ownersToSort(in)
sort.Sort(sts)
return []githubOwner(sts)
}
// sort branches in order that that should be presented.
func sortBranches(in []githubBranch) []githubBranch {
sts := branchesToSort(in)
sort.Sort(sts)
return []githubBranch(sts)
}

View file

@ -1,40 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
import gogithub "github.com/google/go-github/github"
const (
tagSummaryData = "summaryData"
)
// sort branches in order that they should be presented.
type branchesToSort []githubBranch
func (s branchesToSort) Len() int { return len(s) }
func (s branchesToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s branchesToSort) Less(i, j int) bool {
return s[i].URL < s[j].URL
}
func init() {
reports[tagSummaryData] = report{refreshSummary, renderSummary, summaryTemplate}
}
func refreshSummary(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) {
return nil
}
func renderSummary(payload *githubRender, c *githubConfig) error {
return nil
}

View file

@ -1,48 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package github
const summaryTemplate = `
<div class="section-github-render">
<p>Activity since {{.Config.Since}}{{.Config.DateMessage}} for {{.Config.Owner}} repository
{{range $data := .Config.Lists}}
{{if $data.Included}}
<a class="link" href="{{$data.URL}}">
{{$data.Repo}}{{if $data.Comma}},{{end}}
</a>
{{end}}
{{end}}
</p>
<!--
{{if .HasSharedLabels}}
<div class="heading">Labels</div>
<p>There
{{if eq 1 (len .SharedLabels)}} is {{else}} are {{end}}
{{len .SharedLabels}}
shared
{{if eq 1 (len .SharedLabels)}} label {{else}} labels {{end}}
across the repositories.</p>
<table class="github-table">
<tbody>
{{range $slabel := .SharedLabels}}
<tr>
<td class="no-width"><span class="issue-label" style="background-color:#{{$slabel.Color}}">{{$slabel.Name}} ({{$slabel.Count}})</span></td>
<td>{{$slabel.Repos}}</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
-->
</div>
`

View file

@ -19,6 +19,7 @@ import (
"net/http" "net/http"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
@ -98,7 +99,7 @@ func (h *Handler) SetSMTP(w http.ResponseWriter, r *http.Request) {
Message string `json:"message"` Message string `json:"message"`
} }
result.Message = "Email sent successfully!" result.Message = i18n.Localize(ctx.Locale, "server_smtp_success")
u, err := h.Store.User.Get(ctx, ctx.UserID) u, err := h.Store.User.Get(ctx, ctx.UserID)
if err != nil { if err != nil {
@ -110,11 +111,11 @@ func (h *Handler) SetSMTP(w http.ResponseWriter, r *http.Request) {
} }
cfg := GetSMTPConfig(h.Store) cfg := GetSMTPConfig(h.Store)
h.Runtime.Log.Infof("%v", cfg) // h.Runtime.Log.Infof("%v", cfg)
dialer, err := smtp.Connect(cfg) dialer, err := smtp.Connect(cfg)
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = "Documize SMTP Test" em.Subject = i18n.Localize(ctx.Locale, "server_smtp_test_subject")
em.BodyHTML = "<p>This is a test email from Documize using current SMTP settings.</p>" em.BodyHTML = "<p>" + i18n.Localize(ctx.Locale, "server_smtp_test_body") + "</p>"
em.ToEmail = u.Email em.ToEmail = u.Email
em.ToName = u.Fullname() em.ToName = u.Fullname()
@ -208,7 +209,7 @@ func (h *Handler) GetInstanceSetting(w http.ResponseWriter, r *http.Request) {
ctx := domain.GetRequestContext(r) ctx := domain.GetRequestContext(r)
orgID := request.Param(r, "orgID") orgID := request.Param(r, "orgID")
if orgID != ctx.OrgID || !ctx.Administrator { if orgID != ctx.OrgID {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -216,8 +217,12 @@ func (h *Handler) GetInstanceSetting(w http.ResponseWriter, r *http.Request) {
key := request.Query(r, "key") key := request.Query(r, "key")
setting, _ := h.Store.Setting.GetUser(orgID, "", key, "") setting, _ := h.Store.Setting.GetUser(orgID, "", key, "")
if len(setting) == 0 { if len(setting) == 0 {
if key == "flowchart" {
setting = fmt.Sprintf(`{ "url": "%s" }`, "https://embed.diagrams.net/?embed=1&ui=Kennedy&spin=0&proto=json&splash=0")
} else {
setting = "{}" setting = "{}"
} }
}
response.WriteJSON(w, setting) response.WriteJSON(w, setting)
} }

View file

@ -36,7 +36,7 @@ func GetSMTPConfig(s *store.Store) (c smtp.Config) {
c.SenderEmail, _ = s.Setting.Get("SMTP", "sender") c.SenderEmail, _ = s.Setting.Get("SMTP", "sender")
c.SenderName, _ = s.Setting.Get("SMTP", "senderName") c.SenderName, _ = s.Setting.Get("SMTP", "senderName")
if c.SenderName == "" { if c.SenderName == "" {
c.SenderName = "Documize" c.SenderName = "Documize Community"
} }
// anon auth? // anon auth?

View file

@ -44,7 +44,6 @@ import (
"github.com/documize/community/model/space" "github.com/documize/community/model/space"
"github.com/documize/community/model/user" "github.com/documize/community/model/user"
wf "github.com/documize/community/model/workflow" wf "github.com/documize/community/model/workflow"
"github.com/microcosm-cc/bluemonday"
uuid "github.com/nu7hatch/gouuid" uuid "github.com/nu7hatch/gouuid"
) )
@ -100,7 +99,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
var sp space.Space var sp space.Space
sp.Name = model.Name sp.Name = model.Name
sp.Description = bluemonday.StrictPolicy().Sanitize(model.Description) // sp.Description = bluemonday.StrictPolicy().Sanitize(model.Description)
sp.Icon = model.Icon sp.Icon = model.Icon
sp.LabelID = model.LabelID sp.LabelID = model.LabelID

View file

@ -42,6 +42,7 @@ func inviteNewUserToSharedSpace(ctx domain.RequestContext, rt *env.Runtime, s *s
u.Password = secrets.GeneratePassword(requestedPassword, u.Salt) u.Password = secrets.GeneratePassword(requestedPassword, u.Salt)
userID := uniqueid.Generate() userID := uniqueid.Generate()
u.RefID = userID u.RefID = userID
u.Locale = ctx.OrgLocale
err = s.User.Add(ctx, u) err = s.User.Add(ctx, u)
if err != nil { if err != nil {

View file

@ -27,7 +27,7 @@ type Context struct {
// Bind selects query parameter placeholder for given database provider. // Bind selects query parameter placeholder for given database provider.
// //
// MySQL uses ?, ?, ? (default for all Documize queries).`` // MySQL uses ?, ?, ? (default for all Documize queries).
// PostgreSQL uses $1, $2, $3. // PostgreSQL uses $1, $2, $3.
// MS SQL Server uses @p1, @p2, @p3. // MS SQL Server uses @p1, @p2, @p3.
func (c *Context) Bind(sql string) string { func (c *Context) Bind(sql string) string {
@ -86,20 +86,6 @@ func (c *Context) DeleteConstrainedWithID(tx *sqlx.Tx, table string, orgID, id s
return return
} }
// DeleteWhere free form query.
func (c *Context) DeleteWhere(tx *sqlx.Tx, statement string) (rows int64, err error) {
_, err = tx.Exec(statement)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to delete rows: %s", statement))
return
}
return
}
// EmptyJSON returns database specific empty JSON object. // EmptyJSON returns database specific empty JSON object.
func (c *Context) EmptyJSON() string { func (c *Context) EmptyJSON() string {
return c.Runtime.StoreProvider.JSONEmpty() return c.Runtime.StoreProvider.JSONEmpty()

View file

@ -22,6 +22,7 @@ import (
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/event" "github.com/documize/community/core/event"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
@ -92,6 +93,7 @@ func (h *Handler) SavedList(w http.ResponseWriter, r *http.Request) {
func (h *Handler) SaveAs(w http.ResponseWriter, r *http.Request) { func (h *Handler) SaveAs(w http.ResponseWriter, r *http.Request) {
method := "template.saved" method := "template.saved"
ctx := domain.GetRequestContext(r) ctx := domain.GetRequestContext(r)
unseq := doc.Unsequenced
if !h.Runtime.Product.IsValid(ctx) { if !h.Runtime.Product.IsValid(ctx) {
response.WriteBadLicense(w) response.WriteBadLicense(w)
@ -148,6 +150,7 @@ func (h *Handler) SaveAs(w http.ResponseWriter, r *http.Request) {
doc.ID = 0 doc.ID = 0
doc.Template = true doc.Template = true
doc.Lifecycle = workflow.LifecycleLive doc.Lifecycle = workflow.LifecycleLive
doc.Sequence = unseq
// Duplicate pages and associated meta // Duplicate pages and associated meta
pages, err := h.Store.Page.GetPages(ctx, model.DocumentID) pages, err := h.Store.Page.GetPages(ctx, model.DocumentID)
@ -296,7 +299,7 @@ func (h *Handler) Use(w http.ResponseWriter, r *http.Request) {
var d = doc.Document{} var d = doc.Document{}
d.Name = docTitle d.Name = docTitle
d.Location = fmt.Sprintf("template-%s", templateID) d.Location = fmt.Sprintf("template-%s", templateID)
d.Excerpt = "Add detailed description for document..." d.Excerpt = i18n.Localize(ctx.Locale, "description")
d.Slug = stringutil.MakeSlug(d.Name) d.Slug = stringutil.MakeSlug(d.Name)
d.Tags = "" d.Tags = ""
d.SpaceID = spaceID d.SpaceID = spaceID
@ -341,6 +344,7 @@ func (h *Handler) Use(w http.ResponseWriter, r *http.Request) {
d.SpaceID = spaceID d.SpaceID = spaceID
d.UserID = ctx.UserID d.UserID = ctx.UserID
d.Name = docTitle d.Name = docTitle
d.Sequence = doc.Unsequenced
if h.Runtime.Product.Edition == domain.CommunityEdition { if h.Runtime.Product.Edition == domain.CommunityEdition {
d.Lifecycle = workflow.LifecycleLive d.Lifecycle = workflow.LifecycleLive

View file

@ -26,6 +26,7 @@ import (
"github.com/documize/community/core/event" "github.com/documize/community/core/event"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/core/stringutil" "github.com/documize/community/core/stringutil"
@ -135,6 +136,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
if addUser { if addUser {
userID = uniqueid.Generate() userID = uniqueid.Generate()
userModel.RefID = userID userModel.RefID = userID
userModel.Locale = ctx.OrgLocale
err = h.Store.User.Add(ctx, userModel) err = h.Store.User.Add(ctx, userModel)
if err != nil { if err != nil {
@ -506,6 +508,21 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
return return
} }
// Trap for non-admin users boosting their own user roles
if u.Admin && !a.Admin && !ctx.Administrator {
response.WriteForbiddenError(w)
return
}
if u.Editor && !a.Editor && !ctx.Administrator {
response.WriteForbiddenError(w)
return
}
if u.Active && !a.Active && !ctx.Administrator {
response.WriteForbiddenError(w)
return
}
// Set user roles
a.Editor = u.Editor a.Editor = u.Editor
a.Admin = u.Admin a.Admin = u.Admin
a.Active = u.Active a.Active = u.Active
@ -780,6 +797,7 @@ func (h *Handler) BulkImport(w http.ResponseWriter, r *http.Request) {
userModel.Firstname = strings.TrimSpace(v[0]) userModel.Firstname = strings.TrimSpace(v[0])
userModel.Lastname = strings.TrimSpace(v[1]) userModel.Lastname = strings.TrimSpace(v[1])
userModel.Email = strings.ToLower(strings.TrimSpace(v[2])) userModel.Email = strings.ToLower(strings.TrimSpace(v[2]))
userModel.Locale = ctx.OrgLocale
if len(userModel.Email) == 0 || len(userModel.Firstname) == 0 || len(userModel.Lastname) == 0 { if len(userModel.Email) == 0 || len(userModel.Firstname) == 0 || len(userModel.Lastname) == 0 {
h.Runtime.Log.Info(method + " missing firstname, lastname, or email") h.Runtime.Log.Info(method + " missing firstname, lastname, or email")

View file

@ -38,8 +38,8 @@ func (s Store) Add(ctx domain.RequestContext, u user.User) (err error) {
u.Created = time.Now().UTC() u.Created = time.Now().UTC()
u.Revised = time.Now().UTC() u.Revised = time.Now().UTC()
_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_user (c_refid, c_firstname, c_lastname, c_email, c_initials, c_password, c_salt, c_reset, c_lastversion, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), _, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_user (c_refid, c_firstname, c_lastname, c_email, c_initials, c_password, c_salt, c_reset, c_lastversion, c_locale, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
u.RefID, u.Firstname, u.Lastname, strings.TrimSpace(strings.ToLower(u.Email)), u.Initials, u.Password, u.Salt, "", u.LastVersion, u.Created, u.Revised) u.RefID, u.Firstname, u.Lastname, strings.TrimSpace(strings.ToLower(u.Email)), u.Initials, u.Password, u.Salt, "", u.LastVersion, u.Locale, u.Created, u.Revised)
if err != nil { if err != nil {
err = errors.Wrap(err, "execute user insert") err = errors.Wrap(err, "execute user insert")
@ -53,7 +53,7 @@ func (s Store) Get(ctx domain.RequestContext, id string) (u user.User, err error
err = s.Runtime.Db.Get(&u, s.Bind(` err = s.Runtime.Db.Get(&u, s.Bind(`
SELECT id, c_refid AS refid, c_firstname AS firstname, c_lastname AS lastname, c_email AS email, SELECT id, c_refid AS refid, c_firstname AS firstname, c_lastname AS lastname, c_email AS email,
c_initials AS initials, c_globaladmin AS globaladmin, c_password AS password, c_salt AS salt, c_reset AS reset, c_initials AS initials, c_globaladmin AS globaladmin, c_password AS password, c_salt AS salt, c_reset AS reset,
c_lastversion AS lastversion, c_created AS created, c_revised AS revised c_lastversion AS lastversion, c_locale as locale, c_created AS created, c_revised AS revised
FROM dmz_user FROM dmz_user
WHERE c_refid=?`), WHERE c_refid=?`),
id) id)
@ -72,7 +72,7 @@ func (s Store) GetByDomain(ctx domain.RequestContext, domain, email string) (u u
err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u, dmz_user_account a, dmz_org o FROM dmz_user u, dmz_user_account a, dmz_org o
WHERE LOWER(u.c_email)=? AND u.c_refid=a.c_userid AND a.c_orgid=o.c_refid AND LOWER(o.c_domain)=?`), WHERE LOWER(u.c_email)=? AND u.c_refid=a.c_userid AND a.c_orgid=o.c_refid AND LOWER(o.c_domain)=?`),
@ -92,7 +92,7 @@ func (s Store) GetByEmail(ctx domain.RequestContext, email string) (u user.User,
err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u FROM dmz_user u
WHERE LOWER(u.c_email)=?`), WHERE LOWER(u.c_email)=?`),
@ -110,7 +110,7 @@ func (s Store) GetByToken(ctx domain.RequestContext, token string) (u user.User,
err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u FROM dmz_user u
WHERE u.c_reset=?`), WHERE u.c_reset=?`),
@ -130,7 +130,7 @@ func (s Store) GetBySerial(ctx domain.RequestContext, serial string) (u user.Use
err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u FROM dmz_user u
WHERE u.c_salt=?`), WHERE u.c_salt=?`),
@ -151,7 +151,7 @@ func (s Store) GetActiveUsersForOrganization(ctx domain.RequestContext) (u []use
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -176,7 +176,7 @@ func (s Store) GetSpaceUsers(ctx domain.RequestContext, spaceID string) (u []use
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -210,7 +210,7 @@ func (s Store) GetUsersForSpaces(ctx domain.RequestContext, spaces []string) (u
SELECT u.id, u.c_refid AS refid, SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -244,7 +244,7 @@ func (s Store) UpdateUser(ctx domain.RequestContext, u user.User) (err error) {
u.Revised = time.Now().UTC() u.Revised = time.Now().UTC()
u.Email = strings.ToLower(u.Email) u.Email = strings.ToLower(u.Email)
_, err = ctx.Transaction.NamedExec("UPDATE dmz_user SET c_firstname=:firstname, c_lastname=:lastname, c_email=:email, c_revised=:revised, c_initials=:initials, c_lastversion=:lastversion WHERE c_refid=:refid", &u) _, err = ctx.Transaction.NamedExec("UPDATE dmz_user SET c_firstname=:firstname, c_lastname=:lastname, c_email=:email, c_revised=:revised, c_initials=:initials, c_lastversion=:lastversion, c_locale=:locale WHERE c_refid=:refid", &u)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute user update %s", u.RefID)) err = errors.Wrap(err, fmt.Sprintf("execute user update %s", u.RefID))
} }
@ -313,7 +313,7 @@ func (s Store) GetUsersForOrganization(ctx domain.RequestContext, filter string,
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT TOP(`+strconv.Itoa(limit)+`) u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT TOP(`+strconv.Itoa(limit)+`) u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -323,7 +323,7 @@ func (s Store) GetUsersForOrganization(ctx domain.RequestContext, filter string,
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -357,7 +357,7 @@ func (s Store) MatchUsers(ctx domain.RequestContext, text string, maxMatches int
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT TOP(`+strconv.Itoa(maxMatches)+`) u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT TOP(`+strconv.Itoa(maxMatches)+`) u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -367,7 +367,7 @@ func (s Store) MatchUsers(ctx domain.RequestContext, text string, maxMatches int
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a

View file

@ -18,6 +18,7 @@ import (
"os" "os"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/section" "github.com/documize/community/domain/section"
"github.com/documize/community/domain/store" "github.com/documize/community/domain/store"
@ -38,10 +39,10 @@ func main() {
// Specify the product edition. // Specify the product edition.
rt.Product = domain.Product{} rt.Product = domain.Product{}
rt.Product.Major = "4" rt.Product.Major = "5"
rt.Product.Minor = "2" rt.Product.Minor = "13"
rt.Product.Patch = "2" rt.Product.Patch = "0"
rt.Product.Revision = "220208183403" rt.Product.Revision = "1735665467719"
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch) rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
rt.Product.Edition = domain.CommunityEdition rt.Product.Edition = domain.CommunityEdition
rt.Product.Title = "Community" rt.Product.Title = "Community"
@ -62,6 +63,12 @@ func main() {
} }
rt.Log.Info("Configuration: " + rt.Flags.ConfigSource) rt.Log.Info("Configuration: " + rt.Flags.ConfigSource)
// i18n
err := i18n.Initialize(rt.Assets)
if err != nil {
rt.Log.Error("i18n", err)
}
// Start database init. // Start database init.
boot.InitRuntime(&rt, &s) boot.InitRuntime(&rt, &s)

View file

@ -16,7 +16,6 @@ import (
"fmt" "fmt"
"strings" "strings"
_ "github.com/denisenkom/go-mssqldb" // the SQL Server driver is required behind the scenes
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
account "github.com/documize/community/domain/account" account "github.com/documize/community/domain/account"
activity "github.com/documize/community/domain/activity" activity "github.com/documize/community/domain/activity"
@ -39,6 +38,7 @@ import (
space "github.com/documize/community/domain/space" space "github.com/documize/community/domain/space"
"github.com/documize/community/domain/store" "github.com/documize/community/domain/store"
user "github.com/documize/community/domain/user" user "github.com/documize/community/domain/user"
_ "github.com/microsoft/go-mssqldb" // the SQL Server driver is required behind the scenes
) )
// SQLServerProvider supports Microsoft SQl Server. // SQLServerProvider supports Microsoft SQl Server.
@ -55,11 +55,16 @@ type SQLServerProvider struct {
// Useful links: // Useful links:
// //
// Driver for Golang: // Driver for Golang:
//
// https://github.com/denisenkom/go-mssqldb // https://github.com/denisenkom/go-mssqldb
//
// Docker Linux testing: // Docker Linux testing:
//
// https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-2017 // https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-2017
// docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Passw0rd' -p 1433:1433 --name sql1 -d mcr.microsoft.com/mssql/server:2017-latest // docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Passw0rd' -p 1433:1433 --name sql1 -d mcr.microsoft.com/mssql/server:2017-latest
//
// JSON types: // JSON types:
//
// https://docs.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-2017 // https://docs.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-2017
// //
// Supports 2016, 2017 and 2019. // Supports 2016, 2017 and 2019.
@ -341,13 +346,17 @@ func (p SQLServerProvider) JSONGetValue(column, attribute string) string {
// See: http://sqlserverbuilds.blogspot.com // See: http://sqlserverbuilds.blogspot.com
func (p SQLServerProvider) VerfiyVersion(dbVersion string) (bool, string) { func (p SQLServerProvider) VerfiyVersion(dbVersion string) (bool, string) {
if strings.HasPrefix(dbVersion, "13.") || if strings.HasPrefix(dbVersion, "12.") ||
strings.HasPrefix(dbVersion, "13.") ||
strings.HasPrefix(dbVersion, "14.") || strings.HasPrefix(dbVersion, "14.") ||
strings.HasPrefix(dbVersion, "15.") { strings.HasPrefix(dbVersion, "15.") ||
strings.HasPrefix(dbVersion, "16.") {
return true, "" return true, ""
} }
return false, "Microsoft SQL Server 2016, 2017 or 2019 is required" return true, ""
// return false, "Microsoft SQL Server 2016+ or SQL Azure is required"
} }
// VerfiyCharacterCollation needs to ensure utf8. // VerfiyCharacterCollation needs to ensure utf8.

61
go.mod
View file

@ -1,52 +1,51 @@
module github.com/documize/community module github.com/documize/community
go 1.17 go 1.21
require ( require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v1.3.2
github.com/andygrunwald/go-jira v1.12.0 github.com/andygrunwald/go-jira v1.16.0
github.com/codegangsta/negroni v1.0.0 github.com/codegangsta/negroni v1.0.0
github.com/denisenkom/go-mssqldb v0.10.1-0.20210728001037-ee2fbc25fd8f
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/documize/blackfriday v2.0.0+incompatible github.com/documize/blackfriday v2.0.0+incompatible
github.com/documize/glick v0.0.0-20160503134043-a8ccbef88237 github.com/documize/glick v0.0.0-20160503134043-a8ccbef88237
github.com/documize/html-diff v0.0.0-20160503140253-f61c192c7796 github.com/documize/html-diff v0.0.0-20160503140253-f61c192c7796
github.com/documize/slug v1.1.1 github.com/documize/slug v1.1.1
github.com/go-ldap/ldap/v3 v3.4.1 github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.7.1
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/glog v1.2.0 // indirect
github.com/golang/protobuf v1.4.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-github v17.0.0+incompatible github.com/gorilla/handlers v1.5.2
github.com/google/go-querystring v1.0.0 // indirect github.com/gorilla/mux v1.8.1
github.com/gorilla/handlers v1.4.2 github.com/jmoiron/sqlx v1.3.5
github.com/gorilla/mux v1.7.4 github.com/kr/pretty v0.3.1 // indirect
github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.10.9
github.com/kr/pretty v0.2.0 // indirect
github.com/lib/pq v1.10.2
github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d // indirect github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d // indirect
github.com/microcosm-cc/bluemonday v1.0.17 github.com/microcosm-cc/bluemonday v1.0.26
github.com/microsoft/go-mssqldb v1.6.0
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 golang.org/x/crypto v0.19.0
golang.org/x/net v0.0.0-20210614182718-04defd469f4e golang.org/x/net v0.21.0
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
google.golang.org/appengine v1.6.6 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc
gopkg.in/cas.v2 v2.1.0 gopkg.in/cas.v2 v2.2.2
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
) )
require ( require (
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/aymerick/douceur v0.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect
github.com/fatih/structs v1.0.0 // indirect github.com/fatih/structs v1.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/gorilla/css v1.0.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/trivago/tgo v1.0.1 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect
google.golang.org/protobuf v1.21.0 // indirect github.com/google/uuid v1.3.1 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/trivago/tgo v1.0.7 // indirect
golang.org/x/text v0.14.0 // indirect
) )

236
go.sum
View file

@ -1,16 +1,31 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/andygrunwald/go-jira v1.12.0 h1:JJi2cEDmDxVtTXxC8ruLDbtOU6pA4OLeL0niyfNcoWw= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/andygrunwald/go-jira v1.12.0/go.mod h1:jYi4kFDbRPZTJdJOVJO4mpMMIwdB+rcZwSO58DzPd2I= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/andygrunwald/go-jira v1.16.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ=
github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY= github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY=
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
github.com/denisenkom/go-mssqldb v0.10.1-0.20210728001037-ee2fbc25fd8f h1:3UtVZFKTqZwLZi65UbfSIqYR75aUTP8FYUAEQnMXSJs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/denisenkom/go-mssqldb v0.10.1-0.20210728001037-ee2fbc25fd8f/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/documize/blackfriday v2.0.0+incompatible h1:qjRGAIVwZlHBtA/b9u0LtseYM3v3WpIXofPCwNjcUsE= github.com/documize/blackfriday v2.0.0+incompatible h1:qjRGAIVwZlHBtA/b9u0LtseYM3v3WpIXofPCwNjcUsE=
@ -21,111 +36,148 @@ github.com/documize/html-diff v0.0.0-20160503140253-f61c192c7796 h1:CuipXymSP8Di
github.com/documize/html-diff v0.0.0-20160503140253-f61c192c7796/go.mod h1:GTEVMy1JkyV+k/j8hLGRGHVs/IHJS4s7AtJJ9LSYjRQ= github.com/documize/html-diff v0.0.0-20160503140253-f61c192c7796/go.mod h1:GTEVMy1JkyV+k/j8hLGRGHVs/IHJS4s7AtJJ9LSYjRQ=
github.com/documize/slug v1.1.1 h1:OCJRbWxbOgrgiBYSbVzuFwxb9wVu4oy1LxvLJOC2s8Y= github.com/documize/slug v1.1.1 h1:OCJRbWxbOgrgiBYSbVzuFwxb9wVu4oy1LxvLJOC2s8Y=
github.com/documize/slug v1.1.1/go.mod h1:Vi7fQ5PzeOpXAiIrk1WCEDRihjTfU/bf4eWUPSD7tkU= github.com/documize/slug v1.1.1/go.mod h1:Vi7fQ5PzeOpXAiIrk1WCEDRihjTfU/bf4eWUPSD7tkU=
github.com/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-ldap/ldap/v3 v3.4.1 h1:fU/0xli6HY02ocbMuozHAYsaHLcnkLjvho2r5a34BUU= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d h1:eAS2t2Vy+6psf9LZ4T5WXWsbkBt3Tu5PWekJy5AGyEU= github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d h1:eAS2t2Vy+6psf9LZ4T5WXWsbkBt3Tu5PWekJy5AGyEU=
github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d/go.mod h1:3YMHqrw2Qu3Liy82v4QdAG17e9k91HZ7w3hqlpWqhDo= github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d/go.mod h1:3YMHqrw2Qu3Liy82v4QdAG17e9k91HZ7w3hqlpWqhDo=
github.com/microcosm-cc/bluemonday v1.0.17 h1:Z1a//hgsQ4yjC+8zEkV8IWySkXnsxmdSY642CTFQb5Y= github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
github.com/microcosm-cc/bluemonday v1.0.17/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc=
github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/trivago/tgo v1.0.1 h1:bxatjJIXNIpV18bucU4Uk/LaoxvxuOlp/oowRHyncLQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/trivago/tgo v1.0.1/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM=
github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/cas.v2 v2.1.0 h1:sbYBMWtpanwLH75GAWjIp5JnON9wa3NodLZhouu0G9I= gopkg.in/cas.v2 v2.2.2 h1:teLr/JI7VDEQu6qkXKndYac9w5tfy57sWlV+eNYHH+o=
gopkg.in/cas.v2 v2.1.0/go.mod h1:M291I/o/u3eeMl9SkXMPYpWasHp7weFY9G/pM5DbB+g= gopkg.in/cas.v2 v2.2.2/go.mod h1:mlmjh4qM/Jm3eSDD0QVr5GaaSW3nOonSUSWkLLvNYnI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -24,7 +24,11 @@ module.exports = {
"ember/no-get": "off", "ember/no-get": "off",
"ember/no-jquery": "off", "ember/no-jquery": "off",
"ember/no-mixins": "off", "ember/no-mixins": "off",
"ember/no-actions-hash": "off" "ember/no-actions-hash": "off",
"ember/require-computed-macros": "off",
"ember/use-ember-data-rfc-395-imports": "off",
"ember/avoid-leaking-state-in-ember-objects": "off",
"ember/require-return-from-computed": "off"
}, },
overrides: [ overrides: [
// node files // node files

View file

@ -1,3 +1,3 @@
{ {
"ignore_dirs": ["tmp", "dist", "dist-prod", "tests", "node_modules"] "ignore_dirs": ["tmp", "dist", "dist-prod", "tests", "node_modules", "public"]
} }

View file

@ -23,6 +23,7 @@ import Component from '@ember/component';
export default Component.extend(ModalMixin, Notifier, { export default Component.extend(ModalMixin, Notifier, {
appMeta: service(), appMeta: service(),
globalSvc: service('global'), globalSvc: service('global'),
i18n: service(),
isDocumizeProvider: computed('authProvider', function() { isDocumizeProvider: computed('authProvider', function() {
return this.get('authProvider') === this.get('constants').AuthProvider.Documize; return this.get('authProvider') === this.get('constants').AuthProvider.Documize;
@ -90,7 +91,7 @@ export default Component.extend(ModalMixin, Notifier, {
let provider = this.get('authProvider'); let provider = this.get('authProvider');
let constants = this.get('constants'); let constants = this.get('constants');
this.set('ldapPreview', {isError: true, message: 'Unable to connect'}); this.set('ldapPreview', {isError: true, message: this.i18n.localize('auth_ldap_preview_error')});
switch (provider) { switch (provider) {
case constants.AuthProvider.Documize: { case constants.AuthProvider.Documize: {
@ -176,7 +177,7 @@ export default Component.extend(ModalMixin, Notifier, {
this.get('globalSvc').previewLDAP(config).then((preview) => { this.get('globalSvc').previewLDAP(config).then((preview) => {
this.set('ldapPreview', preview); this.set('ldapPreview', preview);
this.modalOpen("#ldap-preview-modal", {"show": true}); this.modalOpen("#ldap-preview-modal", {"show": true});
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
}); });
}, },
@ -314,7 +315,7 @@ export default Component.extend(ModalMixin, Notifier, {
}); });
} }
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
}); });
} }
} }

View file

@ -18,16 +18,18 @@ import Component from '@ember/component';
export default Component.extend(Notifier, Modal, { export default Component.extend(Notifier, Modal, {
appMeta: service(), appMeta: service(),
router: service(), router: service(),
i18n: service(),
browserSvc: service('browser'), browserSvc: service('browser'),
backupLabel: 'Backup', backupLabel: '',
backupSystemLabel: 'System Backup', backupSystemLabel: '',
backupSpec: null, backupSpec: null,
backupFilename: '', backupFilename: '',
backupError: false, backupError: false,
backupSuccess: false, backupSuccess: false,
backupRunning: false, backupRunning: false,
restoreSpec: null, restoreSpec: null,
restoreButtonLabel: 'Restore', restoreButtonLabel: '',
restoreUploadReady: false, restoreUploadReady: false,
confirmRestore: '', confirmRestore: '',
@ -53,6 +55,10 @@ export default Component.extend(Notifier, Modal, {
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
this.set('backupLabel', this.i18n.localize('backup'));
this.set('backupSystemLabel', this.i18n.localize('backup_system'));
this.set('restoreButtonLabel', this.i18n.localize('restore'))
$('#restore-file').on('change', function(){ $('#restore-file').on('change', function(){
var fileName = document.getElementById("restore-file").files[0].name; var fileName = document.getElementById("restore-file").files[0].name;
$(this).next('.custom-file-label').html(fileName); $(this).next('.custom-file-label').html(fileName);
@ -68,14 +74,14 @@ export default Component.extend(Notifier, Modal, {
let spec = this.get('backupSpec'); let spec = this.get('backupSpec');
this.get('onBackup')(spec).then((filename) => { this.get('onBackup')(spec).then((filename) => {
this.notifySuccess('Completed'); this.notifySuccess(this.i18n.localize('completed'));
this.set('backupLabel', 'Start Backup'); this.set('backupLabel', this.i18n.localize('backup_start'));
this.set('backupSuccess', true); this.set('backupSuccess', true);
this.set('backupFilename', filename); this.set('backupFilename', filename);
this.set('backupRunning', false); this.set('backupRunning', false);
}, ()=> { }, ()=> {
this.notifyError('Failed'); this.notifyError(this.i18n.localize('backup_failed'));
this.set('backupLabel', 'Run Backup'); this.set('backupLabel', this.i18n.localize('backup_run'));
this.set('backupFailed', true); this.set('backupFailed', true);
this.set('backupRunning', false); this.set('backupRunning', false);
}); });
@ -133,7 +139,7 @@ export default Component.extend(Notifier, Modal, {
} }
// start restore process // start restore process
this.set('restoreButtonLabel', 'Please wait, restore running...'); this.set('restoreButtonLabel', this.i18n.localize('restore_running'));
this.set('restoreSuccess', false); this.set('restoreSuccess', false);
this.set('restoreFailed', false); this.set('restoreFailed', false);
@ -145,13 +151,13 @@ export default Component.extend(Notifier, Modal, {
} }
this.get('onRestore')(spec, filedata).then(() => { this.get('onRestore')(spec, filedata).then(() => {
this.notifySuccess('Completed'); this.notifySuccess(this.i18n.localize('completed'));
this.set('backupLabel', 'Restore'); this.set('backupLabel', this.i18n.localize('restore'));
this.set('restoreSuccess', true); this.set('restoreSuccess', true);
this.get('router').transitionTo('auth.logout'); this.get('router').transitionTo('auth.logout');
}, ()=> { }, ()=> {
this.notifyError('Failed'); this.notifyError(this.i18n.localize('backup_failed'));
this.set('restorbackupLabel', 'Restore'); this.set('restorbackupLabel', this.i18n.localize('restore'));
this.set('restoreFailed', true); this.set('restoreFailed', true);
}); });
}, },

View file

@ -25,7 +25,7 @@ export default Component.extend(Notifier, {
let self = this; let self = this;
let cacheBuster = + new Date(); let cacheBuster = + new Date();
$.ajax({ $.ajax({
url: `https://documize.s3-eu-west-1.amazonaws.com/news/summary.html?cb=${cacheBuster}`, url: `https://www.documize.com/community/news/summary.html?cb=${cacheBuster}`,
type: 'GET', type: 'GET',
dataType: 'html', dataType: 'html',
success: function (response) { success: function (response) {

View file

@ -10,6 +10,7 @@
// https://documize.com // https://documize.com
import $ from 'jquery'; import $ from 'jquery';
import { A } from '@ember/array';
import { empty, and } from '@ember/object/computed'; import { empty, and } from '@ember/object/computed';
import { isEmpty } from '@ember/utils'; import { isEmpty } from '@ember/utils';
import { set } from '@ember/object'; import { set } from '@ember/object';
@ -21,6 +22,7 @@ import Component from '@ember/component';
export default Component.extend(Notifier, { export default Component.extend(Notifier, {
appMeta: service(), appMeta: service(),
router: service(), router: service(),
i18n: service(),
maxTags: 3, maxTags: 3,
domain: '', domain: '',
titleEmpty: empty('model.general.title'), titleEmpty: empty('model.general.title'),
@ -29,12 +31,29 @@ export default Component.extend(Notifier, {
hasTitleInputError: and('titleEmpty', 'titleError'), hasTitleInputError: and('titleEmpty', 'titleError'),
hasMessageInputError: and('messageEmpty', 'messageError'), hasMessageInputError: and('messageEmpty', 'messageError'),
hasConversionEndpointInputError: and('conversionEndpointEmpty', 'conversionEndpointError'), hasConversionEndpointInputError: and('conversionEndpointEmpty', 'conversionEndpointError'),
locale: { name: '' },
locales: A([]),
init(...args) {
this._super(...args);
let l = this.get('appMeta.locales');
let t = A([]);
l.forEach((locale) => {
t.pushObject({ name: locale });
});
this.set('locales', t);
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.set('maxTags', this.get('model.general.maxTags')); this.set('maxTags', this.get('model.general.maxTags'));
this.set('domain', this.get('model.general.domain')); this.set('domain', this.get('model.general.domain'));
this.set('locale', this.locales.findBy('name', this.get('model.general.locale')));
}, },
didInsertElement() { didInsertElement() {
@ -68,7 +87,7 @@ export default Component.extend(Notifier, {
}); });
this.on("queuecomplete", function () { this.on("queuecomplete", function () {
self.notifySuccess('Logo uploaded'); self.notifySuccess(this.i18n.localize('saved'));
}); });
this.on("error", function (error, msg) { this.on("error", function (error, msg) {
@ -148,6 +167,10 @@ export default Component.extend(Notifier, {
}, },
actions: { actions: {
onSelectLocale(locale) {
this.set('model.general.locale', locale.name);
},
change() { change() {
const selectEl = $('#maxTags')[0]; const selectEl = $('#maxTags')[0];
const selection = selectEl.selectedOptions[0].value; const selection = selectEl.selectedOptions[0].value;
@ -186,12 +209,11 @@ export default Component.extend(Notifier, {
this.set('model.general.domain', this.get('domain').toLowerCase()); this.set('model.general.domain', this.get('domain').toLowerCase());
this.get('onUpdate')().then(() => { this.get('onUpdate')().then(() => {
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
set(this, 'titleError', false); set(this, 'titleError', false);
set(this, 'messageError', false); set(this, 'messageError', false);
set(this, 'conversionEndpointError', false); set(this, 'conversionEndpointError', false);
if (domainChanged) { if (domainChanged) {
let router = this.get('router'); let router = this.get('router');
router.transitionTo('auth.login'); router.transitionTo('auth.login');
@ -206,7 +228,7 @@ export default Component.extend(Notifier, {
onDefaultLogo() { onDefaultLogo() {
this.get('onDefaultLogo')(this.get('appMeta.orgId')); this.get('onDefaultLogo')(this.get('appMeta.orgId'));
this.notifySuccess('Using default logo'); this.notifySuccess(this.i18n.localize('saved'));
} }
} }
}); });

View file

@ -45,6 +45,14 @@ export default Component.extend(Notifier, {
this.set('trelloCreds', trello); this.set('trelloCreds', trello);
} }
let flowchart = this.get('flowchart');
if (_.isEmpty(flowchart) || !_.isObject(flowchart)) {
flowchart = {
url: '',
};
}
this.set('flowchart', flowchart);
}, },
actions: { actions: {
@ -61,7 +69,9 @@ export default Component.extend(Notifier, {
this.get('orgSvc').saveGlobalSetting('SECTION-TRELLO', this.get('trelloCreds')); this.get('orgSvc').saveGlobalSetting('SECTION-TRELLO', this.get('trelloCreds'));
} }
this.notifySuccess('Saved'); this.get('orgSvc').saveOrgSetting(orgId, 'flowchart', this.get('flowchart'));
this.notifySuccess(this.i18n.localize('saved'));
}); });
} }
} }

View file

@ -22,10 +22,13 @@ export default Component.extend(Notifier, Modals, {
subscription: null, subscription: null,
planCloud: false, planCloud: false,
planSelfhost: false, planSelfhost: false,
comment: 'Nothing in particular -- just passing through. Please close my Documize account.', comment: '',
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.set('comment', this.i18n.localize('close_account'));
this.get('global').getSubscription().then((subs) => { this.get('global').getSubscription().then((subs) => {
this.set('subscription', subs); this.set('subscription', subs);
if (subs.plan === 'Installed') { if (subs.plan === 'Installed') {
@ -41,7 +44,7 @@ export default Component.extend(Notifier, Modals, {
actions: { actions: {
saveLicense() { saveLicense() {
this.get('global').setLicense(this.get('license')).then(() => { this.get('global').setLicense(this.get('license')).then(() => {
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
window.location.reload(); window.location.reload();
}); });
}, },
@ -55,7 +58,7 @@ export default Component.extend(Notifier, Modals, {
let comment = this.get('comment'); let comment = this.get('comment');
this.get('global').deactivate(comment).then(() => { this.get('global').deactivate(comment).then(() => {
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
this.modalOpen("#deactivation-confirmation-modal", {"show": true}); this.modalOpen("#deactivation-confirmation-modal", {"show": true});
}); });
} }

View file

@ -15,14 +15,21 @@ import Component from '@ember/component';
export default Component.extend(Notifier, { export default Component.extend(Notifier, {
appMeta: service(), appMeta: service(),
buttonLabel: 'Rebuild', i18n: service(),
buttonLabel: '',
init() {
this._super(...arguments);
this.buttonLabel = this.i18n.localize('search_reindex_rebuild');
},
actions: { actions: {
reindex() { reindex() {
this.set('buttonLabel', 'Running...'); this.set('buttonLabel', this.i18n.localize('running'));
this.notifyInfo("Starting search re-index process"); this.notifyInfo(this.i18n.localize('search_reindex_start'));
this.get('reindex')(() => { this.get('reindex')(() => {
this.notifySuccess("Search re-indexing complete"); this.notifySuccess(this.i18n.localize('search_reindex_finish'));
this.set('buttonLabel', this.i18n.localize('search_reindex_rebuild'));
}); });
} }
} }

View file

@ -17,14 +17,20 @@ import Component from '@ember/component';
export default Component.extend(Notifier, { export default Component.extend(Notifier, {
appMeta: service(), appMeta: service(),
i18n: service(),
SMTPHostEmptyError: empty('model.smtp.host'), SMTPHostEmptyError: empty('model.smtp.host'),
SMTPPortEmptyError: empty('model.smtp.port'), SMTPPortEmptyError: empty('model.smtp.port'),
SMTPSenderEmptyError: empty('model.smtp.sender'), SMTPSenderEmptyError: empty('model.smtp.sender'),
senderNameError: empty('model.smtp.senderName'), senderNameError: empty('model.smtp.senderName'),
buttonText: 'Save & Test', buttonText: 'Save & Test',
testSMTP: null, testSMTP: null,
init() {
this._super(...arguments);
this.buttonText = this.i18n.localize('smtp_save_test');
},
actions: { actions: {
saveSMTP() { saveSMTP() {
if (this.get('SMTPHostEmptyError')) { if (this.get('SMTPHostEmptyError')) {
@ -50,11 +56,11 @@ export default Component.extend(Notifier, {
}, },
); );
this.set('buttonText', 'Please wait...'); this.set('buttonText', this.i18n.localize('please_wait'));
this.notifyInfo('Sending test email to you'); this.notifyInfo(this.i18n.localize('smtp_sent_test_email'));
this.get('saveSMTP')().then((result) => { this.get('saveSMTP')().then((result) => {
this.set('buttonText', 'Save & Test'); this.set('buttonText', this.i18n.localize('smtp_save_test'));
this.set('testSMTP', result); this.set('testSMTP', result);
this.set('appMeta.configured', true); this.set('appMeta.configured', true);

View file

@ -20,6 +20,7 @@ export default Component.extend(Notifier, Modals, {
spaceSvc: service('folder'), spaceSvc: service('folder'),
browserSvc: service('browser'), browserSvc: service('browser'),
documentSvc: service('document'), documentSvc: service('document'),
i18n: service(),
spaces: null, spaces: null,
label: computed('model', function() { label: computed('model', function() {
@ -75,7 +76,7 @@ export default Component.extend(Notifier, Modals, {
this.set('deleteSpace.id', ''); this.set('deleteSpace.id', '');
this.set('deleteSpace.name', ''); this.set('deleteSpace.name', '');
this.loadData(); this.loadData();
this.notifySuccess('Deleted'); this.notifySuccess(this.i18n.localize('deleted'));
}); });
}, },
@ -86,17 +87,17 @@ export default Component.extend(Notifier, Modals, {
filterType: 'space', filterType: 'space',
}; };
this.notifyInfo('Export running...'); this.notifyInfo(this.i18n.localize('space_admin_export_running'));
this.get('documentSvc').export(spec).then((htmlExport) => { this.get('documentSvc').export(spec).then((htmlExport) => {
this.get('browserSvc').downloadFile(htmlExport, 'documize.html'); this.get('browserSvc').downloadFile(htmlExport, 'documize-community.html');
this.notifySuccess('Export completed'); this.notifySuccess(this.i18n.localize('completed'));
}); });
}, },
onOwner(spaceId) { onOwner(spaceId) {
this.get('spaceSvc').grantOwnerPermission(spaceId).then(() => { /* jshint ignore:line */ this.get('spaceSvc').grantOwnerPermission(spaceId).then(() => { /* jshint ignore:line */
this.notifySuccess('Added as owner'); this.notifySuccess(this.i18n.localize('completed'));
}); });
} }
} }

View file

@ -15,10 +15,12 @@ import ModalMixin from '../../mixins/modal';
import Notifier from '../../mixins/notifier'; import Notifier from '../../mixins/notifier';
import stringUtil from '../../utils/string'; import stringUtil from '../../utils/string';
import Component from '@ember/component'; import Component from '@ember/component';
import { inject as service } from '@ember/service';
export default Component.extend(AuthProvider, ModalMixin, Notifier, { export default Component.extend(AuthProvider, ModalMixin, Notifier, {
bulkUsers: '', bulkUsers: '',
newUser: null, newUser: null,
i18n: service(),
init() { init() {
this._super(...arguments); this._super(...arguments);
@ -53,7 +55,7 @@ export default Component.extend(AuthProvider, ModalMixin, Notifier, {
this.get('onAddUser')(user).then(() => { this.get('onAddUser')(user).then(() => {
this.set('newUser', { firstname: '', lastname: '', email: '', active: true }); this.set('newUser', { firstname: '', lastname: '', email: '', active: true });
this.notifySuccess('Added user'); this.notifySuccess(this.i18n.localize('added'));
}); });
this.modalClose("#add-user-modal"); this.modalClose("#add-user-modal");
@ -68,7 +70,7 @@ export default Component.extend(AuthProvider, ModalMixin, Notifier, {
this.get('onAddUsers')(this.get('bulkUsers')).then(() => { this.get('onAddUsers')(this.get('bulkUsers')).then(() => {
this.set('bulkUsers', ''); this.set('bulkUsers', '');
this.notifySuccess('Added users'); this.notifySuccess(this.i18n.localize('added'));
}); });
this.modalClose("#add-user-modal"); this.modalClose("#add-user-modal");

Some files were not shown because too many files have changed in this diff Show more