Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8eaaf5e6c0 | ||
|
|
b6eb20b71d | ||
|
|
63e2ee30e9 | ||
|
|
5d31ca8ec3 | ||
|
|
cc29b6f89e | ||
|
|
b3ef70bcd4 | ||
|
|
0a70d7c1e7 | ||
|
|
ddd0a14b3e | ||
|
|
09a38afa57 | ||
|
|
217e61d9ad | ||
|
|
6a761aca54 |
4
.env
4
.env
@@ -1,6 +1,6 @@
|
|||||||
# do not use quotes (")
|
# do not use quotes (")
|
||||||
MYDOMAIN=overleaf.mildstone.org
|
MYDOMAIN=MYDOMAIN.TLD
|
||||||
MYMAIL=andrea.rgn@gmail.com
|
MYMAIL=MYEMAIL@MYDOMAIN.TLD
|
||||||
MYDATA=/data
|
MYDATA=/data
|
||||||
LOGIN_TEXT=username
|
LOGIN_TEXT=username
|
||||||
COLLAB_TEXT=Direct share with collaborators is enabled only for activated users!
|
COLLAB_TEXT=Direct share with collaborators is enabled only for activated users!
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -4,7 +4,7 @@ build:
|
|||||||
docker build --build-arg login_text="${LOGIN_TEXT}" \
|
docker build --build-arg login_text="${LOGIN_TEXT}" \
|
||||||
--build-arg collab_text="${COLLAB_TEXT}" \
|
--build-arg collab_text="${COLLAB_TEXT}" \
|
||||||
--build-arg admin_is_sysadmin="${ADMIN_IS_SYSADMIN}" \
|
--build-arg admin_is_sysadmin="${ADMIN_IS_SYSADMIN}" \
|
||||||
-t "ldap-overleaf-sl:240728" ldap-overleaf-sl
|
-t "ldap-overleaf-sl" ldap-overleaf-sl
|
||||||
|
|
||||||
clean: check_clean
|
clean: check_clean
|
||||||
docker-compose down
|
docker-compose down
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -2,11 +2,27 @@
|
|||||||
|
|
||||||
This repo contains an improved, free ldap authentication and authorisation
|
This repo contains an improved, free ldap authentication and authorisation
|
||||||
for sharelatex/[overleaf](https://github.com/overleaf/overleaf) community
|
for sharelatex/[overleaf](https://github.com/overleaf/overleaf) community
|
||||||
edition. Currently this repo uses `sharelatex/sharelatex:4.2.0`.
|
edition. Currently this repo uses `sharelatex/sharelatex:5.0.6`.
|
||||||
|
|
||||||
The inital idea for this implementation was taken from
|
The inital idea for this implementation was taken from
|
||||||
[worksasintended](https://github.com/worksasintended).
|
[worksasintended](https://github.com/worksasintended).
|
||||||
|
|
||||||
|
## Upgrading from 4.x to 5.0
|
||||||
|
- enter mongo database container and open a mongo shell
|
||||||
|
```mongo```
|
||||||
|
- execute
|
||||||
|
```db.adminCommand({ setFeatureCompatibilityVersion: "4.4" })```
|
||||||
|
in the MongoDB shell.
|
||||||
|
- Then upgrade:
|
||||||
|
```
|
||||||
|
bash scripts/extract_files.sh 5.0.6
|
||||||
|
bash scripts/apply_diffs.sh
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** TrackChanges uses newer versions of Feature/Chat and Feature/DocumentUpdater - those are pulled during make directly from the Overleaf git repository.
|
||||||
|
|
||||||
|
|
||||||
## BREAKING CHANGE
|
## BREAKING CHANGE
|
||||||
|
|
||||||
Be careful if you try to migrate from 3.3.2! Backup your machines and data. The migration paths should be:
|
Be careful if you try to migrate from 3.3.2! Backup your machines and data. The migration paths should be:
|
||||||
|
|||||||
@@ -19,40 +19,40 @@ services:
|
|||||||
- redis
|
- redis
|
||||||
- simple-certbot
|
- simple-certbot
|
||||||
volumes:
|
volumes:
|
||||||
- ${MYDATA}/sharelatex:/var/lib/sharelatex
|
- ${MYDATA}/sharelatex:/var/lib/overleaf
|
||||||
- ${MYDATA}/letsencrypt:/etc/letsencrypt
|
- ${MYDATA}/letsencrypt:/etc/letsencrypt
|
||||||
- ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
|
- ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
|
||||||
environment:
|
environment:
|
||||||
SHARELATEX_APP_NAME: Overleaf
|
OVERLEAF_APP_NAME: Overleaf
|
||||||
SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex
|
OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex
|
||||||
SHARELATEX_SITE_URL: https://${MYDOMAIN}
|
OVERLEAF_SITE_URL: https://${MYDOMAIN}
|
||||||
SHARELATEX_NAV_TITLE: Overleaf - run by ${MYDOMAIN}
|
OVERLEAF_NAV_TITLE: Overleaf - run by ${MYDOMAIN}
|
||||||
#SHARELATEX_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg
|
#OVERLEAF_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg
|
||||||
SHARELATEX_ADMIN_EMAIL: ${MYMAIL}
|
OVERLEAF_ADMIN_EMAIL: ${MYMAIL}
|
||||||
SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"} ]'
|
OVERLEAF_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"} ]'
|
||||||
SHARELATEX_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]'
|
OVERLEAF_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]'
|
||||||
SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}"
|
OVERLEAF_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}"
|
||||||
# SHARELATEX_EMAIL_AWS_SES_ACCESS_KEY_ID:
|
# OVERLEAF_EMAIL_AWS_SES_ACCESS_KEY_ID:
|
||||||
# SHARELATEX_EMAIL_AWS_SES_SECRET_KEY:
|
# OVERLEAF_EMAIL_AWS_SES_SECRET_KEY:
|
||||||
SHARELATEX_EMAIL_SMTP_HOST: smtp.${MYDOMAIN}
|
OVERLEAF_EMAIL_SMTP_HOST: smtp.${MYDOMAIN}
|
||||||
SHARELATEX_EMAIL_SMTP_PORT: 587
|
OVERLEAF_EMAIL_SMTP_PORT: 587
|
||||||
SHARELATEX_EMAIL_SMTP_SECURE: "false"
|
OVERLEAF_EMAIL_SMTP_SECURE: "false"
|
||||||
# SHARELATEX_EMAIL_SMTP_USER:
|
# OVERLEAF_EMAIL_SMTP_USER:
|
||||||
# SHARELATEX_EMAIL_SMTP_PASS:
|
# OVERLEAF_EMAIL_SMTP_PASS:
|
||||||
# SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
|
# OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
|
||||||
# SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false
|
# OVERLEAF_EMAIL_SMTP_IGNORE_TLS: false
|
||||||
SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues."
|
OVERLEAF_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues."
|
||||||
|
|
||||||
# make public links accessible w/o login (link sharing issue)
|
# make public links accessible w/o login (link sharing issue)
|
||||||
# https://github.com/overleaf/docker-image/issues/66
|
# https://github.com/overleaf/docker-image/issues/66
|
||||||
# https://github.com/overleaf/overleaf/issues/628
|
# https://github.com/overleaf/overleaf/issues/628
|
||||||
# https://github.com/overleaf/web/issues/367
|
# https://github.com/overleaf/web/issues/367
|
||||||
# Fixed in 2.0.2 (Release date: 2019-11-26)
|
# Fixed in 2.0.2 (Release date: 2019-11-26)
|
||||||
SHARELATEX_ALLOW_PUBLIC_ACCESS: "true"
|
OVERLEAF_ALLOW_PUBLIC_ACCESS: "true"
|
||||||
SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true"
|
OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true"
|
||||||
|
|
||||||
SHARELATEX_SECURE_COOKIE: "true"
|
OVERLEAF_SECURE_COOKIE: "true"
|
||||||
SHARELATEX_BEHIND_PROXY: "true"
|
OVERLEAF_BEHIND_PROXY: "true"
|
||||||
|
|
||||||
LDAP_SERVER: ldaps://LDAPSERVER:636
|
LDAP_SERVER: ldaps://LDAPSERVER:636
|
||||||
LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD
|
LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD
|
||||||
@@ -99,7 +99,7 @@ services:
|
|||||||
|
|
||||||
# Same property, unfortunately with different names in
|
# Same property, unfortunately with different names in
|
||||||
# different locations
|
# different locations
|
||||||
SHARELATEX_REDIS_HOST: redis
|
OVERLEAF_REDIS_HOST: redis
|
||||||
REDIS_HOST: redis
|
REDIS_HOST: redis
|
||||||
REDIS_PORT: 6379
|
REDIS_PORT: 6379
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ services:
|
|||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
restart: always
|
restart: always
|
||||||
image: mongo:4.4
|
image: mongo:5.0
|
||||||
container_name: mongo
|
container_name: mongo
|
||||||
expose:
|
expose:
|
||||||
- 27017
|
- 27017
|
||||||
@@ -125,7 +125,7 @@ services:
|
|||||||
|
|
||||||
# See also: https://github.com/overleaf/overleaf/issues/1120
|
# See also: https://github.com/overleaf/overleaf/issues/1120
|
||||||
mongoinit:
|
mongoinit:
|
||||||
image: mongo:4.4
|
image: mongo:5.0
|
||||||
# this container will exit after executing the command
|
# this container will exit after executing the command
|
||||||
restart: "no"
|
restart: "no"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ services:
|
|||||||
- mongo
|
- mongo
|
||||||
- redis
|
- redis
|
||||||
volumes:
|
volumes:
|
||||||
- ${MYDATA}/sharelatex:/var/lib/sharelatex
|
- ${MYDATA}/sharelatex:/var/lib/overleaf
|
||||||
- ${MYDATA}/letsencrypt:/etc/letsencrypt:ro
|
- ${MYDATA}/letsencrypt:/etc/letsencrypt:ro
|
||||||
# - ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
|
# - ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
|
||||||
labels:
|
labels:
|
||||||
@@ -106,34 +106,34 @@ services:
|
|||||||
- "traefik.http.services.sharel.loadbalancer.sticky.cookie.samesite=io"
|
- "traefik.http.services.sharel.loadbalancer.sticky.cookie.samesite=io"
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
SHARELATEX_APP_NAME: Overleaf
|
OVERLEAF_APP_NAME: Overleaf
|
||||||
SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex
|
OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex
|
||||||
SHARELATEX_SITE_URL: https://${MYDOMAIN}
|
OVERLEAF_SITE_URL: https://${MYDOMAIN}
|
||||||
SHARELATEX_NAV_TITLE: Overleaf - run by ${MYDOMAIN}
|
OVERLEAF_NAV_TITLE: Overleaf - run by ${MYDOMAIN}
|
||||||
#SHARELATEX_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg
|
#OVERLEAF_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg
|
||||||
SHARELATEX_ADMIN_EMAIL: ${MYMAIL}
|
OVERLEAF_ADMIN_EMAIL: ${MYMAIL}
|
||||||
SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"} ]'
|
OVERLEAF_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"} ]'
|
||||||
SHARELATEX_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]'
|
OVERLEAF_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]'
|
||||||
SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}"
|
OVERLEAF_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}"
|
||||||
SHARELATEX_EMAIL_SMTP_HOST: smtp.${MYDOMAIN}
|
OVERLEAF_EMAIL_SMTP_HOST: smtp.${MYDOMAIN}
|
||||||
SHARELATEX_EMAIL_SMTP_PORT: 587
|
OVERLEAF_EMAIL_SMTP_PORT: 587
|
||||||
SHARELATEX_EMAIL_SMTP_SECURE: "false"
|
OVERLEAF_EMAIL_SMTP_SECURE: "false"
|
||||||
# SHARELATEX_EMAIL_SMTP_USER:
|
# OVERLEAF_EMAIL_SMTP_USER:
|
||||||
# SHARELATEX_EMAIL_SMTP_PASS:
|
# OVERLEAF_EMAIL_SMTP_PASS:
|
||||||
# SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
|
# OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
|
||||||
# SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false
|
# OVERLEAF_EMAIL_SMTP_IGNORE_TLS: false
|
||||||
SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues."
|
OVERLEAF_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues."
|
||||||
|
|
||||||
# make public links accessible w/o login (link sharing issue)
|
# make public links accessible w/o login (link sharing issue)
|
||||||
# https://github.com/overleaf/docker-image/issues/66
|
# https://github.com/overleaf/docker-image/issues/66
|
||||||
# https://github.com/overleaf/overleaf/issues/628
|
# https://github.com/overleaf/overleaf/issues/628
|
||||||
# https://github.com/overleaf/web/issues/367
|
# https://github.com/overleaf/web/issues/367
|
||||||
# Fixed in 2.0.2 (Release date: 2019-11-26)
|
# Fixed in 2.0.2 (Release date: 2019-11-26)
|
||||||
SHARELATEX_ALLOW_PUBLIC_ACCESS: "true"
|
OVERLEAF_ALLOW_PUBLIC_ACCESS: "true"
|
||||||
SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true"
|
OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true"
|
||||||
|
|
||||||
SHARELATEX_SECURE_COOKIE: "true"
|
OVERLEAF_SECURE_COOKIE: "true"
|
||||||
SHARELATEX_BEHIND_PROXY: "true"
|
OVERLEAF_BEHIND_PROXY: "true"
|
||||||
|
|
||||||
LDAP_SERVER: ldaps://LDAPSERVER:636
|
LDAP_SERVER: ldaps://LDAPSERVER:636
|
||||||
LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD
|
LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD
|
||||||
@@ -180,7 +180,7 @@ services:
|
|||||||
|
|
||||||
# Same property, unfortunately with different names in
|
# Same property, unfortunately with different names in
|
||||||
# different locations
|
# different locations
|
||||||
SHARELATEX_REDIS_HOST: redis
|
OVERLEAF_REDIS_HOST: redis
|
||||||
REDIS_HOST: redis
|
REDIS_HOST: redis
|
||||||
REDIS_PORT: 6379
|
REDIS_PORT: 6379
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ services:
|
|||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
restart: always
|
restart: always
|
||||||
image: mongo:4.4
|
image: mongo:5.0
|
||||||
container_name: mongo
|
container_name: mongo
|
||||||
expose:
|
expose:
|
||||||
- 27017
|
- 27017
|
||||||
@@ -214,7 +214,7 @@ services:
|
|||||||
|
|
||||||
# See also: https://github.com/overleaf/overleaf/issues/1120
|
# See also: https://github.com/overleaf/overleaf/issues/1120
|
||||||
mongoinit:
|
mongoinit:
|
||||||
image: mongo:4.4
|
image: mongo:5.0
|
||||||
# this container will exit after executing the command
|
# this container will exit after executing the command
|
||||||
restart: "no"
|
restart: "no"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: "2.2"
|
|||||||
services:
|
services:
|
||||||
sharelatex:
|
sharelatex:
|
||||||
restart: always
|
restart: always
|
||||||
image: ldap-overleaf-sl:240728
|
image: ldap-overleaf-sl
|
||||||
container_name: ldap-overleaf-sl
|
container_name: ldap-overleaf-sl
|
||||||
depends_on:
|
depends_on:
|
||||||
mongo:
|
mongo:
|
||||||
@@ -16,72 +16,68 @@ services:
|
|||||||
- mongo
|
- mongo
|
||||||
- redis
|
- redis
|
||||||
volumes:
|
volumes:
|
||||||
- ${MYDATA}/sharelatex:/var/lib/sharelatex
|
- ${MYDATA}/sharelatex:/var/lib/overleaf
|
||||||
- ${MYDATA}/letsencrypt:/etc/letsencrypt
|
- ${MYDATA}/letsencrypt:/etc/letsencrypt
|
||||||
- ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
|
- ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
|
||||||
environment:
|
environment:
|
||||||
SHARELATEX_APP_NAME: Overleaf
|
OVERLEAF_APP_NAME: Overleaf
|
||||||
SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex
|
OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex
|
||||||
SHARELATEX_SITE_URL: https://${MYDOMAIN}
|
OVERLEAF_SITE_URL: https://${MYDOMAIN}
|
||||||
SHARELATEX_NAV_TITLE: Overleaf - run by ${MYDOMAIN}
|
OVERLEAF_NAV_TITLE: Overleaf - run by ${MYDOMAIN}
|
||||||
#SHARELATEX_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg
|
#OVERLEAF_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg
|
||||||
SHARELATEX_ADMIN_EMAIL: ${MYMAIL}
|
OVERLEAF_ADMIN_EMAIL: ${MYMAIL}
|
||||||
SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"} ]'
|
OVERLEAF_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"} ]'
|
||||||
SHARELATEX_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]'
|
OVERLEAF_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]'
|
||||||
SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}"
|
OVERLEAF_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}"
|
||||||
# SHARELATEX_EMAIL_AWS_SES_ACCESS_KEY_ID:
|
# OVERLEAF_EMAIL_AWS_SES_ACCESS_KEY_ID:
|
||||||
# SHARELATEX_EMAIL_AWS_SES_SECRET_KEY:
|
# OVERLEAF_EMAIL_AWS_SES_SECRET_KEY:
|
||||||
SHARELATEX_EMAIL_SMTP_HOST: 192.168.1.99
|
OVERLEAF_EMAIL_SMTP_HOST: smtp.${MYDOMAIN}
|
||||||
SHARELATEX_EMAIL_SMTP_PORT: 25
|
OVERLEAF_EMAIL_SMTP_PORT: 587
|
||||||
SHARELATEX_EMAIL_SMTP_SECURE: "false"
|
OVERLEAF_EMAIL_SMTP_SECURE: "false"
|
||||||
# SHARELATEX_EMAIL_SMTP_USER:
|
# OVERLEAF_EMAIL_SMTP_USER:
|
||||||
# SHARELATEX_EMAIL_SMTP_PASS:
|
# OVERLEAF_EMAIL_SMTP_PASS:
|
||||||
# SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
|
# OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
|
||||||
# SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false
|
# OVERLEAF_EMAIL_SMTP_IGNORE_TLS: false
|
||||||
SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues."
|
OVERLEAF_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues."
|
||||||
|
|
||||||
#LOG_LEVEL: "debug"
|
|
||||||
|
|
||||||
# make public links accessible w/o login (link sharing issue)
|
# make public links accessible w/o login (link sharing issue)
|
||||||
# https://github.com/overleaf/docker-image/issues/66
|
# https://github.com/overleaf/docker-image/issues/66
|
||||||
# https://github.com/overleaf/overleaf/issues/628
|
# https://github.com/overleaf/overleaf/issues/628
|
||||||
# https://github.com/overleaf/web/issues/367
|
# https://github.com/overleaf/web/issues/367
|
||||||
# Fixed in 2.0.2 (Release date: 2019-11-26)
|
# Fixed in 2.0.2 (Release date: 2019-11-26)
|
||||||
SHARELATEX_ALLOW_PUBLIC_ACCESS: "true"
|
OVERLEAF_ALLOW_PUBLIC_ACCESS: "true"
|
||||||
SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true"
|
OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true"
|
||||||
|
|
||||||
# Uncomment the following line to enable secure cookies if you are using SSL
|
# Uncomment the following line to enable secure cookies if you are using SSL
|
||||||
# SHARELATEX_SECURE_COOKIE: "true"
|
# OVERLEAF_SECURE_COOKIE: "true"
|
||||||
# SHARELATEX_BEHIND_PROXY: "true"
|
# OVERLEAF_BEHIND_PROXY: "true"
|
||||||
|
|
||||||
LDAP_SERVER: ldap://ipa.mildstone.org:389
|
LDAP_SERVER: ldaps://LDAPSERVER:636
|
||||||
LDAP_BASE: cn=users,cn=accounts,dc=mildstone,dc=org
|
LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD
|
||||||
|
|
||||||
### There are to ways get users from the ldap server
|
### There are to ways get users from the ldap server
|
||||||
|
|
||||||
## NO LDAP BIND USER:
|
## NO LDAP BIND USER:
|
||||||
# Tries directly to bind with the login user (as uid)
|
# Tries directly to bind with the login user (as uid)
|
||||||
# LDAP_BINDDN: uid=%u,ou=someunit,ou=people,dc=DOMAIN,dc=TLD
|
# LDAP_BINDDN: uid=%u,ou=someunit,ou=people,dc=DOMAIN,dc=TLD
|
||||||
# LDAP_BINDDN: uid=%u,ou=people,dc=mildstone,dc=org
|
|
||||||
|
|
||||||
## Or you can use ai global LDAP_BIND_USER
|
## Or you can use ai global LDAP_BIND_USER
|
||||||
LDAP_BIND_USER: uid=ldapsearch,cn=users,cn=accounts,dc=mildstone,dc=org
|
# LDAP_BIND_USER:
|
||||||
LDAP_BIND_PW: ldap_ha39it9
|
# LDAP_BIND_PW:
|
||||||
|
|
||||||
# Only allow users matching LDAP_USER_FILTER
|
# Only allow users matching LDAP_USER_FILTER
|
||||||
LDAP_USER_FILTER: "(&(memberof=cn=latexusers,cn=groups,cn=accounts,dc=mildstone,dc=org)(uid=%u))"
|
LDAP_USER_FILTER: "(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
|
||||||
|
|
||||||
# If user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true.
|
# If user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true.
|
||||||
# Admin Users can invite external (non ldap) users. This feature makes only sense
|
# Admin Users can invite external (non ldap) users. This feature makes only sense
|
||||||
# when ALLOW_EMAIL_LOGIN is set to 'true'. Additionally admins can send
|
# when ALLOW_EMAIL_LOGIN is set to 'true'. Additionally admins can send
|
||||||
# system wide messages.
|
# system wide messages.
|
||||||
LDAP_ADMIN_GROUP_FILTER: "(memberof=cn=admins,cn=groups,cn=accounts,dc=mildstone,dc=org)"
|
LDAP_ADMIN_GROUP_FILTER: "(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
|
||||||
ALLOW_EMAIL_LOGIN: "true"
|
ALLOW_EMAIL_LOGIN: "true"
|
||||||
|
|
||||||
# All users in the LDAP_CONTACT_FILTER are loaded from the ldap server into contacts.
|
# All users in the LDAP_CONTACT_FILTER are loaded from the ldap server into contacts.
|
||||||
LDAP_CONTACT_FILTER: "(memberof=cn=latexusers,cn=groups,cn=accounts,dc=mildstone,dc=org)"
|
LDAP_CONTACT_FILTER: "(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
|
||||||
LDAP_CONTACTS: "true"
|
LDAP_CONTACTS: "false"
|
||||||
|
|
||||||
|
|
||||||
## OAuth2 Settings
|
## OAuth2 Settings
|
||||||
# OAUTH2_ENABLED: "true"
|
# OAUTH2_ENABLED: "true"
|
||||||
@@ -101,7 +97,7 @@ services:
|
|||||||
|
|
||||||
# Same property, unfortunately with different names in
|
# Same property, unfortunately with different names in
|
||||||
# different locations
|
# different locations
|
||||||
SHARELATEX_REDIS_HOST: redis
|
OVERLEAF_REDIS_HOST: redis
|
||||||
REDIS_HOST: redis
|
REDIS_HOST: redis
|
||||||
REDIS_PORT: 6379
|
REDIS_PORT: 6379
|
||||||
|
|
||||||
@@ -112,7 +108,7 @@ services:
|
|||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
restart: always
|
restart: always
|
||||||
image: mongo:4.4
|
image: mongo:5.0
|
||||||
container_name: mongo
|
container_name: mongo
|
||||||
expose:
|
expose:
|
||||||
- 27017
|
- 27017
|
||||||
@@ -127,7 +123,7 @@ services:
|
|||||||
|
|
||||||
# See also: https://github.com/overleaf/overleaf/issues/1120
|
# See also: https://github.com/overleaf/overleaf/issues/1120
|
||||||
mongoinit:
|
mongoinit:
|
||||||
image: mongo:4.4
|
image: mongo:5.0
|
||||||
# this container will exit after executing the command
|
# this container will exit after executing the command
|
||||||
restart: "no"
|
restart: "no"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM sharelatex/sharelatex:4.2.0
|
FROM sharelatex/sharelatex:5.0.6
|
||||||
# FROM sharelatex/sharelatex:latest
|
# FROM sharelatex/sharelatex:latest
|
||||||
# latest might not be tested
|
# latest might not be tested
|
||||||
# e.g. the AuthenticationManager.js script had to be adapted after versions 2.3.1
|
# e.g. the AuthenticationManager.js script had to be adapted after versions 2.3.1
|
||||||
@@ -13,40 +13,54 @@ ARG admin_is_sysadmin
|
|||||||
# set workdir (might solve issue #2 - see https://stackoverflow.com/questions/57534295/)
|
# set workdir (might solve issue #2 - see https://stackoverflow.com/questions/57534295/)
|
||||||
WORKDIR /overleaf/services/web
|
WORKDIR /overleaf/services/web
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
RUN npm cache clean --force && \
|
|
||||||
npm install -g npm@10.5.0
|
|
||||||
|
|
||||||
|
|
||||||
# install latest npm
|
# install latest npm
|
||||||
RUN \
|
RUN npm install -g npm && \
|
||||||
## clean cache (might solve issue #2)
|
## clean cache (might solve issue #2)
|
||||||
##npm cache clean --force && \
|
# npm cache clean --force && \
|
||||||
##npm install -g npm@latest && \
|
|
||||||
npm install ldap-escape ldapts-search ldapts@3.2.4 && \
|
npm install ldap-escape ldapts-search ldapts@3.2.4 && \
|
||||||
# npm install bcrypt@5.0.0 && \
|
# npm install bcrypt@5.0.0 && \
|
||||||
## This variant of updateing texlive does not work
|
|
||||||
# bash -c tlmgr install scheme-full && \
|
|
||||||
## try this one:
|
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get -y install python-pygments && \
|
apt-get -y install libxml-libxslt-perl cpanminus libbtparse2 python3-pygments
|
||||||
apt-get -y install texlive texlive-lang-german texlive-latex-extra texlive-full texlive-science && \
|
# now install latest texlive2023 from tlmgr
|
||||||
|
RUN wget -O /tmp/update-tlmgr-latest.sh http://mirror.ctan.org/systems/texlive/tlnet/update-tlmgr-latest.sh
|
||||||
|
RUN bash /tmp/update-tlmgr-latest.sh
|
||||||
|
RUN tlmgr update --self --all && \
|
||||||
|
tlmgr install scheme-full --verify-repo=none && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# latex-bin must be on path to be found in compilation process
|
||||||
|
# needed for biber epstopdf and others
|
||||||
|
ENV PATH="/usr/local/texlive/2023/bin/x86_64-linux:${PATH};"
|
||||||
|
|
||||||
# overwrite some files
|
# overwrite some files
|
||||||
COPY sharelatex/AuthenticationManager.js /overleaf/services/web/app/src/Features/Authentication/
|
COPY sharelatex/AuthenticationManager.js /overleaf/services/web/app/src/Features/Authentication/
|
||||||
COPY sharelatex/AuthenticationController.js /overleaf/services/web/app/src/Features/Authentication/
|
COPY sharelatex/AuthenticationController.js /overleaf/services/web/app/src/Features/Authentication/
|
||||||
COPY sharelatex/ContactController.js /overleaf/services/web/app/src/Features/Contacts/
|
COPY sharelatex/ContactController.js /overleaf/services/web/app/src/Features/Contacts/
|
||||||
|
COPY sharelatex/ProjectEditorHandler.js /overleaf/services/web/app/src/Features/Project/
|
||||||
|
COPY sharelatex_diff/TrackChangesController.js /overleaf/services/web/app/src/Features/TrackChanges/
|
||||||
COPY sharelatex/router.js /overleaf/services/web/app/src/router.js
|
COPY sharelatex/router.js /overleaf/services/web/app/src/router.js
|
||||||
|
|
||||||
|
## Copy some new files from the Docker git repo (because they are not in the current Docker release - to enable Track-Changes
|
||||||
|
|
||||||
|
RUN wget -O /overleaf/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js https://raw.githubusercontent.com/overleaf/overleaf/main/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js
|
||||||
|
RUN wget -O /overleaf/services/web/app/src/Features/Chat/ChatApiHandler.js https://raw.githubusercontent.com/overleaf/overleaf/main/services/web/app/src/Features/Chat/ChatApiHandler.js
|
||||||
|
RUN wget -O /overleaf/services/web/app/src/Features/Chat/ChatController.js https://raw.githubusercontent.com/overleaf/overleaf/main/services/web/app/src/Features/Chat/ChatController.js
|
||||||
|
RUN wget -O /overleaf/services/web/app/src/Features/Chat/ChatManager.js https://raw.githubusercontent.com/overleaf/overleaf/main/services/web/app/src/Features/Chat/ChatManager.js
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Too much changes to do inline (>10 Lines).
|
# Too much changes to do inline (>10 Lines).
|
||||||
COPY sharelatex/settings.pug /overleaf/services/web/app/views/user/
|
#COPY sharelatex/settings.pug /overleaf/services/web/app/views/user/
|
||||||
COPY sharelatex/login.pug /overleaf/services/web/app/views/user/
|
COPY sharelatex/login.pug /overleaf/services/web/app/views/user/
|
||||||
COPY sharelatex/navbar.pug /overleaf/services/web/app/views/layout/
|
#COPY sharelatex/navbar.pug /overleaf/services/web/app/views/layout/
|
||||||
COPY sharelatex/navbar-marketing.pug /overleaf/services/web/app/views/layout/
|
COPY sharelatex/navbar-marketing.pug /overleaf/services/web/app/views/layout/
|
||||||
|
|
||||||
|
# Copy TrackChanges Module
|
||||||
|
#COPY sharelatex-modules/track-changes /overleaf/services/web/modules/track-changes
|
||||||
|
|
||||||
|
|
||||||
# Non LDAP User Registration for Admins
|
# Non LDAP User Registration for Admins
|
||||||
COPY sharelatex/admin-index.pug /overleaf/services/web/app/views/admin/index.pug
|
COPY sharelatex/admin-index.pug /overleaf/services/web/app/views/admin/index.pug
|
||||||
COPY sharelatex/admin-sysadmin.pug /tmp/admin-sysadmin.pug
|
COPY sharelatex/admin-sysadmin.pug /tmp/admin-sysadmin.pug
|
||||||
@@ -92,6 +106,3 @@ RUN sed -iE "s/email@example.com/${login_text:-user}/g" /overleaf/services/web/a
|
|||||||
# echo "/usr/cron.weekly/nginx-cert.sh 2>&1 > /dev/null" > /etc/rc.local && \
|
# echo "/usr/cron.weekly/nginx-cert.sh 2>&1 > /dev/null" > /etc/rc.local && \
|
||||||
# chmod 0744 /etc/rc.local
|
# chmod 0744 /etc/rc.local
|
||||||
|
|
||||||
|
|
||||||
COPY update_texlive.sh /overleaf/services/web
|
|
||||||
RUN sh update_texlive.sh
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
268a268,364
|
300a301,394
|
||||||
>
|
|
||||||
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
||||||
> oauth2Redirect(req, res, next) {
|
> oauth2Redirect(req, res, next) {
|
||||||
> // random state
|
> // random state
|
||||||
> const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
> const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||||
> const state = new Array(6).fill(0).map(() => characters.charAt(Math.floor(Math.random() * characters.length))).join("")
|
> const state = new Array(6).fill(0).map(() => characters.charAt(Math.floor(Math.random() * characters.length))).join("")
|
||||||
> req.session.oauth2State = state
|
> req.session.oauth2State = state
|
||||||
>
|
>
|
||||||
> const redirectURI = encodeURIComponent(`${process.env.SHARELATEX_SITE_URL}/oauth/callback`)
|
> const redirectURI = encodeURIComponent(`${process.env.OVERLEAF_SITE_URL}/oauth/callback`)
|
||||||
> const authURL = (
|
> const authURL = (
|
||||||
> process.env.OAUTH2_AUTHORIZATION_URL
|
> process.env.OAUTH2_AUTHORIZATION_URL
|
||||||
> + `?response_type=code`
|
> + `?response_type=code`
|
||||||
@@ -34,7 +32,7 @@
|
|||||||
> client_id: process.env.OAUTH2_CLIENT_ID,
|
> client_id: process.env.OAUTH2_CLIENT_ID,
|
||||||
> client_secret: process.env.OAUTH2_CLIENT_SECRET,
|
> client_secret: process.env.OAUTH2_CLIENT_SECRET,
|
||||||
> code: req.query.code,
|
> code: req.query.code,
|
||||||
> redirect_uri: `${process.env.SHARELATEX_SITE_URL}/oauth/callback`,
|
> redirect_uri: `${process.env.OVERLEAF_SITE_URL}/oauth/callback`,
|
||||||
> }
|
> }
|
||||||
> const body = contentType === 'application/json'
|
> const body = contentType === 'application/json'
|
||||||
> ? JSON.stringify(bodyParams)
|
> ? JSON.stringify(bodyParams)
|
||||||
@@ -95,4 +93,3 @@
|
|||||||
> console.error("Fails to access by OAuth2: " + String(e))
|
> console.error("Fails to access by OAuth2: " + String(e))
|
||||||
> }
|
> }
|
||||||
> },
|
> },
|
||||||
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
19a20,25
|
19a20,22
|
||||||
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
||||||
> const fs = require("fs")
|
> const fs = require("fs")
|
||||||
> const { Client } = require("ldapts")
|
> const { Client } = require("ldapts")
|
||||||
> const ldapEscape = require("ldap-escape")
|
> const ldapEscape = require("ldap-escape")
|
||||||
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
120a124,132
|
||||||
>
|
>
|
||||||
120a127,136
|
|
||||||
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
||||||
> _checkUserPassword2(query, password, callback) {
|
> _checkUserPassword2(query, password, callback) {
|
||||||
> // leave original _checkUserPassword untouched, because it will be called by
|
> // leave original _checkUserPassword untouched, because it will be called by
|
||||||
> // setUserPasswordInV2 (e.g. UserRegistrationHandler.js )
|
> // setUserPasswordInV2 (e.g. UserRegistrationHandler.js )
|
||||||
@@ -14,17 +11,16 @@
|
|||||||
> AuthenticationManager.authUserObj(error, user, query, password, callback)
|
> AuthenticationManager.authUserObj(error, user, query, password, callback)
|
||||||
> })
|
> })
|
||||||
> },
|
> },
|
||||||
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
||||||
>
|
>
|
||||||
126c142,144
|
122c134,138
|
||||||
< AuthenticationManager._checkUserPassword(
|
< AuthenticationManager._checkUserPassword(
|
||||||
---
|
---
|
||||||
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
> if (typeof callback === 'undefined') {
|
||||||
|
> callback = auditLog
|
||||||
|
> auditLog = null
|
||||||
|
> }
|
||||||
> AuthenticationManager._checkUserPassword2(
|
> AuthenticationManager._checkUserPassword2(
|
||||||
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
201a218,494
|
||||||
190a209,488
|
|
||||||
>
|
|
||||||
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
||||||
> /**
|
> /**
|
||||||
> * login with any password
|
> * login with any password
|
||||||
> */
|
> */
|
||||||
@@ -70,9 +66,10 @@
|
|||||||
> isAdmin
|
> isAdmin
|
||||||
> ) {
|
> ) {
|
||||||
> if (!user) {
|
> if (!user) {
|
||||||
|
> //console.log('Creating User:' + JSON.stringify(query))
|
||||||
> //create random pass for local userdb, does not get checked for ldap users during login
|
> //create random pass for local userdb, does not get checked for ldap users during login
|
||||||
> const pass = require("crypto").randomBytes(32).toString("hex")
|
> let pass = require("crypto").randomBytes(32).toString("hex")
|
||||||
> console.log('Creating User', { mail, uid, firstname, lastname, isAdmin, pass })
|
> //console.log('Creating User:' + JSON.stringify(query) + 'Random Pass' + pass)
|
||||||
>
|
>
|
||||||
> const userRegHand = require("../User/UserRegistrationHandler.js")
|
> const userRegHand = require("../User/UserRegistrationHandler.js")
|
||||||
> userRegHand.registerNewUser(
|
> userRegHand.registerNewUser(
|
||||||
@@ -102,7 +99,6 @@
|
|||||||
> }
|
> }
|
||||||
> ) // end register user
|
> ) // end register user
|
||||||
> } else {
|
> } else {
|
||||||
> console.log('User exists', { mail })
|
|
||||||
> AuthenticationManager.login(user, "randomPass", callback)
|
> AuthenticationManager.login(user, "randomPass", callback)
|
||||||
> }
|
> }
|
||||||
> },
|
> },
|
||||||
@@ -302,4 +298,3 @@
|
|||||||
> }
|
> }
|
||||||
> })
|
> })
|
||||||
> },
|
> },
|
||||||
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
11c11
|
||||||
|
< trackChangesAvailable: false,
|
||||||
|
---
|
||||||
|
> trackChangesAvailable: true,
|
||||||
|
60c60
|
||||||
|
< trackChanges: false,
|
||||||
|
---
|
||||||
|
> trackChanges: true,
|
||||||
308
ldap-overleaf-sl/sharelatex_diff/TrackChangesController.js
Normal file
308
ldap-overleaf-sl/sharelatex_diff/TrackChangesController.js
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
const ChatApiHandler = require('../Chat/ChatApiHandler')
|
||||||
|
const ChatManager = require('../Chat/ChatManager')
|
||||||
|
const EditorRealTimeController = require('../Editor/EditorRealTimeController')
|
||||||
|
const SessionManager = require('../Authentication/SessionManager')
|
||||||
|
const UserInfoManager = require('../User/UserInfoManager')
|
||||||
|
const DocstoreManager = require('../Docstore/DocstoreManager')
|
||||||
|
const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
|
||||||
|
const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
|
||||||
|
const { Project } = require('../../models/Project')
|
||||||
|
const pLimit = require('p-limit')
|
||||||
|
|
||||||
|
async function _updateTCState (projectId, state, callback) {
|
||||||
|
await Project.updateOne({_id: projectId}, {track_changes: state}).exec()
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
function _transformId(doc) {
|
||||||
|
if (doc._id) {
|
||||||
|
doc.id = doc._id;
|
||||||
|
delete doc._id;
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TrackChangesController = {
|
||||||
|
trackChanges(req, res, next) {
|
||||||
|
const { project_id } = req.params
|
||||||
|
let state = req.body.on || req.body.on_for
|
||||||
|
if ( req.body.on_for_guests && !req.body.on ) state.__guests__ = true
|
||||||
|
|
||||||
|
return _updateTCState(project_id, state,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
EditorRealTimeController.emitToRoom(
|
||||||
|
project_id,
|
||||||
|
'toggle-track-changes',
|
||||||
|
state
|
||||||
|
)
|
||||||
|
return res.sendStatus(204)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
acceptChanges(req, res, next) {
|
||||||
|
const { project_id, doc_id } = req.params
|
||||||
|
const change_ids = req.body.change_ids
|
||||||
|
return DocumentUpdaterHandler.acceptChanges(
|
||||||
|
project_id,
|
||||||
|
doc_id,
|
||||||
|
change_ids,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
EditorRealTimeController.emitToRoom(
|
||||||
|
project_id,
|
||||||
|
'accept-changes',
|
||||||
|
doc_id,
|
||||||
|
change_ids,
|
||||||
|
)
|
||||||
|
return res.sendStatus(204)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
async getAllRanges(req, res, next) {
|
||||||
|
const { project_id } = req.params
|
||||||
|
// FIXME: ranges are from mongodb, probably already outdated
|
||||||
|
const ranges = await DocstoreManager.promises.getAllRanges(project_id)
|
||||||
|
// frontend expects 'id', not '_id'
|
||||||
|
return res.json(ranges.map(_transformId))
|
||||||
|
},
|
||||||
|
async getChangesUsers(req, res, next) {
|
||||||
|
const { project_id } = req.params
|
||||||
|
const memberIds = await CollaboratorsGetter.promises.getMemberIds(project_id)
|
||||||
|
// FIXME: Does not work properly if the user is no longer a member of the project
|
||||||
|
// memberIds from DocstoreManager.getAllRanges(project_id) is not a remedy
|
||||||
|
// because ranges are not updated in real-time
|
||||||
|
const limit = pLimit(3)
|
||||||
|
const users = await Promise.all(
|
||||||
|
memberIds.map(memberId =>
|
||||||
|
limit(async () => {
|
||||||
|
const user = await UserInfoManager.promises.getPersonalInfo(memberId)
|
||||||
|
return user
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
users.push({_id: null}) // An anonymous user won't cause any harm
|
||||||
|
// frontend expects 'id', not '_id'
|
||||||
|
return res.json(users.map(_transformId))
|
||||||
|
},
|
||||||
|
getThreads(req, res, next) {
|
||||||
|
const { project_id } = req.params
|
||||||
|
return ChatApiHandler.getThreads(
|
||||||
|
project_id,
|
||||||
|
function (err, messages) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
return ChatManager.injectUserInfoIntoThreads(
|
||||||
|
messages,
|
||||||
|
function (err) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
return res.json(messages)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
sendComment(req, res, next) {
|
||||||
|
const { project_id, thread_id } = req.params
|
||||||
|
const { content } = req.body
|
||||||
|
const user_id = SessionManager.getLoggedInUserId(req.session)
|
||||||
|
if (user_id == null) {
|
||||||
|
const err = new Error('no logged-in user')
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
return ChatApiHandler.sendComment(
|
||||||
|
project_id,
|
||||||
|
thread_id,
|
||||||
|
user_id,
|
||||||
|
content,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
return UserInfoManager.getPersonalInfo(
|
||||||
|
user_id,
|
||||||
|
function (err, user) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
message.user = user
|
||||||
|
EditorRealTimeController.emitToRoom(
|
||||||
|
project_id,
|
||||||
|
'new-comment',
|
||||||
|
thread_id, message
|
||||||
|
)
|
||||||
|
return res.sendStatus(204)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
editMessage(req, res, next) {
|
||||||
|
const { project_id, thread_id, message_id } = req.params
|
||||||
|
const { content } = req.body
|
||||||
|
const user_id = SessionManager.getLoggedInUserId(req.session)
|
||||||
|
if (user_id == null) {
|
||||||
|
const err = new Error('no logged-in user')
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
return ChatApiHandler.editMessage(
|
||||||
|
project_id,
|
||||||
|
thread_id,
|
||||||
|
message_id,
|
||||||
|
user_id,
|
||||||
|
content,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
EditorRealTimeController.emitToRoom(
|
||||||
|
project_id,
|
||||||
|
'edit-message',
|
||||||
|
thread_id,
|
||||||
|
message_id,
|
||||||
|
content
|
||||||
|
)
|
||||||
|
return res.sendStatus(204)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
deleteMessage(req, res, next) {
|
||||||
|
const { project_id, thread_id, message_id } = req.params
|
||||||
|
return ChatApiHandler.deleteMessage(
|
||||||
|
project_id,
|
||||||
|
thread_id,
|
||||||
|
message_id,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
EditorRealTimeController.emitToRoom(
|
||||||
|
project_id,
|
||||||
|
'delete-message',
|
||||||
|
thread_id,
|
||||||
|
message_id
|
||||||
|
)
|
||||||
|
return res.sendStatus(204)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
resolveThread(req, res, next) {
|
||||||
|
const { project_id, doc_id, thread_id } = req.params
|
||||||
|
const user_id = SessionManager.getLoggedInUserId(req.session)
|
||||||
|
if (user_id == null) {
|
||||||
|
const err = new Error('no logged-in user')
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
DocumentUpdaterHandler.resolveThread(
|
||||||
|
project_id,
|
||||||
|
doc_id,
|
||||||
|
thread_id,
|
||||||
|
user_id,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return ChatApiHandler.resolveThread(
|
||||||
|
project_id,
|
||||||
|
thread_id,
|
||||||
|
user_id,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
return UserInfoManager.getPersonalInfo(
|
||||||
|
user_id,
|
||||||
|
function (err, user) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
EditorRealTimeController.emitToRoom(
|
||||||
|
project_id,
|
||||||
|
'resolve-thread',
|
||||||
|
thread_id,
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
return res.sendStatus(204)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
reopenThread(req, res, next) {
|
||||||
|
const { project_id, doc_id, thread_id } = req.params
|
||||||
|
const user_id = SessionManager.getLoggedInUserId(req.session)
|
||||||
|
if (user_id == null) {
|
||||||
|
const err = new Error('no logged-in user')
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
DocumentUpdaterHandler.reopenThread(
|
||||||
|
project_id,
|
||||||
|
doc_id,
|
||||||
|
thread_id,
|
||||||
|
user_id,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return ChatApiHandler.reopenThread(
|
||||||
|
project_id,
|
||||||
|
thread_id,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
EditorRealTimeController.emitToRoom(
|
||||||
|
project_id,
|
||||||
|
'reopen-thread',
|
||||||
|
thread_id
|
||||||
|
)
|
||||||
|
return res.sendStatus(204)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
deleteThread(req, res, next) {
|
||||||
|
const { project_id, doc_id, thread_id } = req.params
|
||||||
|
const user_id = SessionManager.getLoggedInUserId(req.session)
|
||||||
|
if (user_id == null) {
|
||||||
|
const err = new Error('no logged-in user')
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
return DocumentUpdaterHandler.deleteThread(
|
||||||
|
project_id,
|
||||||
|
doc_id,
|
||||||
|
thread_id,
|
||||||
|
user_id,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
ChatApiHandler.deleteThread(
|
||||||
|
project_id,
|
||||||
|
thread_id,
|
||||||
|
function (err, message) {
|
||||||
|
if (err != null) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
EditorRealTimeController.emitToRoom(
|
||||||
|
project_id,
|
||||||
|
'delete-thread',
|
||||||
|
thread_id
|
||||||
|
)
|
||||||
|
return res.sendStatus(204)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
module.exports = TrackChangesController
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
4,6c4,5
|
|
||||||
< if (typeof(suppressNavbarRight) == "undefined")
|
|
||||||
< button.navbar-toggle(ng-init="navCollapsed = true", ng-click="navCollapsed = !navCollapsed", ng-class="{active: !navCollapsed}", aria-label="Toggle " + translate('navigation'))
|
|
||||||
< i.fa.fa-bars(aria-hidden="true")
|
|
||||||
---
|
|
||||||
> button.navbar-toggle(ng-init="navCollapsed = true", ng-click="navCollapsed = !navCollapsed", ng-class="{active: !navCollapsed}", aria-label="Toggle " + translate('navigation'))
|
|
||||||
> i.fa.fa-bars(aria-hidden="true")
|
|
||||||
14,106c13,74
|
|
||||||
< - var canDisplayAdminMenu = hasAdminAccess()
|
|
||||||
< - var canDisplayAdminRedirect = canRedirectToAdminDomain()
|
|
||||||
< - var canDisplaySplitTestMenu = hasFeature('saas') && (canDisplayAdminMenu || (getSessionUser() && getSessionUser().staffAccess && (getSessionUser().staffAccess.splitTestMetrics || getSessionUser().staffAccess.splitTestManagement)))
|
|
||||||
< - var canDisplaySurveyMenu = hasFeature('saas') && canDisplayAdminMenu
|
|
||||||
< - var featuresPageVariant = splitTestVariants && splitTestVariants['features-page']
|
|
||||||
<
|
|
||||||
< if (typeof(suppressNavbarRight) == "undefined")
|
|
||||||
< .navbar-collapse.collapse(collapse="navCollapsed")
|
|
||||||
< ul.nav.navbar-nav.navbar-right
|
|
||||||
< if (canDisplayAdminMenu || canDisplayAdminRedirect || canDisplaySplitTestMenu)
|
|
||||||
< li.dropdown(class="subdued", dropdown)
|
|
||||||
< a.dropdown-toggle(href, dropdown-toggle)
|
|
||||||
< | Admin
|
|
||||||
< b.caret
|
|
||||||
< ul.dropdown-menu
|
|
||||||
< if canDisplayAdminMenu
|
|
||||||
< li
|
|
||||||
< a(href="/admin") Manage Site
|
|
||||||
< li
|
|
||||||
< a(href="/admin/user") Manage Users
|
|
||||||
< li
|
|
||||||
< a(href="/admin/project") Project URL Lookup
|
|
||||||
< li
|
|
||||||
< a(href="/admin/saml/logs") SAML logs
|
|
||||||
< if canDisplayAdminRedirect
|
|
||||||
< li
|
|
||||||
< a(href=settings.adminUrl) Switch to Admin
|
|
||||||
< if canDisplaySplitTestMenu
|
|
||||||
< li
|
|
||||||
< a(href="/admin/split-test") Manage Feature Flags
|
|
||||||
< if canDisplaySurveyMenu
|
|
||||||
< li
|
|
||||||
< a(href="/admin/survey") Manage Surveys
|
|
||||||
<
|
|
||||||
< // loop over header_extras
|
|
||||||
< each item in nav.header_extras
|
|
||||||
< -
|
|
||||||
< if ((item.only_when_logged_in && getSessionUser())
|
|
||||||
< || (item.only_when_logged_out && (!getSessionUser()))
|
|
||||||
< || (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages)
|
|
||||||
< || (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks))
|
|
||||||
< ){
|
|
||||||
< var showNavItem = true
|
|
||||||
< } else {
|
|
||||||
< var showNavItem = false
|
|
||||||
< }
|
|
||||||
<
|
|
||||||
< if showNavItem
|
|
||||||
< if item.dropdown
|
|
||||||
< li.dropdown(class=item.class, dropdown)
|
|
||||||
< a.dropdown-toggle(href, dropdown-toggle)
|
|
||||||
< | !{translate(item.text)}
|
|
||||||
< b.caret
|
|
||||||
< ul.dropdown-menu
|
|
||||||
< each child in item.dropdown
|
|
||||||
< if child.divider
|
|
||||||
< li.divider
|
|
||||||
< else if child.isContactUs
|
|
||||||
< li
|
|
||||||
< a(ng-controller="ContactModal" ng-click="contactUsModal()" href)
|
|
||||||
< span(event-tracking="menu-clicked-contact" event-tracking-mb="true" event-tracking-trigger="click")
|
|
||||||
< | #{translate("contact_us")}
|
|
||||||
< else
|
|
||||||
< li
|
|
||||||
< if child.url
|
|
||||||
< if !child.splitTest || child.splitTest && child.splitTest === 'features-page' && child.splitTestVariant === featuresPageVariant
|
|
||||||
< a(
|
|
||||||
< href=child.url,
|
|
||||||
< class=child.class,
|
|
||||||
< event-tracking=child.event
|
|
||||||
< event-tracking-mb="true"
|
|
||||||
< event-tracking-trigger="click"
|
|
||||||
< event-segmentation=child.eventSegmentation
|
|
||||||
< ) !{translate(child.text)}
|
|
||||||
< else
|
|
||||||
< | !{translate(child.text)}
|
|
||||||
< else
|
|
||||||
< li(class=item.class)
|
|
||||||
< if item.url
|
|
||||||
< a(
|
|
||||||
< href=item.url,
|
|
||||||
< class=item.class,
|
|
||||||
< event-tracking=item.event
|
|
||||||
< event-tracking-mb="true"
|
|
||||||
< event-tracking-trigger="click"
|
|
||||||
< ) !{translate(item.text)}
|
|
||||||
< else
|
|
||||||
< | !{translate(item.text)}
|
|
||||||
<
|
|
||||||
< // logged out
|
|
||||||
< if !getSessionUser()
|
|
||||||
< // register link
|
|
||||||
< if hasFeature('registration-page')
|
|
||||||
---
|
|
||||||
> .navbar-collapse.collapse(collapse="navCollapsed")
|
|
||||||
>
|
|
||||||
> ul.nav.navbar-nav.navbar-right
|
|
||||||
> if (getSessionUser() && getSessionUser().isAdmin)
|
|
||||||
> li
|
|
||||||
> a(href="/admin") Admin
|
|
||||||
>
|
|
||||||
>
|
|
||||||
> // loop over header_extras
|
|
||||||
> each item in nav.header_extras
|
|
||||||
> -
|
|
||||||
> if ((item.only_when_logged_in && getSessionUser())
|
|
||||||
> || (item.only_when_logged_out && (!getSessionUser()))
|
|
||||||
> || (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages)
|
|
||||||
> || (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks))
|
|
||||||
> ){
|
|
||||||
> var showNavItem = true
|
|
||||||
> } else {
|
|
||||||
> var showNavItem = false
|
|
||||||
> }
|
|
||||||
>
|
|
||||||
> if showNavItem
|
|
||||||
> if item.dropdown
|
|
||||||
> li.dropdown(class=item.class, dropdown)
|
|
||||||
> a.dropdown-toggle(href, dropdown-toggle)
|
|
||||||
> | !{translate(item.text)}
|
|
||||||
> b.caret
|
|
||||||
> ul.dropdown-menu
|
|
||||||
> each child in item.dropdown
|
|
||||||
> if child.divider
|
|
||||||
> li.divider
|
|
||||||
> else
|
|
||||||
> li
|
|
||||||
> if child.url
|
|
||||||
> a(href=child.url, class=child.class) !{translate(child.text)}
|
|
||||||
> else
|
|
||||||
> | !{translate(child.text)}
|
|
||||||
> else
|
|
||||||
> li(class=item.class)
|
|
||||||
> if item.url
|
|
||||||
> a(href=item.url, class=item.class) !{translate(item.text)}
|
|
||||||
> else
|
|
||||||
> | !{translate(item.text)}
|
|
||||||
>
|
|
||||||
> // logged out
|
|
||||||
> if !getSessionUser()
|
|
||||||
> // login link
|
|
||||||
> li
|
|
||||||
> a(href="/login") #{translate('log_in')}
|
|
||||||
>
|
|
||||||
> // projects link and account menu
|
|
||||||
> if getSessionUser()
|
|
||||||
> li
|
|
||||||
> a(href="/project") #{translate('Projects')}
|
|
||||||
> li.dropdown(dropdown)
|
|
||||||
> a.dropdown-toggle(href, dropdown-toggle)
|
|
||||||
> | #{translate('Account')}
|
|
||||||
> b.caret
|
|
||||||
> ul.dropdown-menu
|
|
||||||
> //li
|
|
||||||
> // div.subdued(ng-non-bindable) #{getUserEmail()}
|
|
||||||
> //li.divider.hidden-xs.hidden-sm
|
|
||||||
108,139c76,77
|
|
||||||
< a(
|
|
||||||
< href="/register"
|
|
||||||
< event-tracking="menu-clicked-register"
|
|
||||||
< event-tracking-action="clicked"
|
|
||||||
< event-tracking-trigger="click"
|
|
||||||
< event-tracking-mb="true"
|
|
||||||
< event-segmentation={ page: currentUrl }
|
|
||||||
< ) #{translate('register')}
|
|
||||||
<
|
|
||||||
< // login link
|
|
||||||
< li
|
|
||||||
< a(
|
|
||||||
< href="/login"
|
|
||||||
< event-tracking="menu-clicked-login"
|
|
||||||
< event-tracking-action="clicked"
|
|
||||||
< event-tracking-trigger="click"
|
|
||||||
< event-tracking-mb="true"
|
|
||||||
< event-segmentation={ page: currentUrl }
|
|
||||||
< ) #{translate('log_in')}
|
|
||||||
<
|
|
||||||
< // projects link and account menu
|
|
||||||
< if getSessionUser()
|
|
||||||
< li
|
|
||||||
< a(href="/project") #{translate('Projects')}
|
|
||||||
< li.dropdown(dropdown)
|
|
||||||
< a.dropdown-toggle(href, dropdown-toggle)
|
|
||||||
< | #{translate('Account')}
|
|
||||||
< b.caret
|
|
||||||
< ul.dropdown-menu
|
|
||||||
< li
|
|
||||||
< div.subdued {{ usersEmail }}
|
|
||||||
< li.divider.hidden-xs.hidden-sm
|
|
||||||
---
|
|
||||||
> a(href="/user/settings") #{translate('Account Settings')}
|
|
||||||
> if nav.showSubscriptionLink
|
|
||||||
141,149c79,84
|
|
||||||
< a(href="/user/settings") #{translate('Account Settings')}
|
|
||||||
< if nav.showSubscriptionLink
|
|
||||||
< li
|
|
||||||
< a(href="/user/subscription") #{translate('subscription')}
|
|
||||||
< li.divider.hidden-xs.hidden-sm
|
|
||||||
< li
|
|
||||||
< form(method="POST" action="/logout")
|
|
||||||
< input(name='_csrf', type='hidden', value=csrfToken)
|
|
||||||
< button.btn-link.text-left.dropdown-menu-button #{translate('log_out')}
|
|
||||||
---
|
|
||||||
> a(href="/user/subscription") #{translate('subscription')}
|
|
||||||
> li.divider.hidden-xs.hidden-sm
|
|
||||||
> li
|
|
||||||
> form(method="POST" action="/logout")
|
|
||||||
> input(name='_csrf', type='hidden', value=csrfToken)
|
|
||||||
> button.btn-link.text-left.dropdown-menu-button #{translate('log_out')}
|
|
||||||
@@ -1,10 +1,91 @@
|
|||||||
259a260,268
|
39a40
|
||||||
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
> const TrackChangesController = require('./Features/TrackChanges/TrackChangesController')
|
||||||
|
218c219,226
|
||||||
|
< webRouter.get('/login', UserPagesController.loginPage)
|
||||||
|
---
|
||||||
|
> // If no LDAP Server is in use and no local db login then we can redirect the login
|
||||||
|
> // and just use OAUTH
|
||||||
|
> if ( (typeof process.env.LDAP_SERVER === typeof undefined) && (process.env.ALLOW_EMAIL_LOGIN === 'false') && (process.env.OAUTH2_ENABLED === 'true') ) {
|
||||||
|
> webRouter.get('/login', function (req, res, next) { res.redirect('/oauth/redirect') })
|
||||||
|
> } else {
|
||||||
|
> webRouter.get('/login', UserPagesController.loginPage)
|
||||||
|
> }
|
||||||
|
>
|
||||||
|
259a268,274
|
||||||
> if (process.env.OAUTH2_ENABLED === 'true') {
|
> if (process.env.OAUTH2_ENABLED === 'true') {
|
||||||
> webRouter.get('/oauth/redirect', AuthenticationController.oauth2Redirect)
|
> webRouter.get('/oauth/redirect', AuthenticationController.oauth2Redirect)
|
||||||
> webRouter.get('/oauth/callback', AuthenticationController.oauth2Callback)
|
> webRouter.get('/oauth/callback', AuthenticationController.oauth2Callback)
|
||||||
> AuthenticationController.addEndpointToLoginWhitelist('/oauth/redirect')
|
> AuthenticationController.addEndpointToLoginWhitelist('/oauth/redirect')
|
||||||
> AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
|
> AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
|
||||||
> }
|
> }
|
||||||
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
||||||
>
|
>
|
||||||
|
1352a1368,1436
|
||||||
|
> )
|
||||||
|
>
|
||||||
|
> webRouter.post('/project/:project_id/track_changes',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.trackChanges
|
||||||
|
> )
|
||||||
|
> webRouter.post('/project/:project_id/doc/:doc_id/changes/accept',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.acceptChanges
|
||||||
|
> )
|
||||||
|
> webRouter.get('/project/:project_id/ranges',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.getAllRanges
|
||||||
|
> )
|
||||||
|
> webRouter.get('/project/:project_id/changes/users',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.getChangesUsers
|
||||||
|
> )
|
||||||
|
> webRouter.get(
|
||||||
|
> '/project/:project_id/threads',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.getThreads
|
||||||
|
> )
|
||||||
|
> webRouter.post(
|
||||||
|
> '/project/:project_id/thread/:thread_id/messages',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.sendComment
|
||||||
|
> )
|
||||||
|
> webRouter.post(
|
||||||
|
> '/project/:project_id/thread/:thread_id/messages/:message_id/edit',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.editMessage
|
||||||
|
> )
|
||||||
|
> webRouter.delete(
|
||||||
|
> '/project/:project_id/thread/:thread_id/messages/:message_id',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.deleteMessage
|
||||||
|
> )
|
||||||
|
> webRouter.post(
|
||||||
|
> '/project/:project_id/doc/:doc_id/thread/:thread_id/resolve',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.resolveThread
|
||||||
|
> )
|
||||||
|
> webRouter.post(
|
||||||
|
> '/project/:project_id/thread/:thread_id/resolve',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.resolveThread
|
||||||
|
> )
|
||||||
|
> webRouter.post(
|
||||||
|
> '/project/:project_id/doc/:doc_id/thread/:thread_id/reopen',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.reopenThread
|
||||||
|
> )
|
||||||
|
> webRouter.delete(
|
||||||
|
> '/project/:project_id/doc/:doc_id/thread/:thread_id',
|
||||||
|
> AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||||
|
> AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
> TrackChangesController.deleteThread
|
||||||
|
|||||||
@@ -1,212 +0,0 @@
|
|||||||
1c1
|
|
||||||
< extends ../layout-marketing
|
|
||||||
---
|
|
||||||
> extends ../layout
|
|
||||||
3,4c3,14
|
|
||||||
< block entrypointVar
|
|
||||||
< - entrypoint = 'pages/user/settings'
|
|
||||||
---
|
|
||||||
> block content
|
|
||||||
> .content.content-alt
|
|
||||||
> .container
|
|
||||||
> .row
|
|
||||||
> .col-md-12.col-lg-10.col-lg-offset-1
|
|
||||||
> if ssoError
|
|
||||||
> .alert.alert-danger
|
|
||||||
> | #{translate('sso_link_error')}: #{translate(ssoError)}
|
|
||||||
> .card
|
|
||||||
> .page-header
|
|
||||||
> h1 #{translate("account_settings")}
|
|
||||||
> .account-settings(ng-controller="AccountSettingsController", ng-cloak)
|
|
||||||
6,29c16,17
|
|
||||||
< block append meta
|
|
||||||
< meta(name="ol-hasPassword" data-type="boolean" content=hasPassword)
|
|
||||||
< meta(name="ol-shouldAllowEditingDetails" data-type="boolean" content=shouldAllowEditingDetails)
|
|
||||||
< meta(name="ol-oauthProviders", data-type="json", content=oauthProviders)
|
|
||||||
< meta(name="ol-institutionLinked", data-type="json", content=institutionLinked)
|
|
||||||
< meta(name="ol-samlError", data-type="json", content=samlError)
|
|
||||||
< meta(name="ol-institutionEmailNonCanonical", content=institutionEmailNonCanonical)
|
|
||||||
<
|
|
||||||
< meta(name="ol-reconfirmedViaSAML", content=reconfirmedViaSAML)
|
|
||||||
< meta(name="ol-reconfirmationRemoveEmail", content=reconfirmationRemoveEmail)
|
|
||||||
< meta(name="ol-samlBeta", content=samlBeta)
|
|
||||||
< meta(name="ol-ssoErrorMessage", content=ssoErrorMessage)
|
|
||||||
< meta(name="ol-thirdPartyIds", data-type="json", content=thirdPartyIds || {})
|
|
||||||
< meta(name="ol-passwordStrengthOptions", data-type="json", content=settings.passwordStrengthOptions || {})
|
|
||||||
< meta(name="ol-isExternalAuthenticationSystemUsed" data-type="boolean" content=externalAuthenticationSystemUsed())
|
|
||||||
< meta(name="ol-user" data-type="json" content=user)
|
|
||||||
< meta(name="ol-dropbox" data-type="json" content=dropbox)
|
|
||||||
< meta(name="ol-github" data-type="json" content=github)
|
|
||||||
< meta(name="ol-projectSyncSuccessMessage", content=projectSyncSuccessMessage)
|
|
||||||
< meta(name="ol-showPersonalAccessToken", data-type="boolean" content=showPersonalAccessToken)
|
|
||||||
< meta(name="ol-optionalPersonalAccessToken", data-type="boolean" content=optionalPersonalAccessToken)
|
|
||||||
< meta(name="ol-personalAccessTokens", data-type="json" content=personalAccessTokens)
|
|
||||||
< meta(name="ol-emailAddressLimit", data-type="json", content=emailAddressLimit)
|
|
||||||
< meta(name="ol-currentManagedUserAdminEmail" data-type="string" content=currentManagedUserAdminEmail)
|
|
||||||
---
|
|
||||||
>
|
|
||||||
>
|
|
||||||
31,32c19,178
|
|
||||||
< block content
|
|
||||||
< main.content.content-alt#settings-page-root
|
|
||||||
---
|
|
||||||
> .row
|
|
||||||
> .col-md-5
|
|
||||||
> h3 #{translate("update_account_info")}
|
|
||||||
> form(async-form="settings", name="settingsForm", method="POST", action="/user/settings", novalidate)
|
|
||||||
> input(type="hidden", name="_csrf", value=csrfToken)
|
|
||||||
> if !hasFeature('affiliations')
|
|
||||||
> // show the email, non-editable
|
|
||||||
> .form-group
|
|
||||||
> label.control-label #{translate("email")}
|
|
||||||
> div.form-control(
|
|
||||||
> readonly="true",
|
|
||||||
> ng-non-bindable
|
|
||||||
> ) #{user.email}
|
|
||||||
>
|
|
||||||
> if shouldAllowEditingDetails
|
|
||||||
> .form-group
|
|
||||||
> label(for='firstName').control-label #{translate("first_name")}
|
|
||||||
> input.form-control(
|
|
||||||
> id="firstName"
|
|
||||||
> type='text',
|
|
||||||
> name='first_name',
|
|
||||||
> value=user.first_name
|
|
||||||
> ng-non-bindable
|
|
||||||
> )
|
|
||||||
> .form-group
|
|
||||||
> label(for='lastName').control-label #{translate("last_name")}
|
|
||||||
> input.form-control(
|
|
||||||
> id="lastName"
|
|
||||||
> type='text',
|
|
||||||
> name='last_name',
|
|
||||||
> value=user.last_name
|
|
||||||
> ng-non-bindable
|
|
||||||
> )
|
|
||||||
> .form-group
|
|
||||||
> form-messages(aria-live="polite" for="settingsForm")
|
|
||||||
> .alert.alert-success(ng-show="settingsForm.response.success")
|
|
||||||
> | #{translate("thanks_settings_updated")}
|
|
||||||
> .actions
|
|
||||||
> button.btn.btn-primary(
|
|
||||||
> type='submit',
|
|
||||||
> ng-disabled="settingsForm.$invalid"
|
|
||||||
> ) #{translate("update")}
|
|
||||||
> else
|
|
||||||
> .form-group
|
|
||||||
> label.control-label #{translate("first_name")}
|
|
||||||
> div.form-control(
|
|
||||||
> readonly="true",
|
|
||||||
> ng-non-bindable
|
|
||||||
> ) #{user.first_name}
|
|
||||||
> .form-group
|
|
||||||
> label.control-label #{translate("last_name")}
|
|
||||||
> div.form-control(
|
|
||||||
> readonly="true",
|
|
||||||
> ng-non-bindable
|
|
||||||
> ) #{user.last_name}
|
|
||||||
>
|
|
||||||
> .col-md-5.col-md-offset-1
|
|
||||||
> h3
|
|
||||||
> | Set Password for Email login
|
|
||||||
> p
|
|
||||||
> | Note: you can not change the LDAP password from here. You can set/reset a password for
|
|
||||||
> | your email login:
|
|
||||||
> | #[a(href="/user/password/reset", target='_blank') Reset.]
|
|
||||||
>
|
|
||||||
> | !{moduleIncludes("userSettings", locals)}
|
|
||||||
> hr
|
|
||||||
>
|
|
||||||
> h3
|
|
||||||
> | Contact
|
|
||||||
> div
|
|
||||||
> | If you need any help, please contact your sysadmins.
|
|
||||||
>
|
|
||||||
> p #{translate("need_to_leave")}
|
|
||||||
> a(href, ng-click="deleteAccount()") #{translate("delete_your_account")}
|
|
||||||
>
|
|
||||||
>
|
|
||||||
>
|
|
||||||
> script(type='text/ng-template', id='deleteAccountModalTemplate')
|
|
||||||
> .modal-header
|
|
||||||
> h3 #{translate("delete_account")}
|
|
||||||
> div.modal-body#delete-account-modal
|
|
||||||
> p !{translate("delete_account_warning_message_3")}
|
|
||||||
> if settings.createV1AccountOnLogin && settings.overleaf
|
|
||||||
> p
|
|
||||||
> strong
|
|
||||||
> | Your Overleaf v2 projects will be deleted if you delete your account.
|
|
||||||
> | If you want to remove any remaining Overleaf v1 projects in your account,
|
|
||||||
> | please first make sure they are imported to Overleaf v2.
|
|
||||||
>
|
|
||||||
> if settings.overleaf && !hasPassword
|
|
||||||
> p
|
|
||||||
> b
|
|
||||||
> | #[a(href="/user/password/reset", target='_blank') #{translate("delete_acct_no_existing_pw")}].
|
|
||||||
> else
|
|
||||||
> form(novalidate, name="deleteAccountForm")
|
|
||||||
> label #{translate('email')}
|
|
||||||
> input.form-control(
|
|
||||||
> type="text",
|
|
||||||
> autocomplete="off",
|
|
||||||
> placeholder="",
|
|
||||||
> ng-model="state.deleteText",
|
|
||||||
> focus-on="open",
|
|
||||||
> ng-keyup="checkValidation()"
|
|
||||||
> )
|
|
||||||
>
|
|
||||||
> label #{translate('password')}
|
|
||||||
> input.form-control(
|
|
||||||
> type="password",
|
|
||||||
> autocomplete="off",
|
|
||||||
> placeholder="",
|
|
||||||
> ng-model="state.password",
|
|
||||||
> ng-keyup="checkValidation()"
|
|
||||||
> )
|
|
||||||
>
|
|
||||||
> div.confirmation-checkbox-wrapper
|
|
||||||
> input(
|
|
||||||
> type="checkbox"
|
|
||||||
> ng-model="state.confirmV1Purge"
|
|
||||||
> ng-change="checkValidation()"
|
|
||||||
> ).pull-left
|
|
||||||
> label(style="display: inline") I have left, purged or imported my projects on Overleaf v1 (if any)
|
|
||||||
>
|
|
||||||
> div.confirmation-checkbox-wrapper
|
|
||||||
> input(
|
|
||||||
> type="checkbox"
|
|
||||||
> ng-model="state.confirmSharelatexDelete"
|
|
||||||
> ng-change="checkValidation()"
|
|
||||||
> ).pull-left
|
|
||||||
> label(style="display: inline") I understand this will delete all projects in my Overleaf v2 account (and ShareLaTeX account, if any) with email address #[em {{ userDefaultEmail }}]
|
|
||||||
>
|
|
||||||
> div(ng-if="state.error")
|
|
||||||
> div.alert.alert-danger(ng-switch="state.error.code")
|
|
||||||
> span(ng-switch-when="InvalidCredentialsError")
|
|
||||||
> | #{translate('email_or_password_wrong_try_again')}
|
|
||||||
> span(ng-switch-when="SubscriptionAdminDeletionError")
|
|
||||||
> | #{translate('subscription_admins_cannot_be_deleted')}
|
|
||||||
> span(ng-switch-when="UserDeletionError")
|
|
||||||
> | #{translate('user_deletion_error')}
|
|
||||||
> span(ng-switch-default)
|
|
||||||
> | #{translate('generic_something_went_wrong')}
|
|
||||||
> if settings.createV1AccountOnLogin && settings.overleaf
|
|
||||||
> div(ng-if="state.error && state.error.code == 'InvalidCredentialsError'")
|
|
||||||
> div.alert.alert-info
|
|
||||||
> | If you can't remember your password, or if you are using Single-Sign-On with another provider
|
|
||||||
> | to sign in (such as Twitter or Google), please
|
|
||||||
> | #[a(href="/user/password/reset", target='_blank') reset your password],
|
|
||||||
> | and try again.
|
|
||||||
> .modal-footer
|
|
||||||
> button.btn.btn-default(
|
|
||||||
> ng-click="cancel()"
|
|
||||||
> ) #{translate("cancel")}
|
|
||||||
> button.btn.btn-danger(
|
|
||||||
> ng-disabled="!state.isValid || state.inflight"
|
|
||||||
> ng-click="delete()"
|
|
||||||
> )
|
|
||||||
> span(ng-hide="state.inflight") #{translate("delete")}
|
|
||||||
> span(ng-show="state.inflight") #{translate("deleting")}...
|
|
||||||
>
|
|
||||||
> script(type='text/javascript').
|
|
||||||
> window.passwordStrengthOptions = !{StringHelper.stringifyJsonForScript(settings.passwordStrengthOptions || {})}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
cd /usr/local/texlive/
|
|
||||||
if [ -d 2023 ]
|
|
||||||
then
|
|
||||||
cp -a 2023 2024
|
|
||||||
rm -f 2024/tlpkg/backups/*
|
|
||||||
cd 2024
|
|
||||||
wget https://mirror.ctan.org/systems/texlive/tlnet/update-tlmgr-latest.sh
|
|
||||||
sh update-tlmgr-latest.sh --accept
|
|
||||||
fi
|
|
||||||
tlmgr update --self --all
|
|
||||||
tlmgr install scheme-full
|
|
||||||
luaotfload-tool -fu
|
|
||||||
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
CONTAINER_FILE_PATHS=(
|
|
||||||
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationManager.js"
|
|
||||||
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationController.js"
|
|
||||||
"/overleaf/services/web/app/src/Features/Contacts/ContactController.js"
|
|
||||||
"/overleaf/services/web/app/src/router.js"
|
|
||||||
"/overleaf/services/web/app/views/user/settings.pug"
|
|
||||||
"/overleaf/services/web/app/views/user/login.pug"
|
|
||||||
"/overleaf/services/web/app/views/layout/navbar.pug"
|
|
||||||
"/overleaf/services/web/app/views/layout/navbar-marketing.pug"
|
|
||||||
"/overleaf/services/web/app/views/admin/index.pug"
|
|
||||||
"/overleaf/services/web/app/views/admin/index.pug"
|
|
||||||
)
|
|
||||||
|
|
||||||
FILENAMES=(
|
|
||||||
"AuthenticationManager.js"
|
|
||||||
"AuthenticationController.js"
|
|
||||||
"ContactController.js"
|
|
||||||
"router.js"
|
|
||||||
"settings.pug"
|
|
||||||
"login.pug"
|
|
||||||
"navbar.pug"
|
|
||||||
"navbar-marketing.pug"
|
|
||||||
"admin-index.pug"
|
|
||||||
"admin-sysadmin.pug"
|
|
||||||
)
|
|
||||||
|
|
||||||
if [ "${#CONTAINER_FILE_PATHS[@]}" -ne "${#FILENAMES[@]}" ]; then
|
|
||||||
echo "Error: The number of source files and target filenames does not match."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
HOST_TARGET_PATH="ldap-overleaf-sl/sharelatex_ori"
|
|
||||||
|
|
||||||
if [ "$#" -ne 1 ]; then
|
|
||||||
echo "Usage: $0 [version]"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
VERSION=$1
|
|
||||||
fi
|
|
||||||
|
|
||||||
CONTAINER_NAME="tmp_sharelatex_for_extract_files"
|
|
||||||
IMAGE="sharelatex/sharelatex:$VERSION"
|
|
||||||
|
|
||||||
for i in "${!CONTAINER_FILE_PATHS[@]}"; do
|
|
||||||
file_path="${CONTAINER_FILE_PATHS[i]}"
|
|
||||||
new_filename="${FILENAMES[i]}"
|
|
||||||
new_target_path="$HOST_TARGET_PATH/$new_filename"
|
|
||||||
echo " copy: $new_target_path"
|
|
||||||
docker cp $CONTAINER_NAME:$file_path $new_target_path
|
|
||||||
done
|
|
||||||
#
|
|
||||||
#echo "Stopping and removing container..."
|
|
||||||
#docker stop $CONTAINER_NAME
|
|
||||||
#docker rm $CONTAINER_NAME
|
|
||||||
@@ -6,6 +6,7 @@ CONTAINER_FILE_PATHS=(
|
|||||||
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationManager.js"
|
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationManager.js"
|
||||||
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationController.js"
|
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationController.js"
|
||||||
"/overleaf/services/web/app/src/Features/Contacts/ContactController.js"
|
"/overleaf/services/web/app/src/Features/Contacts/ContactController.js"
|
||||||
|
"/overleaf/services/web/app/src/Features/Project/ProjectEditorHandler.js"
|
||||||
"/overleaf/services/web/app/src/router.js"
|
"/overleaf/services/web/app/src/router.js"
|
||||||
"/overleaf/services/web/app/views/user/settings.pug"
|
"/overleaf/services/web/app/views/user/settings.pug"
|
||||||
"/overleaf/services/web/app/views/user/login.pug"
|
"/overleaf/services/web/app/views/user/login.pug"
|
||||||
@@ -19,6 +20,7 @@ FILENAMES=(
|
|||||||
"AuthenticationManager.js"
|
"AuthenticationManager.js"
|
||||||
"AuthenticationController.js"
|
"AuthenticationController.js"
|
||||||
"ContactController.js"
|
"ContactController.js"
|
||||||
|
"ProjectEditorHandler.js"
|
||||||
"router.js"
|
"router.js"
|
||||||
"settings.pug"
|
"settings.pug"
|
||||||
"login.pug"
|
"login.pug"
|
||||||
@@ -60,13 +62,13 @@ docker run -d --name $CONTAINER_NAME $IMAGE
|
|||||||
echo "Waiting for container to start up..."
|
echo "Waiting for container to start up..."
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
#for i in "${!CONTAINER_FILE_PATHS[@]}"; do
|
for i in "${!CONTAINER_FILE_PATHS[@]}"; do
|
||||||
# file_path="${CONTAINER_FILE_PATHS[i]}"
|
file_path="${CONTAINER_FILE_PATHS[i]}"
|
||||||
# new_filename="${FILENAMES[i]}"
|
new_filename="${FILENAMES[i]}"
|
||||||
# new_target_path="$HOST_TARGET_PATH/$new_filename"
|
new_target_path="$HOST_TARGET_PATH/$new_filename"
|
||||||
# docker cp $CONTAINER_NAME:$file_path $new_target_path
|
docker cp $CONTAINER_NAME:$file_path $new_target_path
|
||||||
#done
|
done
|
||||||
#
|
|
||||||
#echo "Stopping and removing container..."
|
echo "Stopping and removing container..."
|
||||||
#docker stop $CONTAINER_NAME
|
docker stop $CONTAINER_NAME
|
||||||
#docker rm $CONTAINER_NAME
|
docker rm $CONTAINER_NAME
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ for filename in $(ls $MODIFIED_DIR); do
|
|||||||
raw_file="$ORI_DIR/$filename"
|
raw_file="$ORI_DIR/$filename"
|
||||||
|
|
||||||
if [ -f "$raw_file" ]; then
|
if [ -f "$raw_file" ]; then
|
||||||
echo "working on $raw_file"
|
|
||||||
diff_output="$DIFFS_DIR/${filename}.diff"
|
diff_output="$DIFFS_DIR/${filename}.diff"
|
||||||
diff "$raw_file" "$MODIFIED_DIR/$filename" > "$diff_output"
|
diff "$raw_file" "$MODIFIED_DIR/$filename" > "$diff_output"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user