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

Compare commits

...

200 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
HarveyKandola
ce93a5e623 v4.2.2 2022-02-08 18:48:30 -05:00
HarveyKandola
8df1cc73b0 Update license-key.hbs 2022-02-08 18:31:37 -05:00
HarveyKandola
53ec7c9274 Update README.md 2022-01-12 14:15:56 -05:00
HarveyKandola
cfe85248ce Update screenshot 2022-01-12 14:15:11 -05:00
HarveyKandola
30c31a1ba7 Merge branch 'master' of https://github.com/documize/community 2022-01-12 14:13:18 -05:00
HarveyKandola
a97b6b22d9 Skip LDAP user creation if not sync'ed 2022-01-12 14:12:51 -05:00
Harvey Kandola
e985c5f808
Update README.md 2022-01-11 21:27:18 -05:00
HarveyKandola
4b89f3b1c2 Release 4.2.0 2022-01-11 21:20:01 -05:00
HarveyKandola
707dc1e052 Increase category name size 2022-01-11 13:52:41 -05:00
HarveyKandola
88211739f0 Bump deps 2022-01-11 13:52:30 -05:00
HarveyKandola
6b3cdb5033 Support for ARIA 2022-01-11 12:40:52 -05:00
HarveyKandola
45f216b8a1 Improved tooltip rendering speed 2022-01-10 17:14:08 -05:00
HarveyKandola
c31c130ffd Remove Ember Attacher add-on 2022-01-10 17:01:54 -05:00
HarveyKandola
5d5e212a6b FreeBSD builds 2021-11-19 08:34:19 -05:00
HarveyKandola
8fa5569ae5 Implement more re-branding updates 2021-11-15 14:52:29 -05:00
HarveyKandola
8976bf817b Update README 2021-11-10 19:31:03 -05:00
HarveyKandola
0c3fed2b18 Prep re-branding 2021-11-03 11:46:20 -04:00
HarveyKandola
60dfb54d54 Allow logger.Info multiple args 2021-10-07 14:16:05 -04:00
Harvey Kandola
c6863201b3
Merge pull request #380 from nemunaire/f/dockerfile 2021-09-22 10:31:34 -04:00
Pierre-Olivier Mercier
45567e274a Modernize Dockerfile 2021-09-21 22:14:30 +02:00
HarveyKandola
dff4c6929b Update README.md 2021-09-09 12:29:38 -04:00
HarveyKandola
eea8db9288 Fingerprint during Ember build 2021-09-07 20:18:30 -04:00
HarveyKandola
e19c4ad18a Update build.bat 2021-09-07 11:32:22 -04:00
HarveyKandola
989b7cd62c Sync with Community 2021-08-19 13:02:56 -04:00
HarveyKandola
df8f650319 Support VB.NET for code syntax highlighting 2021-08-18 19:41:19 -04:00
HarveyKandola
565a063231 Tweaked document list rendering of tooltips 2021-08-18 19:40:57 -04:00
HarveyKandola
cb46f34503 Update PDF dep 2021-08-18 19:40:12 -04:00
HarveyKandola
470e2d3ecf Move over to Go embed directive 2021-08-18 19:39:51 -04:00
HarveyKandola
cddba799f8 Update Ember deps 2021-08-18 19:39:14 -04:00
HarveyKandola
05df22ed4a Update go deps 2021-08-18 19:38:43 -04:00
HarveyKandola
a5dfa6ee39 Merge branch 'master' of https://github.com/documize/community 2021-03-28 16:03:51 -04:00
HarveyKandola
780ce2df61 Bump 3.9.0 release 2021-03-28 16:03:47 -04:00
Harvey Kandola
9f28e1bff2
Merge pull request #379 from esquith/patch-1
Update README.md
2021-03-28 21:02:53 +01:00
Esquith Allen
8ae94295a2
Update README.md 2021-03-28 16:00:39 -04:00
HarveyKandola
adb7b4d7bf Enforce TLS 1.2 minimum 2021-03-16 13:58:27 -04:00
1367 changed files with 197364 additions and 180240 deletions

View file

@ -3,4 +3,4 @@
bin
.idea
selfcert
gui
gui/dist-prod

1
.gitignore vendored
View file

@ -18,6 +18,7 @@ _convert
bin/*
dist/*
embed/bindata/*
edition/static/*
gui/dist/*
gui/dist-prod/*

View file

@ -1,11 +1,32 @@
FROM golang:1.15-alpine as builder
COPY . /go/src/github.com/documize/community
FROM node:16-alpine as frontbuilder
WORKDIR /go/src/github.com/documize/community/gui
COPY ./gui /go/src/github.com/documize/community/gui
RUN npm --network-timeout=100000 install
RUN npm run build -- --environment=production --output-path dist-prod --suppress-sizes true
FROM golang:1.21-alpine as builder
WORKDIR /go/src/github.com/documize/community
RUN env GOOS=linux GOARCH=amd64 GODEBUG=tls13=1 go build -mod=vendor -o bin/documize-community-linux-amd64 ./edition/community.go
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/codemirror /go/src/github.com/documize/community/edition/static/public/codemirror
COPY --from=frontbuilder /go/src/github.com/documize/community/gui/dist-prod/prism /go/src/github.com/documize/community/edition/static/public/prism
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/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/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 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/postgresql/*.sql /go/src/github.com/documize/community/edition/static/scripts/postgresql/
COPY core/database/scripts/sqlserver/*.sql /go/src/github.com/documize/community/edition/static/scripts/sqlserver/
COPY domain/onboard/*.json /go/src/github.com/documize/community/edition/static/onboard/
RUN env GODEBUG=tls13=1 go build -mod=vendor -o bin/documize-community ./edition/community.go
# build release image
FROM alpine:3.10
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
COPY --from=builder /go/src/github.com/documize/community/bin/documize-community-linux-amd64 /documize
FROM alpine:3.16
RUN apk add --no-cache ca-certificates
COPY --from=builder /go/src/github.com/documize/community/bin/documize-community /documize
EXPOSE 5001
ENTRYPOINT [ "/documize" ]

View file

@ -1,52 +1,70 @@
Documize is an open source modern, lightweight and comprehensive alternative to Confluence and other such solutions.
Documize Community is an open source, modern, self-hosted, enterprise-grade knowledge management solution.
- Built for technical and non-technical users
- Designed to unify both customer-facing and internal documentation
- Organization through labels, spaces and categories
- No fee-charging marketplace
It's built with Golang + EmberJS and compiled down to a single executable binary for Linux, Windows and macOS.
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 PostgreSQL, Microsoft SQL Server or any MySQL variant.
All you need to provide is your database -- PostgreSQL, Microsoft SQL Server or any MySQL variant.
![Documize](screenshot-1.png "Documize")
![Documize Community](https://github.com/documize/community/blob/master/screenshot.png?raw=true)
## Latest Release
[Community Edition: v3.8.2](https://github.com/documize/community/releases)
[Community edition: v5.13.0](https://github.com/documize/community/releases)
[Enterprise Edition: v3.8.2](https://www.documize.com/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:
- content approval workflows
- content organization by label, space and category
- content version management
- content lifecycle management
- content feedback capture
- content PDF export
- analytics and reporting
- activity streams
- audit logs
- actions assignments
- 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
- Linux
- Windows
- macOS
- Raspberry Pi (using the ARM builds)
- Raspberry Pi (ARM build)
Support for AMD and ARM 64 bit architectures.
## 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+)
- Microsoft SQL Server (2016+ with FTS)
- Microsoft SQL Azure (v12+)
- MySQL (v5.7.10+ and v8.0.12+)
- Percona (v5.7.16-10+)
- MariaDB (10.3.0+)
## Browser Support
- Chrome
- Firefox
- Chrome
- Safari
- Microsoft Edge
- Brave
- Vivaldi
- Opera
- Microsoft Edge (v42+)
## Technology Stack
- Go (v1.15.5)
- Go (v1.23.4)
- Ember JS (v3.12.0)
## Authentication Options
@ -60,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.
## 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
For both Communuity and Enterprise 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>
We aim to respond within two working days!
We aim to respond within two working days.
## The Legal Bit
<https://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 Enterprise Edition and obtaining a commercial license by contacting <sales@documize.com>. Documize® is a registered trade mark 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,64 +7,61 @@ echo "Building Ember assets..."
cd gui
call ember b -o dist-prod/ --environment=production
::Call allows the rest of the file to run
echo "Copying Ember assets..."
cd ..
rd /s /q embed\bindata\public
mkdir embed\bindata\public
rd /s /q edition\static\public
mkdir edition\static\public
echo "Copying Ember assets folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\assets embed\bindata\public\assets
robocopy /e /NFL /NDL /NJH gui\dist-prod\assets edition\static\public\assets
echo "Copying Ember codemirror folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\codemirror embed\bindata\public\codemirror
robocopy /e /NFL /NDL /NJH gui\dist-prod\codemirror edition\static\public\codemirror
echo "Copying Ember prism folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\prism embed\bindata\public\prism
robocopy /e /NFL /NDL /NJH gui\dist-prod\prism edition\static\public\prism
echo "Copying Ember tinymce folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\tinymce embed\bindata\public\tinymce
robocopy /e /NFL /NDL /NJH gui\dist-prod\tinymce edition\static\public\tinymce
echo "Copying Ember pdfjs folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\pdfjs embed\bindata\public\pdfjs
robocopy /e /NFL /NDL /NJH gui\dist-prod\pdfjs edition\static\public\pdfjs
echo "Copying Ember sections folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\sections embed\bindata\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
copy gui\dist-prod\*.* embed\bindata
copy gui\dist-prod\favicon.ico embed\bindata\public
copy gui\dist-prod\manifest.json embed\bindata\public
echo "Copying static files"
copy gui\dist-prod\*.* edition\static
rd /s /q embed\bindata\mail
mkdir embed\bindata\mail
copy domain\mail\*.html embed\bindata\mail
copy core\database\templates\*.html embed\bindata
echo "Copying favicon.ico"
copy gui\dist-prod\favicon.ico edition\static\public
rd /s /q embed\bindata\scripts
mkdir embed\bindata\scripts
mkdir embed\bindata\scripts\mysql
mkdir embed\bindata\scripts\postgresql
mkdir embed\bindata\scripts\sqlserver
echo "Copying manifest.json"
copy gui\dist-prod\manifest.json edition\static\public
echo "Copying mail templates"
rd /s /q edition\static\mail
mkdir edition\static\mail
copy domain\mail\*.html edition\static\mail
echo "Copying database templates"
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
mkdir edition\static\scripts
mkdir edition\static\scripts\mysql
mkdir edition\static\scripts\postgresql
mkdir edition\static\scripts\sqlserver
echo "Copying database scripts folder"
robocopy /e /NFL /NDL /NJH core\database\scripts\mysql embed\bindata\scripts\mysql
robocopy /e /NFL /NDL /NJH core\database\scripts\postgresql embed\bindata\scripts\postgresql
robocopy /e /NFL /NDL /NJH core\database\scripts\sqlserver embed\bindata\scripts\sqlserver
robocopy /e /NFL /NDL /NJH core\database\scripts\mysql edition\static\scripts\mysql
robocopy /e /NFL /NDL /NJH core\database\scripts\postgresql edition\static\scripts\postgresql
robocopy /e /NFL /NDL /NJH core\database\scripts\sqlserver edition\static\scripts\sqlserver
rd /s /q embed\bindata\onboard
mkdir embed\bindata\onboard
robocopy /e /NFL /NDL /NJH domain\onboard\*.json embed\bindata\onboard
echo "Generating in-memory static assets..."
go get -u github.com/jteeuwen/go-bindata/...
go get -u github.com/elazarl/go-bindata-assetfs/...
cd embed
go generate
cd ..
rd /s /q edition\static\onboard
mkdir edition\static\onboard
robocopy /e /NFL /NDL /NJH domain\onboard edition\static\onboard *.json
echo "Compiling Windows"
set GOOS=windows
go build -mod=vendor -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
go build -mod=vendor -trimpath -gcflags="all=-trimpath=$GOPATH" -o bin/documize-community-windows-amd64.exe edition/community.go

View file

@ -8,62 +8,65 @@ echo "Build process started $NOW"
echo "Building Ember assets..."
cd gui
# export NODE_OPTIONS=--openssl-legacy-provider
ember build ---environment=production --output-path dist-prod --suppress-sizes true
cd ..
echo "Copying Ember assets..."
rm -rf embed/bindata/public
mkdir -p embed/bindata/public
cp -r gui/dist-prod/assets embed/bindata/public
cp -r gui/dist-prod/codemirror embed/bindata/public/codemirror
cp -r gui/dist-prod/prism embed/bindata/public/prism
cp -r gui/dist-prod/sections embed/bindata/public/sections
cp -r gui/dist-prod/tinymce embed/bindata/public/tinymce
cp -r gui/dist-prod/pdfjs embed/bindata/public/pdfjs
cp gui/dist-prod/*.* embed/bindata
cp gui/dist-prod/favicon.ico embed/bindata/public
cp gui/dist-prod/manifest.json embed/bindata/public
rm -rf edition/static/public
mkdir -p edition/static/public
cp -r gui/dist-prod/assets edition/static/public
cp -r gui/dist-prod/codemirror edition/static/public/codemirror
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/tinymce edition/static/public/tinymce
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/favicon.ico edition/static/public
cp gui/dist-prod/manifest.json edition/static/public
rm -rf embed/bindata/mail
mkdir -p embed/bindata/mail
cp domain/mail/*.html embed/bindata/mail
cp core/database/templates/*.html embed/bindata
rm -rf edition/static/mail
mkdir -p edition/static/mail
cp domain/mail/*.html edition/static/mail
cp core/database/templates/*.html edition/static
rm -rf embed/bindata/scripts
mkdir -p embed/bindata/scripts
mkdir -p embed/bindata/scripts/mysql
mkdir -p embed/bindata/scripts/postgresql
mkdir -p embed/bindata/scripts/sqlserver
cp -r core/database/scripts/mysql/*.sql embed/bindata/scripts/mysql
cp -r core/database/scripts/postgresql/*.sql embed/bindata/scripts/postgresql
cp -r core/database/scripts/sqlserver/*.sql embed/bindata/scripts/sqlserver
rm -rf edition/static/i18n
mkdir -p edition/static/i18n
cp -r gui/dist-prod/i18n/*.json edition/static/i18n
rm -rf embed/bindata/onboard
mkdir -p embed/bindata/onboard
cp -r domain/onboard/*.json embed/bindata/onboard
rm -rf edition/static/scripts
mkdir -p edition/static/scripts
mkdir -p edition/static/scripts/mysql
mkdir -p edition/static/scripts/postgresql
mkdir -p edition/static/scripts/sqlserver
cp -r core/database/scripts/mysql/*.sql edition/static/scripts/mysql
cp -r core/database/scripts/postgresql/*.sql edition/static/scripts/postgresql
cp -r core/database/scripts/sqlserver/*.sql edition/static/scripts/sqlserver
echo "Generating in-memory static assets..."
# go get -u github.com/jteeuwen/go-bindata/...
# go get -u github.com/elazarl/go-bindata-assetfs/...
cd embed
go generate
rm -rf edition/static/onboard
mkdir -p edition/static/onboard
cp -r domain/onboard/*.json edition/static/onboard
cd ..
echo "Compiling for Linux..."
env GOOS=linux GOARCH=amd64 go build -mod=vendor -trimpath -o bin/documize-community-linux-amd64 ./edition/community.go
echo "Compiling for macOS..."
echo "Compiling for macOS Intel..."
env GOOS=darwin GOARCH=amd64 go build -mod=vendor -trimpath -o bin/documize-community-darwin-amd64 ./edition/community.go
echo "Compiling for Windows..."
echo "Compiling for macOS ARM..."
env GOOS=darwin GOARCH=arm64 go build -mod=vendor -trimpath -o bin/documize-community-darwin-arm64 ./edition/community.go
echo "Compiling for Windows AMD..."
env GOOS=windows GOARCH=amd64 go build -mod=vendor -trimpath -o bin/documize-community-windows-amd64.exe ./edition/community.go
echo "Compiling for ARM..."
echo "Compiling for Linux AMD..."
env GOOS=linux GOARCH=amd64 go build -mod=vendor -trimpath -o bin/documize-community-linux-amd64 ./edition/community.go
echo "Compiling for Linux ARM..."
env GOOS=linux GOARCH=arm go build -mod=vendor -trimpath -o bin/documize-community-linux-arm ./edition/community.go
echo "Compiling for ARM64..."
echo "Compiling for Linux ARM64..."
env GOOS=linux GOARCH=arm64 go build -mod=vendor -trimpath -o bin/documize-community-linux-arm64 ./edition/community.go
echo "Compiling for FreeBSD ARM64..."
env GOOS=freebsd GOARCH=arm64 go build -mod=vendor -trimpath -o bin/documize-community-freebsd-arm64 ./edition/community.go
echo "Compiling for FreeBSD AMD64..."
env GOOS=freebsd GOARCH=amd64 go build -mod=vendor -trimpath -o bin/documize-community-freebsd-amd64 ./edition/community.go
echo "Finished."
# CGO_ENABLED=0 GOOS=linux go build -a -ldflags="-s -w" -installsuffix cgo
# go build -ldflags '-d -s -w' -a -tags netgo -installsuffix netgo test.go
# ldd test
# https://stackoverflow.com/questions/55664630/how-do-i-migrate-from-dep-to-go-modules

View file

@ -19,8 +19,8 @@ import (
"net/http"
"path/filepath"
"context"
api "github.com/documize/community/core/convapi"
"golang.org/x/net/context"
)
// 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"
api "github.com/documize/community/core/convapi"
"golang.org/x/net/context"
"context"
)
// Convert provides the entry-point into the document conversion process.

View file

@ -16,7 +16,7 @@ import (
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.

View file

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

View file

@ -16,7 +16,7 @@ import (
"github.com/documize/blackfriday"
"golang.org/x/net/context"
"context"
)
// 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)
}
}

70
core/asset/assets.go Normal file
View file

@ -0,0 +1,70 @@
package asset
import (
"embed"
"errors"
"io"
"io/fs"
"mime"
"net/http"
"path"
"path/filepath"
)
// GetPublicFileSystem
func GetPublicFileSystem(e embed.FS) (hfs http.FileSystem, err error) {
fsys, err := fs.Sub(e, "static/public")
if err != nil {
return nil, errors.New("failed GetPublicFileSystem")
}
return http.FS(fsys), nil
}
// FetchStatic loads static asset from embed file system.
func FetchStatic(e embed.FS, filename string) (content, contentType string, err error) {
data, err := e.ReadFile("static/" + filename)
if err != nil {
return
}
contentType = mime.TypeByExtension(filepath.Ext(filename))
content = string(data)
return
}
// FetchStaticDir returns filenames within specified directory
func FetchStaticDir(fs embed.FS, directory string) (files []string, err error) {
entries, err := fs.ReadDir("static/" + directory)
if err != nil {
return
}
for i := range entries {
if !entries[i].Type().IsDir() {
files = append(files, entries[i].Name())
}
}
return files, nil
}
// WriteStatic loads static asset from embed file system and writes to HTTP.
func WriteStatic(fs embed.FS, prefix, requestedPath string, w http.ResponseWriter) error {
f, err := fs.Open(path.Join(prefix, requestedPath))
if err != nil {
return err
}
defer f.Close()
stat, _ := f.Stat()
if stat.IsDir() {
return errors.New("cannot write static file")
}
contentType := mime.TypeByExtension(filepath.Ext(requestedPath))
w.Header().Set("Content-Type", contentType)
_, err = io.Copy(w, f)
return err
}

View file

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

View file

@ -26,7 +26,7 @@ func InstallUpgrade(runtime *env.Runtime, existingDB bool) (err error) {
// amLeader := false
// Get all SQL scripts.
scripts, err := LoadScripts()
scripts, err := LoadScripts(runtime)
if err != nil {
runtime.Log.Error("Database: unable to load scripts", err)
return

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

@ -12,11 +12,12 @@
package database
import (
"embed"
"fmt"
"sort"
"github.com/documize/community/core/asset"
"github.com/documize/community/core/env"
"github.com/documize/community/server/web"
)
// Scripts holds all .SQL files for all supported database providers.
@ -33,21 +34,19 @@ type Script struct {
}
// LoadScripts returns .SQL scripts for supported database providers.
func LoadScripts() (s Scripts, err error) {
assetDir := "bindata/scripts"
func LoadScripts(runtime *env.Runtime) (s Scripts, err error) {
// MySQL
s.MySQL, err = loadFiles(fmt.Sprintf("%s/mysql", assetDir))
s.MySQL, err = loadFiles(runtime.Assets, "scripts/mysql")
if err != nil {
return
}
// PostgreSQL
s.PostgreSQL, err = loadFiles(fmt.Sprintf("%s/postgresql", assetDir))
s.PostgreSQL, err = loadFiles(runtime.Assets, "scripts/postgresql")
if err != nil {
return
}
// PostgreSQL
s.SQLServer, err = loadFiles(fmt.Sprintf("%s/sqlserver", assetDir))
s.SQLServer, err = loadFiles(runtime.Assets, "scripts/sqlserver")
if err != nil {
return
}
@ -70,20 +69,22 @@ func SpecificScripts(runtime *env.Runtime, all Scripts) (s []Script) {
}
// loadFiles returns all SQL scripts in specified folder as [][]byte.
func loadFiles(path string) (b []Script, err error) {
buf := []byte{}
scripts, err := web.AssetDir(path)
func loadFiles(fs embed.FS, path string) (b []Script, err error) {
scripts, err := asset.FetchStaticDir(fs, path)
if err != nil {
return
}
sort.Strings(scripts)
for _, file := range scripts {
buf, err = web.Asset(fmt.Sprintf("%s/%s", path, file))
for i := range scripts {
filename := scripts[i]
sqlfile, _, err := asset.FetchStatic(fs, fmt.Sprintf("%s/%s", path, filename))
if err != nil {
return
return b, err
}
b = append(b, Script{Version: extractVersionNumber(file), Script: buf})
b = append(b, Script{Version: extractVersionNumber(filename), Script: []byte(sqlfile)})
}
return b, nil

View file

@ -1,3 +1,3 @@
/* 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 `message` `c_message` VARCHAR(500) NOT NULL,
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 `allowanonymousaccess` `c_anonaccess` BOOL NOT NULL DEFAULT 0,
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_message varchar(500) COLLATE ucs_basic NOT NULL,
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_anonaccess bool NOT NULL DEFAULT '0',
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_message NVARCHAR(500) COLLATE Latin1_General_CS_AS NOT NULL,
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_anonaccess BIT NOT NULL DEFAULT '0',
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

@ -14,7 +14,7 @@
html {
-webkit-font-smoothing: antialiased;
}
body {
font-family: 'Open Sans', sans-serif;
background-color: #1b75bb;
@ -22,54 +22,54 @@
color: #ffffff;
padding-top: 50px;
}
.container {
max-width: 1200px;
margin: 0 auto;
text-align: center;
}
.logo {
margin: 0 15px;
}
.content {
margin: 0 15px;
}
.content > div {
margin: 50px 0;
}
.content h1 {
font-size: 24px;
font-weight: 400;
text-transform: uppercase;
margin: 0 0 30px;
}
.content p {
font-size: 18px;
line-height: 28px;
margin: 30px 0 0 0;
}
.content .image {
text-align: center;
}
.clearfix {
overflow: auto;
zoom: 1;
}
.btn-main {
border: 1px solid #ffffff;
padding: 12px 20px;
border-radius: 5px;
margin-left: 25px;
}
@media (min-width: 768px) {
body {
margin-top: 100px;
@ -102,7 +102,7 @@
<body>
<div class="container">
<div class="logo">
<img src="/assets/img/setup/logo.png" alt="Documize">
<img src="/assets/img/setup/logo.png" alt="Documize Community">
</div>
<div class="content clearfix">
<div class="image">
@ -110,11 +110,11 @@
</div>
<div class="text">
<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>
</div>
</div>
</div>
</body>
</html>
</html>

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
SSLCertFile string // (optional) name of SSL certificate 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
Location string // reserved
ConfigSource string // tells us if configuration info was obtained from command line or config file
@ -43,6 +44,7 @@ type httpConfig struct {
ForceSSLPort int
Cert string
Key string
TLSVersion string
}
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.SSLCertFile = ct.HTTP.Cert
f.SSLKeyFile = ct.HTTP.Key
f.TLSVersion = ct.HTTP.TLSVersion
f.Location = strings.ToLower(ct.Install.Location)
if len(f.TLSVersion) == 0 {
f.TLSVersion = "1.3"
}
ok = true
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.
func commandLineEnv() (f Flags, ok bool) {
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(&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(&port, "port", false, "http/https port number")
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(&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"`)
@ -118,9 +124,14 @@ func commandLineEnv() (f Flags, ok bool) {
f.SiteMode = siteMode
f.SSLCertFile = certFile
f.SSLKeyFile = keyFile
f.TLSVersion = TLSVersion
f.Location = strings.ToLower(location)
f.ConfigSource = "flags/environment"
if len(f.TLSVersion) == 0 {
f.TLSVersion = "1.3"
}
return f, ok
}

2
core/env/runtime.go vendored
View file

@ -15,6 +15,7 @@ package env
import (
"context"
"database/sql"
"embed"
"github.com/documize/community/domain"
"github.com/jmoiron/sqlx"
@ -42,6 +43,7 @@ type Runtime struct {
StoreProvider StoreProvider
Log Logger
Product domain.Product
Assets embed.FS
}
// StartTx begins database transaction with given transaction isolation level.

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 {
case <-time.After(timeout):
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
//fmt.Println("DEBUG timeout")

View file

@ -48,8 +48,8 @@ func WriteServerError(w http.ResponseWriter, method string, err error) {
// WriteError notifies HTTP client of general application error.
func WriteError(w http.ResponseWriter, method string) {
writeStatus(w, http.StatusBadRequest)
w.Write([]byte("{Error: 'Internal server error'}"))
writeStatus(w, http.StatusBadRequest)
w.Write([]byte("{Error: 'Internal server error'}"))
}
// WriteDuplicateError notifies HTTP client of duplicate data that has been rejected.
@ -114,3 +114,17 @@ func WriteJSON(w http.ResponseWriter, v interface{}) {
j, _ := json.Marshal(v)
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.
#
# 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.
#
# You can move between editions anytime without any data loss
@ -29,7 +29,7 @@ services:
app:
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-enterprise-linux-amd64 && chmod 777 ./documize-enterprise-linux-amd64 && ./documize-enterprise-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:
- db
ports:

View file

@ -13,7 +13,6 @@ package activity
import (
"database/sql"
"fmt"
"time"
"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).
func (s Store) DeleteDocumentChangeActivity(ctx domain.RequestContext, documentID string) (rows int64, err error) {
rows, err = s.DeleteWhere(ctx.Transaction,
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))
_, 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)
if err == sql.ErrNoRows {
err = nil
}
return
}

View file

@ -13,7 +13,6 @@ package attachment
import (
"database/sql"
"fmt"
"strings"
"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.
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'",
ctx.OrgID, sectionID))
_, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_attachment WHERE c_orgid=? AND c_sectionid=?"), ctx.OrgID, sectionID)
if err == sql.ErrNoRows {
err = nil
}
return
}

View file

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

View file

@ -21,6 +21,7 @@ import (
"strings"
"github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/response"
"github.com/documize/community/core/secrets"
"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, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil {
result.Message = "Error: unable to get organization record"
result.Message = i18n.Localize(ctx.Locale, "server_err_org")
result.IsError = true
response.WriteJSON(w, result)
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
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
response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message)
@ -77,7 +78,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
c := ath.KeycloakConfig{}
err = json.Unmarshal([]byte(org.AuthConfig), &c)
if err != nil {
result.Message = "Error: unable read Keycloak configuration data"
result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error2")
result.IsError = true
response.WriteJSON(w, result)
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
kcUsers, err := Fetch(c)
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
response.WriteJSON(w, result)
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
dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999)
if err != nil {
result.Message = "Error: unable to fetch Documize users"
result.Message = i18n.Localize(ctx.Locale, "server_error_user")
result.IsError = true
response.WriteJSON(w, result)
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",
len(kcUsers), len(insert), missing)
result.Message = i18n.Localize(ctx.Locale, "server_keycloak_summary",
fmt.Sprintf("%d", len(kcUsers)), fmt.Sprintf("%d", len(insert)), fmt.Sprintf("%d", missing))
response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message)

View file

@ -21,6 +21,7 @@ import (
"strings"
"github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/response"
"github.com/documize/community/core/secrets"
"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, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil {
result.Message = "Error: unable to get organization record"
result.Message = i18n.Localize(ctx.Locale, "server_error_org")
result.IsError = true
response.WriteJSON(w, result)
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
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
response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message)
@ -166,7 +167,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
c := lm.LDAPConfig{}
err = json.Unmarshal([]byte(org.AuthConfig), &c)
if err != nil {
result.Message = "Error: unable read LDAP configuration data"
result.Message = i18n.Localize(ctx.Locale, "server_ldap_error2")
result.IsError = true
response.WriteJSON(w, result)
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.
ldapUsers, err := fetchUsers(c)
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
response.WriteJSON(w, result)
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
dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999)
if err != nil {
result.Message = "Error: unable to fetch Documize users"
result.Message = i18n.Localize(ctx.Locale, "server_error_user")
result.IsError = true
response.WriteJSON(w, result)
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.Message = "Sync complete with LDAP server"
result.Message = fmt.Sprintf(
"LDAP sync found %d users, %d new users added, %d users with missing data ignored",
len(ldapUsers), len(insert), missing)
result.Message = i18n.Localize(ctx.Locale, "server_ldap_complete")
result.Message = i18n.Localize(ctx.Locale, "server_ldap_summary", fmt.Sprintf("%d", len(ldapUsers)), fmt.Sprintf("%d", len(insert)), fmt.Sprintf("%d", missing))
h.Runtime.Log.Info(result.Message)
@ -327,21 +326,16 @@ func (h *Handler) Authenticate(w http.ResponseWriter, r *http.Request) {
h.Runtime.Log.Error(method, err)
return
}
if len(lu.Email) == 0 || len(u.Email) == 0 {
response.WriteUnauthorizedError(w)
h.Runtime.Log.Infof("LDAP user without email faild auth (%s)", username)
return
}
// Create user account if not found
// If user authenticated BUT is not within Documize, we fail authentication.
// If dual auth is enabled, we can try regular email/password login (see next).
if err == sql.ErrNoRows {
h.Runtime.Log.Info("Adding new LDAP user " + lu.Email + " @ " + dom)
u = convertUser(lc, lu)
u.Salt = secrets.GenerateSalt()
u.Password = secrets.GeneratePassword(secrets.GenerateRandomPassword(), u.Salt)
u, err = auth.AddExternalUser(ctx, h.Runtime, h.Store, u, lc.DefaultPermissionAddSpace)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
ok = false
}
}

View file

@ -20,8 +20,8 @@ import (
"github.com/documize/community/core/stringutil"
lm "github.com/documize/community/model/auth"
"github.com/documize/community/model/user"
ld "github.com/go-ldap/ldap/v3"
"github.com/pkg/errors"
ld "gopkg.in/ldap.v3"
)
// Connect establishes connection to LDAP server.
@ -213,12 +213,12 @@ func executeGroupFilter(c lm.LDAPConfig) (u []lm.LDAPUser, err error) {
// \5c,
//
// Relevant notes:
//
// https://docs.oracle.com/cd/E19424-01/820-4811/gdxpo/index.html#6ng8i269q
// https://devblogs.microsoft.com/scripting/how-can-i-work-with-a-cn-that-has-a-comma-in-it/
//
// Example:
//
// CN=Surname\, Name,OU=Something,OU=AD-Example,OU=Examaple,DC=example,DC=example,DC=com
//
// When we split on comma, here is our logic:

View file

@ -244,7 +244,7 @@ func (b backerHandler) dmzOrg(files *[]backupItem) (err error) {
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_sub,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS subscription,
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)
if err != nil {
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,
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_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
FROM dmz_user u`+w)
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,
c_domain, c_service, c_email, c_anonaccess, c_authprovider, c_authconfig,
c_maxtags, c_verified, c_serial, c_sub, c_active,
c_theme, c_logo, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
c_theme, c_logo, c_locale, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
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),
org[i].AllowAnonymousAccess, org[i].AuthProvider, org[i].AuthConfig,
org[i].MaxTags, r.Runtime.StoreProvider.IsTrue(), org[i].Serial,
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)
if err != nil {
r.Context.Transaction.Rollback()
@ -412,6 +412,7 @@ func (r *restoreHandler) dmzOrg() (err error) {
org[0].Title = r.Spec.Org.Title
org[0].Subscription = r.Spec.Org.Subscription
org[0].Theme = r.Spec.Org.Theme
org[0].Locale = r.Spec.Org.Locale
}
_, err = r.Context.Transaction.NamedExec(`UPDATE dmz_org SET
@ -425,7 +426,8 @@ func (r *restoreHandler) dmzOrg() (err error) {
c_message=:message,
c_title=:title,
c_serial=:serial,
c_sub=:subscription
c_sub=:subscription,
c_locale=:locale
WHERE c_refid=:refid`, &org[0])
if err != nil {
r.Context.Transaction.Rollback()
@ -1735,11 +1737,11 @@ func (r *restoreHandler) dmzUser() (err error) {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
INSERT INTO dmz_user
(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)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
c_password, c_salt, c_reset, c_active, c_lastversion, c_locale, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
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].LastVersion, u[i].Created, u[i].Revised)
u[i].LastVersion, u[i].Locale, u[i].Created, u[i].Revised)
if err != nil {
r.Context.Transaction.Rollback()

View file

@ -74,10 +74,10 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return
}
// Category max length 30.
// Category max length 50.
cat.Name = strings.TrimSpace(cat.Name)
if len(cat.Name) > 30 {
cat.Name = cat.Name[:30]
if len(cat.Name) > 50 {
cat.Name = cat.Name[:50]
}
err = h.Store.Category.Add(ctx, cat)

View file

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

View file

@ -44,6 +44,8 @@ type RequestContext struct {
GlobalAdmin bool
ViewUsers bool
Subscription Subscription
Locale string
OrgLocale string
}
//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
documentID := uniqueid.Generate()
document.RefID = documentID
document.Sequence = doc.Unsequenced
if r.Product.Edition == domain.CommunityEdition {
document.Lifecycle = workflow.LifecycleLive

View file

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

View file

@ -64,6 +64,13 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
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)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, id)
@ -82,7 +89,6 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
// draft mode does not record document views
if document.Lifecycle == workflow.LifecycleLive {
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
@ -95,9 +101,10 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypeRead})
ctx.Transaction.Commit()
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeDocumentView)
response.WriteJSON(w, document)
@ -270,6 +277,9 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
}
}
// d.Name = bluemonday.StrictPolicy().Sanitize(d.Name)
// d.Excerpt = bluemonday.StrictPolicy().Sanitize(d.Excerpt)
err = h.Store.Document.Update(ctx, d)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
@ -357,6 +367,13 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
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)
if err != nil {
response.WriteServerError(w, method, err)
@ -393,13 +410,6 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
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)
if err != nil {
ctx.Transaction.Rollback()
@ -557,6 +567,13 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
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)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, id)
@ -664,23 +681,22 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
// Get version information for this document.
v := []doc.Version{}
if len(document.GroupID) > 0 {
// Get versions.
// Get versions
vt, err := h.Store.Document.GetVersions(ctx, document.GroupID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// What about draft document versions?
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 {
if vt[i].Lifecycle == workflow.LifecycleLive {
v = append(v, vt[i])
}
// Determine which document versions user can see.
for i := range vt {
// Everyone can see live documents
if vt[i].Lifecycle == workflow.LifecycleLive {
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])
}
}
}
@ -706,13 +722,6 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
data.Versions = v
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 {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: document.SpaceID,
@ -821,10 +830,10 @@ func (h *Handler) Duplicate(w http.ResponseWriter, r *http.Request) {
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
var ok bool
ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted)
if !ok {
h.Runtime.Log.Info("unable to start transaction " + method)
return
}

View file

@ -83,7 +83,7 @@ func BuildExport(ctx domain.RequestContext, s store.Store, spec exportSpec) (htm
export.WriteString(`<meta charset="utf-8">`)
export.WriteString(`<meta http-equiv="X-UA-Compatible" content="IE=edge">`)
export.WriteString("<title>")
export.WriteString("Documize Export")
export.WriteString("Documize Community Export")
export.WriteString("</title>")
export.WriteString("<style>")
export.WriteString(baseCSS)
@ -96,7 +96,7 @@ func BuildExport(ctx domain.RequestContext, s store.Store, spec exportSpec) (htm
// Show title and timestamp.
generated := time.Now().UTC().Format(time.ANSIC)
export.WriteString(fmt.Sprintf("<h1 class='export-h1'>%s</h1>", "Documize Export"))
export.WriteString(fmt.Sprintf("<h1 class='export-h1'>%s</h1>", "Documize Community Export"))
export.WriteString(fmt.Sprintf("<div class='export-stamp'>%v</div>", generated))
// Spit out table of contents.
@ -419,7 +419,7 @@ const (
font-size: 1.1rem;
font-weight; normal;
}
.section-heading > .page-header {
margin: 2rem 0 2rem 0 !important;
}

View file

@ -254,31 +254,11 @@ func (s Store) MoveActivity(ctx domain.RequestContext, documentID, oldSpaceID, n
// Delete removes the specified document.
// Remove document pages, revisions, attachments, updates the search subsystem.
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))
if err != nil {
return
}
_, 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
}
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)
ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_doc_attachment WHERE c_orgid=? AND c_docid=?"), ctx.OrgID, documentID)
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)
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.
// Remove document pages, revisions, attachments, updates the search subsystem.
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))
if err != nil {
return
}
_, 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
}
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)
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)
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)
return s.DeleteConstrained(ctx.Transaction, "dmz_doc", ctx.OrgID, spaceID)
}

View file

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

View file

@ -13,7 +13,6 @@ package link
import (
"database/sql"
"fmt"
"strings"
"time"
@ -156,12 +155,18 @@ func (s Store) MarkOrphanAttachmentLink(ctx domain.RequestContext, attachmentID
// DeleteSourcePageLinks removes saved links for given source.
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.
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.

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;">
<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">
Document Approval Role Granted
{{.Subject}}
</td>
</tr>
<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;">
<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">
<p>You are requested to approve all changes to the following document:</p>
<p>{{.ActionText}}</p>
<p style="font-weight: bold;">{{.Document}}</p>
<p>{{.Inviter}}</p>
</td>
</td>1
</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;">View document</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>
<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>
</table>

View file

@ -16,6 +16,7 @@ package mail
import (
"fmt"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain/smtp"
)
@ -26,32 +27,32 @@ func (m *Mailer) DocumentApprover(recipient, inviterName, inviterEmail, url, doc
// check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague"
inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
}
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.ToName = recipient
em.ReplyTo = inviterEmail
em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct {
Subject string
Inviter string
URL string
Document string
SenderEmail string
ActionText string
ClickHere string
}{
em.Subject,
inviterName,
url,
document,
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)

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 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
</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 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;">
<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 their Documize account
{{.Subject}}
</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 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;">
<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>
<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>
</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;">
<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 Documize
{{.Subject}}
</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 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;">
<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">
Your temporary password: {{.Password}}
{{.Password}}
</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>
<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>
</table>

View file

@ -13,15 +13,17 @@ package mail
import (
"bytes"
"fmt"
"html/template"
"github.com/documize/community/core/asset"
"github.com/documize/community/core/env"
"github.com/documize/community/core/mail"
"github.com/documize/community/domain"
"github.com/documize/community/domain/setting"
ds "github.com/documize/community/domain/smtp"
"github.com/documize/community/domain/store"
"github.com/documize/community/server/web"
"github.com/pkg/errors"
)
// Mailer provides emailing facilities
@ -43,15 +45,15 @@ func (m *Mailer) Initialize() {
func (m *Mailer) ParseTemplate(filename string, params interface{}) (html string, err error) {
html = ""
file, err := web.ReadFile(filename)
content, _, err := asset.FetchStatic(m.Runtime.Assets, filename)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("missing %s", filename))
m.Runtime.Log.Error("failed to load mail template", err)
return
}
emailTemplate := string(file)
buffer := new(bytes.Buffer)
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
t := template.Must(template.New("emailTemplate").Parse(content))
t.Execute(buffer, &params)
html = buffer.String()

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;">
<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">
Your Documize password reset request
{{.Subject}}
</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">
Someone has requested to reset your Documize 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;">
<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>
</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>
<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>
</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;">
<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 shared {{.Folder}} with you
{{.Subject}}
</td>
</tr>
<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 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;">Login to 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>
<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>
</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;">
<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 shared {{.Folder}} with you on Documize
{{.Subject}}
</td>
</tr>
<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>
</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: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize 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;">
<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>
</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>
<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>
</table>

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

@ -20,6 +20,7 @@ import (
"github.com/pkg/errors"
"github.com/documize/community/core/asset"
"github.com/documize/community/core/env"
"github.com/documize/community/core/response"
"github.com/documize/community/core/uniqueid"
@ -28,7 +29,6 @@ import (
"github.com/documize/community/domain/store"
om "github.com/documize/community/model/onboard"
"github.com/documize/community/model/permission"
"github.com/documize/community/server/web"
)
// Handler contains the runtime information such as logging and database.
@ -112,14 +112,14 @@ func (h *Handler) loadFile(data om.SampleData, filename string, v interface{}) {
// Reads file and unmarshals content as JSON.
func (h *Handler) unpackFile(filename string, v interface{}) (err error) {
data, err := web.Embed.Asset("bindata/onboard/" + filename)
content, _, err := asset.FetchStatic(h.Runtime.Assets, "onboard/"+filename)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("missing %s", filename))
h.Runtime.Log.Error("failed to load file", err)
return
}
err = json.Unmarshal(data, &v)
err = json.Unmarshal([]byte(content), &v)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("failed to read %s as JSON", filename))
h.Runtime.Log.Error("failed to load file", err)

View file

@ -1,142 +0,0 @@
package organization
import (
"testing"
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/model/org"
"github.com/documize/community/domain/test"
)
// TestSpace tests all space database operations.
func TestOrganization(t *testing.T) {
rt, s, ctx := test.SetupTest()
//Create a new organization
var err error
org := org.Organization{}
orgID := uniqueid.Generate()
t.Run("AddOrginization", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
org.RefID = orgID
org.Company = "test"
org.Title = "test"
org.Message = "test"
org.Domain = "testDomain"
org.Active = true
err = s.Organization.AddOrganization(ctx, org)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add org organization")
}
ctx.Transaction.Commit()
orgGot, err := s.Organization.GetOrganization(ctx, org.RefID)
if err != nil || org.Title != orgGot.Title {
t.Error("failed to get org organization")
}
})
t.Run("GetOrganizationByDomain", func(t *testing.T) {
orgGot, err := s.Organization.GetOrganizationByDomain("testDomain")
if err != nil || org.Title != orgGot.Title {
t.Error("failed to get org organization by domain")
}
})
t.Run("UpdateOrginization", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
org.Title = "testUpdate"
err = s.Organization.UpdateOrganization(ctx, org)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to update org organization")
}
ctx.Transaction.Commit()
orgGot, err := s.Organization.GetOrganization(ctx, org.RefID)
if err != nil || org.Title != orgGot.Title {
t.Error("failed to get updated org organization")
}
})
t.Run("CheckDomain", func(t *testing.T) {
Domain := s.Organization.CheckDomain(ctx, "")
if Domain != Domain {
t.Error("failed to CheckDomain")
}
})
t.Run("UpdateAuthConfig", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
err = s.Organization.UpdateAuthConfig(ctx, org)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to update organization AuthConfig")
}
ctx.Transaction.Commit()
})
//
//Run after everything except delete as this makes an org inactive
//
t.Run("RemoveOrganization", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
err = s.Organization.RemoveOrganization(ctx, org.RefID)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to remove organization")
}
ctx.Transaction.Commit()
orgGot, err := s.Organization.GetOrganization(ctx, org.RefID)
if err != nil || orgGot.Active != false {
t.Error("failed to get removed organization activity")
}
})
//
// teardown code goes here
//
t.Run("DeleteOrganization", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
_,
err = s.Organization.DeleteOrganization(ctx, orgID)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to delete org organization")
}
ctx.Transaction.Commit()
})
}

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,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
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
WHERE c_refid=?`),
id)
@ -84,7 +84,7 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
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
WHERE c_domain=? AND c_active=`+s.IsTrue()),
subdomain)
@ -99,7 +99,7 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
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
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
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`,
&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.
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)
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 {
_, _ = 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.Transaction.Exec(s.Bind("DELETE FROM dmz_section_meta WHERE c_orgid=? AND c_sectionid=?"),
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
}
@ -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.
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'",
ctx.OrgID, pageID))
_, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_section_revision WHERE c_orgid=? AND c_sectionid=?"),
ctx.OrgID, pageID)
return
}

View file

@ -69,7 +69,7 @@ func CanViewDocument(ctx domain.RequestContext, s store.Store, documentID string
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 {
document, err := s.Document.Get(ctx, documentID)
@ -98,7 +98,7 @@ func CanChangeDocument(ctx domain.RequestContext, s store.Store, documentID stri
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 {
document, err := s.Document.Get(ctx, documentID)
@ -166,7 +166,6 @@ func CanManageSpace(ctx domain.RequestContext, s store.Store, spaceID string) bo
return false
}
// CanViewSpace returns if the user has permission to view the given spaceID.
func CanViewSpace(ctx domain.RequestContext, s store.Store, spaceID string) bool {
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.
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.
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.
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)
return s.DeleteWhere(ctx.Transaction, sql)
return
}
// 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) {
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)
return s.DeleteWhere(ctx.Transaction, sql)
return
}
// DeleteCategoryPermissions removes records from dmz_permissions table for given category ID.
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.
func (s Store) DeleteSpaceCategoryPermissions(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
sql := fmt.Sprintf(`
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')`,
_, 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=?)"),
ctx.OrgID, ctx.OrgID, spaceID)
return s.DeleteWhere(ctx.Transaction, sql)
return
}
// DeleteGroupPermissions removes all roles for the specified group
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)
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.
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.
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

@ -41,22 +41,7 @@ const (
CommunityEdition Edition = "Community"
// EnterpriseEdition is proprietary closed-source product.
EnterpriseEdition Edition = "Enterprise"
// PackageEssentials provides core capabilities.
PackageEssentials Package = "Essentials"
// PackageAdvanced provides analytics, reporting,
// content lifecycle, content verisoning, and audit logs.
PackageAdvanced Package = "Advanced"
// PackagePremium provides actions, feedback capture,
// approvals workflow, secure external sharing.
PackagePremium Package = "Premium"
// PackageDataCenter provides multi-tenanting
// and a bunch of professional services.
PackageDataCenter Package = "Data Center"
EnterpriseEdition Edition = "Community+"
// PlanCloud represents *.documize.com hosting.
PlanCloud Plan = "Cloud"
@ -64,6 +49,9 @@ const (
// PlanSelfHost represents privately hosted Documize instance.
PlanSelfHost Plan = "Self-host"
// SeatsFree is Five free users.
SeatsFree Seats = 5
// Seats0 is 0 users.
Seats0 Seats = 0
@ -120,8 +108,8 @@ func (p *Product) IsValid(ctx RequestContext) bool {
return true
}
} else {
// First 10 is free for Enterprise edition.
if Seats1 == ctx.Subscription.Seats && time.Now().UTC().Before(ctx.Subscription.End) {
// First 5 is free for Enterprise edition.
if SeatsFree == ctx.Subscription.Seats && time.Now().UTC().Before(ctx.Subscription.End) {
return true
}
}
@ -174,8 +162,10 @@ type SubscriptionUserAccount struct {
// SubscriptionAsXML returns subscription data as XML document:
//
// <DocumizeLicense>
// <Key>some key</Key>
// <Signature>some signature</Signature>
//
// <Key>some key</Key>
// <Signature>some signature</Signature>
//
// </DocumizeLicense>
//
// XML document is empty in case of error.

View file

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

View file

@ -54,5 +54,6 @@ func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
}
func embed(config, data string) string {
// return bluemonday.UGCPolicy().Sanitize(data)
return data
}

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

@ -47,6 +47,7 @@ func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.R
}
// Render converts markdown data into HTML suitable for browser rendering.
// See also https://github.com/yuin/goldmark
func (*Provider) Render(ctx *provider.Context, config, data string) string {
unsafe := blackfriday.Run([]byte(data))

View file

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

View file

@ -99,7 +99,8 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
var sp space.Space
sp.Name = model.Name
sp.Description = model.Description
// sp.Description = bluemonday.StrictPolicy().Sanitize(model.Description)
sp.Icon = model.Icon
sp.LabelID = model.LabelID
sp.RefID = uniqueid.Generate()

View file

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

View file

@ -1,234 +0,0 @@
package space
import (
"testing"
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain/test"
"github.com/documize/community/model/space"
)
// TestSpace tests all space database operations.
func TestSpace(t *testing.T) {
rt, s, ctx := test.SetupTest()
spaceID := uniqueid.Generate()
spaceID2 := uniqueid.Generate()
sp := space.Space{}
sp2 := space.Space{}
r := space.Role{}
r2 := space.Role{}
r3 := space.Role{}
var err error
t.Run("Add Space", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
sp.RefID = spaceID
sp.OrgID = ctx.OrgID
sp.UserID = ctx.UserID
sp.Type = space.ScopePublic
sp.Name = "PublicTestSpace"
sp.UserID = ctx.UserID
sp.Created = time.Now().UTC()
sp.Revised = time.Now().UTC()
err = s.Space.Add(ctx, sp)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add sp space")
}
perm := space.Permission{}
perm.OrgID = ctx.OrgID
perm.Who = "user"
perm.WhoID = ctx.UserID
perm.Scope = permission.ScopeRow
perm.Location = permission.LocationSpace
perm.RefID = spaceID
perm.Action = "" // we send array for actions below
err = s.Space.AddPermissions(ctx, perm, space.SpaceOwner, space.SpaceManage, space.SpaceView)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add permission")
}
ctx.Transaction.Commit()
spGet, err := s.Space.Get(ctx, sp.RefID)
if err != nil || sp.Name != spGet.Name {
t.Error("failed to get sp space")
}
})
t.Run("Update Space", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
sp, err := s.Space.Get(ctx, spaceID)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to get space prior to update")
return
}
sp.Name = "test update"
err = s.Space.Update(ctx, sp)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to update space")
return
}
ctx.Transaction.Commit()
sp, err = s.Space.Get(ctx, spaceID)
if err != nil || sp.Name != "test update" {
t.Error("failed to get the space after update")
}
})
t.Run("Get All", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
sp2.UserID = ctx.UserID
sp2.RefID = spaceID2
sp2.OrgID = ctx.OrgID
sp2.Type = space.ScopePrivate
sp2.Name = "PrivateTestSpace"
sp.UserID = ctx.UserID
sp.Created = time.Now().UTC()
sp.Revised = time.Now().UTC()
err = s.Space.Add(ctx, sp2)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add sp2")
}
perm := space.Permission{}
perm.OrgID = ctx.OrgID
perm.Who = "user"
perm.WhoID = ctx.UserID
perm.Scope = permission.ScopeRow
perm.Location = permission.LocationSpace
perm.RefID = spaceID2
perm.Action = "" // we send array for actions below
err = s.Space.AddPermissions(ctx, perm, space.SpaceOwner, space.SpaceManage, space.SpaceView)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add permission")
}
ctx.Transaction.Commit()
spSlice, err := s.Space.GetAll(ctx)
if err != nil || spSlice == nil {
t.Error("failed to get all spaces")
}
})
t.Run("PublicSpaces", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
spSlice, err := s.Space.PublicSpaces(ctx, sp.OrgID)
if err != nil || spSlice == nil {
t.Error("failed to get public spaces")
}
ctx.Transaction.Commit()
})
t.Run("Change Owner", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
if err != nil {
return
}
newUserID := "Updated Owner"
err := s.Space.ChangeOwner(ctx, sp.UserID, newUserID)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to change Owner")
return
}
ctx.Transaction.Commit()
sp, err = s.Space.Get(ctx, spaceID)
if err != nil || sp.UserID != newUserID {
t.Error("failed to get space w/ new owner")
}
})
t.Run("Add Role", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
perm := space.Permission{}
perm.OrgID = ctx.OrgID
perm.Who = "user"
perm.WhoID = ctx.UserID
perm.Scope = permission.ScopeRow
perm.Location = permission.LocationSpace
perm.RefID = spaceID
perm.Action = "" // we send array for actions below
err = s.Space.AddPermissions(ctx, perm, space.DocumentAdd, space.DocumentDelete, space.DocumentMove)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add permission")
}
ctx.Transaction.Commit()
roles, err := s.Space.GetUserPermissions(ctx, spaceID)
if err != nil || roles == nil {
t.Error("Could not get any roles")
return
}
// TODO: could we Verify the role was added with the if r3.UserID == Returned.UserID?
})
t.Run("Get User Permissions", func(t *testing.T) {
userRoles, err := s.Space.GetUserPermissions(ctx, spaceID)
if err != nil || userRoles == nil {
t.Error("failed to get user roles")
return
}
})
// teardown
t.Run("Delete space", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
_, err = s.Space.Delete(ctx, spaceID)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to delete space")
return
}
ctx.Transaction.Commit()
})
t.Run("Delete space 2", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
_, err = s.Space.Delete(ctx, spaceID2)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to delete space in teardown")
return
}
ctx.Transaction.Commit()
})
}

View file

@ -27,7 +27,7 @@ type Context struct {
// 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.
// MS SQL Server uses @p1, @p2, @p3.
func (c *Context) Bind(sql string) string {
@ -86,20 +86,6 @@ func (c *Context) DeleteConstrainedWithID(tx *sqlx.Tx, table string, orgID, id s
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.
func (c *Context) EmptyJSON() string {
return c.Runtime.StoreProvider.JSONEmpty()

View file

@ -22,6 +22,7 @@ import (
"github.com/documize/community/core/env"
"github.com/documize/community/core/event"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/request"
"github.com/documize/community/core/response"
"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) {
method := "template.saved"
ctx := domain.GetRequestContext(r)
unseq := doc.Unsequenced
if !h.Runtime.Product.IsValid(ctx) {
response.WriteBadLicense(w)
@ -148,6 +150,7 @@ func (h *Handler) SaveAs(w http.ResponseWriter, r *http.Request) {
doc.ID = 0
doc.Template = true
doc.Lifecycle = workflow.LifecycleLive
doc.Sequence = unseq
// Duplicate pages and associated meta
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{}
d.Name = docTitle
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.Tags = ""
d.SpaceID = spaceID
@ -341,6 +344,7 @@ func (h *Handler) Use(w http.ResponseWriter, r *http.Request) {
d.SpaceID = spaceID
d.UserID = ctx.UserID
d.Name = docTitle
d.Sequence = doc.Unsequenced
if h.Runtime.Product.Edition == domain.CommunityEdition {
d.Lifecycle = workflow.LifecycleLive

View file

@ -1,62 +0,0 @@
package test
import (
"fmt"
"github.com/documize/community/domain/store"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/edition/boot"
"github.com/documize/community/edition/logging"
"github.com/documize/community/embed"
"github.com/documize/community/server/web"
_ "github.com/go-sql-driver/mysql" // testing
)
// SetupTest prepares test environment
func SetupTest() (rt *env.Runtime, s *store
.Store, ctx domain.RequestContext) {
rt, s = startRuntime()
ctx = setupContext()
return rt, s, ctx
}
func startRuntime() (rt *env.Runtime, s *store.Store) {
rt = new(env.Runtime)
s = new(store.Store)
rt.Log = logging.NewLogger(false)
web.Embed = embed.NewEmbedder()
rt.Product = env.Product{}
rt.Product.Major = "0"
rt.Product.Minor = "0"
rt.Product.Patch = "0"
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
rt.Product.Edition = "Test"
rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition)
// parse settings from command line and environment
rt.Flags = env.ParseFlags()
boot.InitRuntime(rt, s)
// section.Register(rt, s)
return rt, s
}
// setup testing context
func setupContext() domain.RequestContext {
ctx := domain.RequestContext{}
ctx.AllowAnonymousAccess = true
ctx.Authenticated = true
ctx.Administrator = true
ctx.Guest = false
ctx.Editor = true
ctx.GlobalAdmin = true
ctx.UserID = "test"
ctx.OrgID = "test"
return ctx
}
// For dummy user data https://www.mockaroo.com

View file

@ -26,6 +26,7 @@ import (
"github.com/documize/community/core/event"
"github.com/documize/community/core/request"
"github.com/documize/community/core/response"
"github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/core/stringutil"
@ -135,6 +136,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
if addUser {
userID = uniqueid.Generate()
userModel.RefID = userID
userModel.Locale = ctx.OrgLocale
err = h.Store.User.Add(ctx, userModel)
if err != nil {
@ -506,6 +508,21 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
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.Admin = u.Admin
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.Lastname = strings.TrimSpace(v[1])
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 {
h.Runtime.Log.Info(method + " missing firstname, lastname, or email")

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