diff --git a/build/build.sh b/build/build.sh new file mode 100755 index 0000000000..62194a1be2 --- /dev/null +++ b/build/build.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +echo off + +echo "##########################################################" +echo "######### Start build and deploy #######################" +echo "##########################################################" + +echo "" + +rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Run script directory:" $rd + +dir=$(builtin cd $rd/../; pwd) +echo "Root directory:" $dir + +pushd $dir + +echo "FRONT-END (for start run command 'yarn start' inside the root folder)" +yarn install + +echo "BACK-END" +dotnet build $dir/asc.web.slnf /fl1 /flp1:logfile=asc.web.log;verbosity=normal + +echo "install nodejs projects dependencies..." +pushd $dir/common/ASC.Socket.IO/ +yarn install +pushd $dir/common/ASC.SsoAuth/ +yarn install +pushd $dir/common/ASC.WebDav/ +yarn install +pushd $dir/common/ASC.UrlShortener/ +yarn install +pushd $dir/common/ASC.WebPlugins/ +yarn install \ No newline at end of file diff --git a/build/install/OneClickInstall/install-Docker.sh b/build/install/OneClickInstall/install-Docker.sh index 985f9556f3..c2c903969c 100644 --- a/build/install/OneClickInstall/install-Docker.sh +++ b/build/install/OneClickInstall/install-Docker.sh @@ -48,7 +48,8 @@ DIST=""; REV=""; KERNEL=""; -INSTALL_KAFKA="true"; +INSTALL_REDIS="true"; +INSTALL_RABBITMQ="true"; INSTALL_MYSQL_SERVER="true"; INSTALL_DOCUMENT_SERVER="true"; INSTALL_PRODUCT="true"; @@ -58,6 +59,7 @@ HUB=""; USERNAME=""; PASSWORD=""; +MYSQL_VERSION="" MYSQL_DATABASE="" MYSQL_USER="" MYSQL_PASSWORD="" @@ -65,19 +67,17 @@ MYSQL_ROOT_PASSWORD="" MYSQL_HOST="" DATABASE_MIGRATION="true" -ZOO_PORT="" -ZOO_HOST="" -KAFKA_HOST="" - +ELK_VERSION="" ELK_HOST="" DOCUMENT_SERVER_IMAGE_NAME=onlyoffice/4testing-documentserver-ee:latest DOCUMENT_SERVER_JWT_SECRET="" +DOCUMENT_SERVER_JWT_HEADER="" DOCUMENT_SERVER_HOST="" APP_CORE_BASE_DOMAIN="" APP_CORE_MACHINEKEY="" -APP_DOTNET_ENV="" +ENV_EXTENSION="" HELP_TARGET="install-Docker.sh"; @@ -138,9 +138,16 @@ while [ "$1" != "" ]; do fi ;; - -ikafka | --installkafka ) + -ira | --installrabbitmq ) if [ "$2" != "" ]; then - INSTALL_KAFKA=$2 + INSTALL_RABBITMQ=$2 + shift + fi + ;; + + -ire | --installredis ) + if [ "$2" != "" ]; then + INSTALL_REDIS=$2 shift fi ;; @@ -187,27 +194,6 @@ while [ "$1" != "" ]; do fi ;; - -zp | --zookeeperport ) - if [ "$2" != "" ]; then - ZOO_PORT=$2 - shift - fi - ;; - - -zh | --zookeeperhost ) - if [ "$2" != "" ]; then - ZOO_HOST=$2 - shift - fi - ;; - - -kh | --kafkahost ) - if [ "$2" != "" ]; then - KAFKA_HOST=$2 - shift - fi - ;; - -esh | --elasticsearchhost ) if [ "$2" != "" ]; then ELK_HOST=$2 @@ -252,7 +238,7 @@ while [ "$1" != "" ]; do -env | --environment ) if [ "$2" != "" ]; then - APP_DOTNET_ENV=$2 + ENV_EXTENSION=$2 shift fi ;; @@ -311,16 +297,14 @@ while [ "$1" != "" ]; do echo " -ids, --installdocumentserver install or update document server (true|false)" echo " -di, --documentserverimage document server image name" echo " -imysql, --installmysql install or update mysql (true|false)" - echo " -ikafka, --installkafka install or update kafka (true|false)" + echo " -ira, --installrabbitmq install or update rabbitmq (true|false)" + echo " -ire, --installredis install or update redis (true|false)" echo " -mysqlrp, --mysqlrootpassword mysql server root password" echo " -mysqld, --mysqldatabase $PRODUCT database name" echo " -mysqlu, --mysqluser $PRODUCT database user" echo " -mysqlp, --mysqlpassword $PRODUCT database password" echo " -mysqlh, --mysqlhost mysql server host" echo " -dsh, --docspdcehost $PRODUCT host" - echo " -zp, --zookeeperport zookeeper port (default value 2181)" - echo " -zh, --zookeeperhost zookeeper host" - echo " -kh, --kafkahost kafka host" echo " -esh, --elasticsearchhost elasticsearch host" echo " -env, --environment $PRODUCT environment" echo " -skiphc, --skiphardwarecheck skip hardware check (true|false)" @@ -826,6 +810,7 @@ install_mysql_server () { MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-"$MYSQL_PASSWORD"} fi + reconfigure MYSQL_VERSION ${MYSQL_VERSION} reconfigure MYSQL_DATABASE ${MYSQL_DATABASE} reconfigure MYSQL_USER ${MYSQL_USER} reconfigure MYSQL_PASSWORD ${MYSQL_PASSWORD} @@ -842,35 +827,47 @@ install_document_server () { fi reconfigure DOCUMENT_SERVER_IMAGE_NAME ${DOCUMENT_SERVER_IMAGE_NAME} + reconfigure DOCUMENT_SERVER_JWT_HEADER ${DOCUMENT_SERVER_JWT_HEADER} reconfigure DOCUMENT_SERVER_JWT_SECRET ${DOCUMENT_SERVER_JWT_SECRET} reconfigure DOCUMENT_SERVER_HOST ${DOCUMENT_SERVER_HOST} docker-compose -f $BASE_DIR/ds.yml up -d } -install_kafka () { - reconfigure ZOO_PORT ${ZOO_PORT} - reconfigure ZOO_HOST ${ZOO_HOST} - reconfigure KAFKA_HOST ${KAFKA_HOST} +install_rabbitmq () { + if ! command_exists docker-compose; then + install_docker_compose + fi - docker-compose -f $BASE_DIR/kafka.yml up -d + docker-compose -f $BASE_DIR/rabbitmq.yml up -d +} + +install_redis () { + if ! command_exists docker-compose; then + install_docker_compose + fi + + docker-compose -f $BASE_DIR/redis.yml up -d } install_product () { if ! command_exists docker-compose; then install_docker_compose fi + reconfigure ENV_EXTENSION ${ENV_EXTENSION} reconfigure ELK_HOST ${ELK_HOST} + reconfigure ELK_VERSION ${ELK_VERSION} reconfigure SERVICE_PORT ${SERVICE_PORT} reconfigure APP_CORE_MACHINEKEY ${APP_CORE_MACHINEKEY} reconfigure APP_CORE_BASE_DOMAIN ${APP_CORE_BASE_DOMAIN} reconfigure DOCKER_TAG ${DOCKER_TAG} if [[ -n $EXTERNAL_PORT ]]; then - sed -i "s/8092:8092/${EXTERNAL_PORT}:8092/g" $BASE_DIR/$PRODUCT.yml + sed -i "s/8092:8092/${EXTERNAL_PORT}:8092/g" $BASE_DIR/appserver.yml fi - docker-compose -f $BASE_DIR/$PRODUCT.yml up -d + docker-compose -f $BASE_DIR/migration-runner.yml up -d + docker-compose -f $BASE_DIR/appserver.yml up -d docker-compose -f $BASE_DIR/notify.yml up -d } @@ -903,7 +900,7 @@ check_image_RepoDigest() { } docker_image_update() { - docker-compose -f $BASE_DIR/notify.yml -f $BASE_DIR/$PRODUCT.yml down --volumes + docker-compose -f $BASE_DIR/notify.yml -f $BASE_DIR/appserver.yml down --volumes docker-compose -f $BASE_DIR/build.yml pull } @@ -943,16 +940,14 @@ save_parameters_from_configs() { MYSQL_ROOT_PASSWORD=$(save_parameter MYSQL_ROOT_PASSWORD $MYSQL_ROOT_PASSWORD) MYSQL_HOST=$(save_parameter MYSQL_HOST $MYSQL_HOST) DOCUMENT_SERVER_JWT_SECRET=$(save_parameter DOCUMENT_SERVER_JWT_SECRET $DOCUMENT_SERVER_JWT_SECRET) + DOCUMENT_SERVER_JWT_HEADER=$(save_parameter DOCUMENT_SERVER_JWT_HEADER $DOCUMENT_SERVER_JWT_HEADER) DOCUMENT_SERVER_HOST=$(save_parameter DOCUMENT_SERVER_HOST $DOCUMENT_SERVER_HOST) - ZOO_PORT=$(save_parameter ZOO_PORT $ZOO_PORT) - ZOO_HOST=$(save_parameter ZOO_HOST $ZOO_HOST) - KAFKA_HOST=$(save_parameter KAFKA_HOST $KAFKA_HOST) ELK_HOST=$(save_parameter ELK_HOST $ELK_HOST) SERVICE_PORT=$(save_parameter SERVICE_PORT $SERVICE_PORT) APP_CORE_MACHINEKEY=$(save_parameter APP_CORE_MACHINEKEY $APP_CORE_MACHINEKEY) APP_CORE_BASE_DOMAIN=$(save_parameter APP_CORE_BASE_DOMAIN $APP_CORE_BASE_DOMAIN) if [ ${EXTERNAL_PORT} = "8092" ]; then - EXTERNAL_PORT=$(grep -oP '(?<=- ).*?(?=:8092)' /app/onlyoffice/$PRODUCT.yml) + EXTERNAL_PORT=$(grep -oP '(?<=- ).*?(?=:8092)' /app/onlyoffice/appserver.yml) fi } @@ -1004,8 +999,12 @@ start_installation () { install_document_server fi - if [ "$INSTALL_KAFKA" == "true" ]; then - install_kafka + if [ "$INSTALL_RABBITMQ" == "true" ]; then + install_rabbitmq + fi + + if [ "$INSTALL_REDIS" == "true" ]; then + install_redis fi if [ "$INSTALL_PRODUCT" == "true" ]; then diff --git a/build/install/common/product-configuration.sh b/build/install/common/product-configuration.sh index 36cfa47d46..617251b0d4 100644 --- a/build/install/common/product-configuration.sh +++ b/build/install/common/product-configuration.sh @@ -4,19 +4,19 @@ set -e PRODUCT="docspace" ENVIRONMENT="production" +PACKAGE_SYSNAME="onlyoffice" -APP_DIR="/etc/onlyoffice/${PRODUCT}" +APP_DIR="/etc/${PACKAGE_SYSNAME}/${PRODUCT}" PRODUCT_DIR="/var/www/${PRODUCT}" USER_CONF="$APP_DIR/appsettings.$ENVIRONMENT.json" + NGINX_DIR="/etc/nginx" NGINX_CONF="${NGINX_DIR}/conf.d" -SYSTEMD_DIR="/lib/systemd/system" -MYSQL="" -DB_HOST="" +DB_HOST="localhost" DB_PORT="3306" -DB_NAME="" -DB_USER="" +DB_NAME="${PACKAGE_SYSNAME}" +DB_USER="root" DB_PWD="" APP_HOST="localhost" @@ -31,7 +31,8 @@ ELK_PORT="9200" RABBITMQ_HOST="localhost" RABBITMQ_USER="guest" -RABBITMQ_PWD="guest" +RABBITMQ_PASSWORD="guest" +RABBITMQ_PORT="5672" REDIS_HOST="localhost" REDIS_PORT="6379" @@ -156,12 +157,19 @@ while [ "$1" != "" ]; do fi ;; - -rbp | --rabbitmqpassword ) + -rbpw | --rabbitmqpassword ) if [ "$2" != "" ]; then RABBITMQ_PASSWORD=$2 shift fi ;; + + -rbp | --rabbitmqport ) + if [ "$2" != "" ]; then + RABBITMQ_PORT=$2 + shift + fi + ;; -? | -h | --help ) echo " Usage: bash ${PRODUCT}-configuration.sh [PARAMETER] [[PARAMETER], ...]" @@ -176,8 +184,9 @@ while [ "$1" != "" ]; do echo " -rdh, --redishost redis ip" echo " -rdp, --redisport redis port (default 6379)" echo " -rbh, --rabbitmqhost rabbitmq ip" + echo " -rbp, --rabbitmqport rabbitmq port" echo " -rbu, --rabbitmquser rabbitmq user" - echo " -rbp, --rabbitmqpassword rabbitmq password" + echo " -rbpw, --rabbitmqpassword rabbitmq password" echo " -mysqlh, --mysqlhost mysql server host" echo " -mysqld, --mysqldatabase ${PRODUCT} database name" echo " -mysqlu, --mysqluser ${PRODUCT} database user" @@ -216,7 +225,7 @@ install_json() { #Creating a user-defined .json if [ ! -e $USER_CONF ]; then echo "{}" >> $USER_CONF - chown onlyoffice:onlyoffice $USER_CONF + chown ${PACKAGE_SYSNAME}:${PACKAGE_SYSNAME} $USER_CONF set_core_machinekey $JSON_USERCONF "this.core={'base-domain': \"$APP_HOST\", 'machinekey': \"$CORE_MACHINEKEY\" }" >/dev/null 2>&1 @@ -224,14 +233,16 @@ install_json() { } restart_services() { - echo -n "Restarting services... " - - sed -e "s/ENVIRONMENT=.*/ENVIRONMENT=$ENVIRONMENT/" -e "s/environment=.*/environment=$ENVIRONMENT/" -i $SYSTEMD_DIR/${PRODUCT}*.service >/dev/null 2>&1 + sed -e "s/ENVIRONMENT=.*/ENVIRONMENT=$ENVIRONMENT/" -e "s/environment=.*/environment=$ENVIRONMENT/" -i /lib/systemd/system/${PRODUCT}*.service >/dev/null 2>&1 systemctl daemon-reload - systemctl start ${PRODUCT}-migration-runner || true + echo -n "Updating database... " + systemctl start ${PRODUCT}-migration-runner >/dev/null 2>&1 || true + sleep 15 + echo "OK" - for SVC in api urlshortener socket studio-notify notify \ + echo -n "Restarting services... " + for SVC in login api urlshortener socket studio-notify notify \ people-server files files-services studio backup telegram-service \ webhooks-service clear-events backup-background migration ssoauth doceditor do @@ -412,7 +423,7 @@ setup_nginx(){ # Remove default nginx website rm -f $NGINX_CONF/default.conf >/dev/null 2>&1 || rm -f $NGINX_DIR/sites-enabled/default >/dev/null 2>&1 - sed -i "s/listen.*;/listen $APP_PORT;/" $NGINX_CONF/onlyoffice.conf + sed -i "s/listen.*;/listen $APP_PORT;/" $NGINX_CONF/${PACKAGE_SYSNAME}.conf if [ "$DIST" = "RedHat" ]; then # Remove default nginx settings @@ -427,12 +438,26 @@ setup_nginx(){ if $(getenforce) >/dev/null 2>&1; then case $(getenforce) in enforcing|permissive) - PORTS+=('8081') #Storybook - PORTS+=("$DOCUMENT_SERVER_PORT") - PORTS+=('5001') #ASC.Web.Studio - PORTS+=('5002') #ASC.People - PORTS+=('5008') #ASC.Files/client + PORTS+=('5000') #ASC.Web.Api + PORTS+=('5001') #client + PORTS+=('5003') #ASC.Web.Studio + PORTS+=('5004') #ASC.People + PORTS+=('5005') #ASC.Notify + PORTS+=('5006') #ASC.Studio.Notify + PORTS+=('5007') #ASC.Files/server + PORTS+=('5009') #ASC.Files/service + PORTS+=('5011') #ASC.Login + PORTS+=('5012') #ASC.Data.Backup PORTS+=('5013') #ASC.Files/editor + PORTS+=('5018') #ASC.Migration + PORTS+=('5027') #ASC.ClearEvents + PORTS+=('5028') #ASC.Socket.IO + PORTS+=('5029') #ASC.UrlShortener + PORTS+=('5031') #ASC.Webhooks.Service + PORTS+=('5032') #ASC.Data.Backup.BackgroundTasks + PORTS+=('8081') #Storybook + PORTS+=('9834') #ASC.SsoAuth + PORTS+=('51702') #ASC.TelegramService setsebool -P httpd_can_network_connect on ;; disabled) @@ -448,7 +473,6 @@ setup_nginx(){ fi fi chown nginx:nginx /etc/nginx/* -R - sudo sed -e 's/#//' -i $NGINX_CONF/onlyoffice.conf systemctl enable nginx >/dev/null 2>&1 systemctl restart nginx echo "OK" @@ -456,13 +480,13 @@ setup_nginx(){ setup_docs() { echo -n "Configuring Docs... " - local DS_CONF="/etc/onlyoffice/documentserver/local.json" + local DS_CONF="/etc/${PACKAGE_SYSNAME}/documentserver/local.json" local JSON_DSCONF="$JSON $DS_CONF -e" #Changing the Docs port in nginx conf sed -i "s/0.0.0.0:.*;/0.0.0.0:$DOCUMENT_SERVER_PORT;/" $NGINX_CONF/ds.conf sed -i "s/]:.*;/]:$DOCUMENT_SERVER_PORT default_server;/g" $NGINX_CONF/ds.conf - sed "0,/proxy_pass .*;/{s/proxy_pass .*;/proxy_pass http:\/\/${DOCUMENT_SERVER_HOST}:${DOCUMENT_SERVER_PORT};/}" -i $NGINX_CONF/onlyoffice.conf + sed "0,/proxy_pass .*;/{s/proxy_pass .*;/proxy_pass http:\/\/${DOCUMENT_SERVER_HOST}:${DOCUMENT_SERVER_PORT};/}" -i $NGINX_CONF/${PACKAGE_SYSNAME}.conf #Enable JWT validation for Docs $JSON_DSCONF "this.services.CoAuthoring.token.enable.browser='true'" >/dev/null 2>&1 @@ -475,7 +499,7 @@ setup_docs() { #Save Docs address and JWT in .json $JSON_USERCONF "this.files={'docservice': {\ 'secret': {'value': \"$DOCUMENT_SERVER_JWT_SECRET\",'header': \"$DOCUMENT_SERVER_JWT_HEADER\"}, \ - 'url': {'public': '/ds-vpath/','internal': \"http://${DOCUMENT_SERVER_HOST}:${DOCUMENT_SERVER_PORT}\",'portal': \"http://$APP_HOST:$APP_PORT\"}}}" >/dev/null 2>&1 + 'url': {'public': \"http://${DOCUMENT_SERVER_HOST}:${DOCUMENT_SERVER_PORT}\", 'internal': \"http://${DOCUMENT_SERVER_HOST}:${DOCUMENT_SERVER_PORT}\",'portal': \"http://$APP_HOST:$APP_PORT\"}}}" >/dev/null 2>&1 #Docs Database Migration local DOCUMENT_SERVER_DB_HOST=$(json -f ${DS_CONF} services.CoAuthoring.sql.dbHost) @@ -485,7 +509,7 @@ setup_docs() { local DOCUMENT_SERVER_DB_PASSWORD=$(json -f ${DS_CONF} services.CoAuthoring.sql.dbPass) local DS_CONNECTION_STRING="Host=${DOCUMENT_SERVER_DB_HOST};Port=${DOCUMENT_SERVER_DB_PORT};Database=${DOCUMENT_SERVER_DB_NAME};Username=${DOCUMENT_SERVER_DB_USERNAME};Password=${DOCUMENT_SERVER_DB_PASSWORD};" - sed "s/Host=.*/$DS_CONNECTION_STRING;\"/g" -i $PRODUCT_DIR/services/ASC.Migration.Runner/appsettings.json + sed "s/Host=.*/$DS_CONNECTION_STRING\"/g" -i $PRODUCT_DIR/services/ASC.Migration.Runner/appsettings.json echo "OK" } @@ -576,7 +600,7 @@ setup_redis() { setup_rabbitmq() { echo -n "Configuring rabbitmq... " - $JSON $APP_DIR/rabbitmq.json -e "this.RabbitMQ={'Hostname': \"${RABBITMQ_HOST}\",'UserName': \"${RABBITMQ_USER}\",'Password': \"${RABBITMQ_PASSWORD}\" }" >/dev/null 2>&1 + $JSON $APP_DIR/rabbitmq.json -e "this.RabbitMQ={'Hostname': \"${RABBITMQ_HOST}\",'UserName': \"${RABBITMQ_USER}\",'Password': \"${RABBITMQ_PASSWORD}\",'Port': \"${RABBITMQ_PORT}\",'VirtualHost': \"/\" }" >/dev/null 2>&1 systemctl enable rabbitmq-server >/dev/null 2>&1 systemctl restart rabbitmq-server @@ -607,7 +631,7 @@ if $PACKAGE_MANAGER nginx >/dev/null 2>&1; then setup_nginx fi -if $PACKAGE_MANAGER onlyoffice-documentserver >/dev/null 2>&1 || $PACKAGE_MANAGER onlyoffice-documentserver-de >/dev/null 2>&1 || $PACKAGE_MANAGER onlyoffice-documentserver-ee >/dev/null 2>&1; then +if $PACKAGE_MANAGER ${PACKAGE_SYSNAME}-documentserver >/dev/null 2>&1 || $PACKAGE_MANAGER ${PACKAGE_SYSNAME}-documentserver-de >/dev/null 2>&1 || $PACKAGE_MANAGER ${PACKAGE_SYSNAME}-documentserver-ee >/dev/null 2>&1; then setup_docs fi diff --git a/build/install/common/systemd/build.sh b/build/install/common/systemd/build.sh index 349ff15591..958fb39c71 100644 --- a/build/install/common/systemd/build.sh +++ b/build/install/common/systemd/build.sh @@ -61,6 +61,7 @@ SERVICE_NAME=( migration doceditor migration-runner + login ) reassign_values (){ @@ -154,24 +155,32 @@ reassign_values (){ WORK_DIR="${BASE_DIR}/services/ASC.Migration.Runner/" EXEC_FILE="ASC.Migration.Runner.dll" ;; + login ) + SERVICE_PORT="5011" + WORK_DIR="${BASE_DIR}/products/ASC.Login/login/" + EXEC_FILE="server.js" + ;; esac SERVICE_NAME="$1" if [[ "${EXEC_FILE}" == *".js" ]]; then - SERVICE_TYPE="simple" + SERVICE_TYPE="simple" + RESTART="always" EXEC_START="${NODE_RUN} ${WORK_DIR}${EXEC_FILE} --app.port=${SERVICE_PORT} --app.appsettings=${PATH_TO_CONF} --app.environment=${ENVIRONMENT}" elif [[ "${SERVICE_NAME}" = "migration-runner" ]]; then - SERVICE_TYPE="notify" + SERVICE_TYPE="simple" + RESTART="no" EXEC_START="${DOTNET_RUN} ${WORK_DIR}${EXEC_FILE}" else - SERVICE_TYPE="notify" + SERVICE_TYPE="notify" + RESTART="always" EXEC_START="${DOTNET_RUN} ${WORK_DIR}${EXEC_FILE} --urls=${APP_URLS}:${SERVICE_PORT} --pathToConf=${PATH_TO_CONF} \ --'\$STORAGE_ROOT'=${STORAGE_ROOT} --log:dir=${LOG_DIR} --log:name=${SERVICE_NAME}${CORE}${ENVIRONMENT}" fi } write_to_file () { - sed -i -e 's#${SERVICE_NAME}#'$SERVICE_NAME'#g' -e 's#${WORK_DIR}#'$WORK_DIR'#g' -e \ - "s#\${EXEC_START}#$EXEC_START#g" -e "s#\${SERVICE_TYPE}#$SERVICE_TYPE#g" $BUILD_PATH/${PRODUCT}-${SERVICE_NAME[$i]}.service + sed -i -e 's#${SERVICE_NAME}#'$SERVICE_NAME'#g' -e 's#${WORK_DIR}#'$WORK_DIR'#g' -e "s#\${RESTART}#$RESTART#g" \ + -e "s#\${EXEC_START}#$EXEC_START#g" -e "s#\${SERVICE_TYPE}#$SERVICE_TYPE#g" $BUILD_PATH/${PRODUCT}-${SERVICE_NAME[$i]}.service } mkdir -p $BUILD_PATH diff --git a/build/install/common/systemd/service b/build/install/common/systemd/service index b83ce87ceb..08702e4157 100644 --- a/build/install/common/systemd/service +++ b/build/install/common/systemd/service @@ -10,7 +10,7 @@ WorkingDirectory=${WORK_DIR} ExecStart=${EXEC_START} TimeoutSec=600 -Restart=always +Restart=${RESTART} PrivateTmp=false [Install] diff --git a/build/install/deb/debian/config b/build/install/deb/debian/config index df2b516337..c81551ba5f 100644 --- a/build/install/deb/debian/config +++ b/build/install/deb/debian/config @@ -17,6 +17,7 @@ db_input medium {{product}}/redis-port || true db_input medium {{product}}/rabbitmq-host || true db_input medium {{product}}/rabbitmq-user || true +db_input medium {{product}}/rabbitmq-port || true db_input medium {{product}}/rabbitmq-password || true db_input medium {{product}}/db-host || true diff --git a/build/install/deb/debian/control b/build/install/deb/debian/control index e34d2ddb0c..e2d7e84850 100644 --- a/build/install/deb/debian/control +++ b/build/install/deb/debian/control @@ -16,6 +16,7 @@ Depends: {{product}}-api (= {{package_header_tag_version}}), {{product}}-doceditor(= {{package_header_tag_version}}), {{product}}-files (= {{package_header_tag_version}}), {{product}}-files-services (= {{package_header_tag_version}}), + {{product}}-login (= {{package_header_tag_version}}), {{product}}-migration (= {{package_header_tag_version}}), {{product}}-migration-runner (= {{package_header_tag_version}}), {{product}}-notify (= {{package_header_tag_version}}), @@ -192,3 +193,11 @@ Depends: {{product}}-common (= {{package_header_tag_version}}), ${misc:Depends}, ${shlibs:Depends} Description: Description + +Package: {{product}}-login +Architecture: any +Depends: {{product}}-common (= {{package_header_tag_version}}), + nodejs (>=14), + ${misc:Depends}, + ${shlibs:Depends} +Description: Description diff --git a/build/install/deb/debian/postinst b/build/install/deb/debian/postinst index 812cda00e1..7f3e272931 100644 --- a/build/install/deb/debian/postinst +++ b/build/install/deb/debian/postinst @@ -31,6 +31,8 @@ case "$1" in RABBITMQ_HOST="$RET" db_get {{product}}/rabbitmq-user || true RABBITMQ_USER="$RET" + db_get {{product}}/rabbitmq-port || true + RABBITMQ_PORT="$RET" db_get {{product}}/rabbitmq-password || true RABBITMQ_PASSWORD="$RET" @@ -46,8 +48,8 @@ case "$1" in db_get onlyoffice/ds-port || true DOCUMENT_SERVER_PORT="$RET" - bash /usr/bin/{{product}}-configuration.sh -e $ENVIRONMENT -mysqlh $DB_HOST -mysqld $DB_NAME -mysqlu $DB_USER -mysqlp $DB_PWD -ash $APP_HOST -asp $APP_PORT \ - -dsh $DOCUMENT_SERVER_HOST -dsp $DOCUMENT_SERVER_PORT -rdh $REDIS_HOST -rdp $REDIS_PORT -rbh $RABBITMQ_HOST -rbu $RABBITMQ_USER -rbp $RABBITMQ_PASSWORD -ess $ELK_SHEME -esh $ELK_HOST -esp $ELK_PORT + bash /usr/bin/{{product}}-configuration.sh -e $ENVIRONMENT -mysqlh $DB_HOST -mysqld $DB_NAME -mysqlu $DB_USER -mysqlp $DB_PWD -ash $APP_HOST -asp $APP_PORT -dsh $DOCUMENT_SERVER_HOST \ + -dsp $DOCUMENT_SERVER_PORT -rdh $REDIS_HOST -rdp $REDIS_PORT -rbh $RABBITMQ_HOST -rbp $RABBITMQ_PORT -rbu $RABBITMQ_USER -rbpw $RABBITMQ_PASSWORD -ess $ELK_SHEME -esh $ELK_HOST -esp $ELK_PORT ;; abort-upgrade|abort-remove|abort-deconfigure) diff --git a/build/install/deb/debian/product-login.install b/build/install/deb/debian/product-login.install new file mode 100644 index 0000000000..23593c8ed3 --- /dev/null +++ b/build/install/deb/debian/product-login.install @@ -0,0 +1 @@ +../../../build/deploy/login/* var/www/{{product}}/products/ASC.Login/login diff --git a/build/install/deb/debian/product-proxy.install b/build/install/deb/debian/product-proxy.install index f9f4677338..bac01ba192 100644 --- a/build/install/deb/debian/product-proxy.install +++ b/build/install/deb/debian/product-proxy.install @@ -3,4 +3,3 @@ ../../../config/nginx/includes/onlyoffice*.conf etc/nginx/includes ../../../build/deploy/public/* var/www/{{product}}/public ../../../build/deploy/client/* var/www/{{product}}/client -../../../build/deploy/login/* var/www/{{product}}/login diff --git a/build/install/deb/debian/rules b/build/install/deb/debian/rules index 7db265c19e..2a996c1214 100755 --- a/build/install/deb/debian/rules +++ b/build/install/deb/debian/rules @@ -32,6 +32,7 @@ override_dh_auto_build: bash build-backend.sh -sp ${SRC_PATH}; \ bash publish-backend.sh -sp ${SRC_PATH} + rm ${SRC_PATH}/config/nginx/onlyoffice-login.conf sed -i "s@var/www@var/www/${PRODUCT}@g" ${SRC_PATH}/config/nginx/*.conf sed -i "s@var/www@var/www/${PRODUCT}@g" ${SRC_PATH}/config/nginx/includes/*.conf diff --git a/build/install/deb/debian/templates b/build/install/deb/debian/templates index 61e8bf5f84..d6e158ecb3 100644 --- a/build/install/deb/debian/templates +++ b/build/install/deb/debian/templates @@ -63,6 +63,11 @@ Type: string Default: localhost Description: RabbitMQ host: +Template: {{product}}/rabbitmq-port +Type: string +Default: 5672 +Description: RabbitMQ port: + Template: {{product}}/rabbitmq-user Type: string Default: guest diff --git a/build/install/docker/migration-runner.yml b/build/install/docker/migration-runner.yml index a8a1d1cf78..4fbf9e0582 100644 --- a/build/install/docker/migration-runner.yml +++ b/build/install/docker/migration-runner.yml @@ -2,7 +2,7 @@ version: "3.8" services: onlyoffice-migration-runner: - image: "${REPO}/${STATUS}${DOCKER_IMAGE_PREFIX}-migration-runner:${DOCKER_TAG}" + image: "${REPO}/${DOCKER_IMAGE_PREFIX}-migration-runner:${DOCKER_TAG}" container_name: ${MIGRATION_RUNNER_HOST} restart: "no" environment: diff --git a/build/install/rpm/SPECS/build.spec b/build/install/rpm/SPECS/build.spec index 1e30cd208a..b44314724b 100644 --- a/build/install/rpm/SPECS/build.spec +++ b/build/install/rpm/SPECS/build.spec @@ -7,7 +7,7 @@ bash build/install/common/build-backend.sh --srcpath %{_builddir}/%{sourcename} bash build/install/common/publish-backend.sh --srcpath %{_builddir}/%{sourcename} rename -f -v "s/product([^\/]*)$/%{product}\$1/g" build/install/common/*.sh -sed -i "s@var/www@var/www/%{product}@g" config/nginx/*.conf && sed -i "s@var/www@var/www/%{product}@g" config/nginx/includes/*.conf +sed -i "s@var/www@var/www/%{product}@g" config/nginx/*.conf && sed -i "s@var/www@var/www/%{product}@g" config/nginx/includes/*.conf && rm config/nginx/onlyoffice-login.conf json -I -f %{_builddir}/%{sourcename}/config/appsettings.services.json -e "this.logPath=\"/var/log/onlyoffice/%{product}\"" \ -e "this.urlshortener={ 'path': '../ASC.UrlShortener/index.js' }" -e "this.thumb={ 'path': '../ASC.Thumbnails/' }" -e "this.socket={ 'path': '../ASC.Socket.IO/' }" \ diff --git a/build/install/rpm/SPECS/files.spec b/build/install/rpm/SPECS/files.spec index fca820da71..9e2afd89d5 100644 --- a/build/install/rpm/SPECS/files.spec +++ b/build/install/rpm/SPECS/files.spec @@ -74,7 +74,6 @@ %{_sysconfdir}/nginx/conf.d/* %{buildpath}/public/ %{buildpath}/client/ -%{buildpath}/login/ %files studio-notify %defattr(-, onlyoffice, onlyoffice, -) @@ -189,3 +188,10 @@ %{buildpath}/services/ASC.Migration.Runner/ /usr/lib/systemd/system/%{product}-migration-runner.service %dir %{buildpath}/services/ + +%files login +%defattr(-, onlyoffice, onlyoffice, -) +%{buildpath}/products/ASC.Login/login +/usr/lib/systemd/system/%{product}-login.service +%dir %{buildpath}/products/ +%dir %{buildpath}/products/ASC.Login/ diff --git a/build/install/rpm/SPECS/install.spec b/build/install/rpm/SPECS/install.spec index d1ee96de6d..0fee71d0b1 100644 --- a/build/install/rpm/SPECS/install.spec +++ b/build/install/rpm/SPECS/install.spec @@ -29,13 +29,13 @@ mkdir -p "%{buildroot}%{buildpath}/studio/ASC.Web.Api/" mkdir -p "%{buildroot}%{buildpath}/studio/ASC.Web.Studio/" mkdir -p "%{buildroot}%{buildpath}/public/" mkdir -p "%{buildroot}%{buildpath}/client/" -mkdir -p "%{buildroot}%{buildpath}/login/" +mkdir -p "%{buildroot}%{buildpath}/products/ASC.Login/login/" mkdir -p "%{buildroot}/usr/lib/systemd/system/" cp -rf %{_builddir}/%{sourcename}/ASC.Migration.Runner/service/* "%{buildroot}%{buildpath}/services/ASC.Migration.Runner/" cp -rf %{_builddir}/%{sourcename}/build/deploy/editor/* "%{buildroot}%{buildpath}/products/ASC.Files/editor/" cp -rf %{_builddir}/%{sourcename}/build/deploy/public/* "%{buildroot}%{buildpath}/public/" cp -rf %{_builddir}/%{sourcename}/build/deploy/client/* "%{buildroot}%{buildpath}/client/" -cp -rf %{_builddir}/%{sourcename}/build/deploy/login/* "%{buildroot}%{buildpath}/login/" +cp -rf %{_builddir}/%{sourcename}/build/deploy/login/* "%{buildroot}%{buildpath}/products/ASC.Login/login/" cp -rf %{_builddir}/%{sourcename}/build/install/RadicalePlugins/* "%{buildroot}%{buildpath}/Tools/radicale/plugins/" cp -rf %{_builddir}/%{sourcename}/build/install/common/%{product}-configuration.sh "%{buildroot}%{_bindir}/" cp -rf %{_builddir}/%{sourcename}/build/install/common/systemd/modules/* "%{buildroot}/usr/lib/systemd/system/" diff --git a/build/install/rpm/SPECS/package.spec b/build/install/rpm/SPECS/package.spec index c6befc3cff..d442814eaa 100644 --- a/build/install/rpm/SPECS/package.spec +++ b/build/install/rpm/SPECS/package.spec @@ -163,3 +163,11 @@ Requires: %name-common = %version-%release Requires: dotnet-sdk-6.0 AutoReqProv: no %description migration-runner + +%package login +Summary: login +Group: Applications/Internet +Requires: %name-common = %version-%release +Requires: nodejs >= 14.0 +AutoReqProv: no +%description login diff --git a/build/install/rpm/SPECS/product.spec b/build/install/rpm/SPECS/product.spec index dd9495f853..03764ff395 100644 --- a/build/install/rpm/SPECS/product.spec +++ b/build/install/rpm/SPECS/product.spec @@ -30,6 +30,7 @@ Requires: %name-clear-events = %version-%release Requires: %name-doceditor = %version-%release Requires: %name-files = %version-%release Requires: %name-files-services = %version-%release +Requires: %name-login = %version-%release Requires: %name-migration = %version-%release Requires: %name-migration-runner = %version-%release Requires: %name-notify = %version-%release diff --git a/build/install/win/Apache Kafka.aip b/build/install/win/Apache Kafka.aip deleted file mode 100644 index 731c6efca2..0000000000 --- a/build/install/win/Apache Kafka.aip +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/install/win/Apache ZooKeeper.aip b/build/install/win/Apache ZooKeeper.aip deleted file mode 100644 index 07f73daf62..0000000000 --- a/build/install/win/Apache ZooKeeper.aip +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/install/win/CustomActions/C#/Utils/CustomAction.cs b/build/install/win/CustomActions/C#/Utils/CustomAction.cs index 45b43184c3..843e2a1210 100644 --- a/build/install/win/CustomActions/C#/Utils/CustomAction.cs +++ b/build/install/win/CustomActions/C#/Utils/CustomAction.cs @@ -1,11 +1,113 @@ using System; +using System.Collections.Generic; +using System.Text; +using System.Web; using System.Net.Sockets; using Microsoft.Deployment.WindowsInstaller; +using RabbitMQ.Client; namespace Utils { public class CustomActions { + public static string CreateAuthToken(string pkey, string machinekey) + { + using (var hasher = new System.Security.Cryptography.HMACSHA1(Encoding.UTF8.GetBytes(machinekey))) + { + var now = DateTime.UtcNow.ToString("yyyyMMddHHmmss"); + var hash = System.Web.HttpServerUtility.UrlTokenEncode(hasher.ComputeHash(Encoding.UTF8.GetBytes(string.Join("\n", now, pkey)))); + return string.Format("ASC {0}:{1}:{2}", pkey, now, hash); + } + } + + [CustomAction] + public static ActionResult SetVersionReleaseDateSign(Session session) + { + var pkey = Convert.ToString(session["VERSION.RELEASE_DATE"]); + var machineKey = Convert.ToString(session["MACHINE_KEY"]); + + session.Log("SetVersionReleaseDateSign: pkey {0}, machineKey {1}", pkey, machineKey); + + session["VERSION.RELEASE_DATE.SIGN"] = CreateAuthToken(pkey, machineKey); + + session.Log("SetVersionReleaseDateSign End: {0}", session["VERSION.RELEASE_DATE.SIGN"]); + + return ActionResult.Success; + } + + + [CustomAction] + public static ActionResult TestRedisServerConnection(Session session) + { + try + { + using (var redis = new Redis(session["REDIS_HOST_PROP"], Convert.ToInt32(session["REDIS_PORT_PROP"]))) + { + + if (!String.IsNullOrEmpty(session["REDIS_PASSWORD_PROP"].Trim())) + redis.Password = session["REDIS_PASSWORD_PROP"]; + + var pong = redis.Ping("ONLYOFFICE"); + + session.Log("Redis Status: IsConnected is {0}", !String.IsNullOrEmpty(pong)); + session["RedisServerConnectionError"] = !String.IsNullOrEmpty(pong) ? "" : String.Format("Connection Refused HOST:{0},PORT:{1},PASS:{2}", session["REDIS_HOST_PROP"], session["REDIS_PORT_PROP"], session["REDIS_PASSWORD_PROP"]); + + } + } + catch (Exception ex) + { + session.Log("RedisConnectionException '{0}'", ex.Message); + session["RedisServerConnectionError"] = String.Format("Connection Refused HOST:{0},PORT:{1},PASS:{2}", session["REDIS_HOST_PROP"], session["REDIS_PORT_PROP"], session["REDIS_PASSWORD_PROP"]); + } + + return ActionResult.Success; + } + + + [CustomAction] + public static ActionResult TestRabbitMQConnection(Session session) + { + ConnectionFactory factory = new ConnectionFactory(); + + factory.HostName = session["RABBITMQ_HOSTNAME_PROP"]; + factory.Port = Convert.ToInt32(session["RABBITMQ_PORT_PROP"]); + factory.VirtualHost = session["RABBITMQ_VIRTUALHOST_PROP"]; + factory.UserName = session["RABBITMQ_USERNAME_PROP"]; + factory.Password = session["RABBITMQ_PASSWORD_PROP"]; + + try + { + using (IConnection conn = factory.CreateConnection()) + { + session.Log("RabbitMQ Status: IsConnected is {0}", conn.IsOpen); + + session["RabbitMQServerConnectionError"] = conn.IsOpen ? "" : String.Format("Connection Refused HOST:{0}, PORT:{1}, VirtualHost:{2}, UserName:{3}, PASS:{4}", + session["RABBITMQ_HOSTNAME_PROP"], + session["RABBITMQ_PORT_PROP"], + session["RABBITMQ_VIRTUALHOST_PROP"], + session["RABBITMQ_USERNAME_PROP"], + session["RABBITMQ_PASSWORD_PROP"] + ); + } + } + catch (Exception ex) + { + + session.Log("RabbitMQ.Client.Exceptions.BrokerUnreachableException {0}", ex.Message); + session["RabbitMQServerConnectionError"] = String.Format("Connection Refused HOST:{0}, PORT:{1}, VirtualHost:{2}, UserName:{3}, PASS:{4}", + session["RABBITMQ_HOSTNAME_PROP"], + session["RABBITMQ_PORT_PROP"], + session["RABBITMQ_VIRTUALHOST_PROP"], + session["RABBITMQ_USERNAME_PROP"], + session["RABBITMQ_PASSWORD_PROP"] + ); + + } + + return ActionResult.Success; + + } + [CustomAction] public static ActionResult CheckTCPAvailability(Session session) { diff --git a/build/install/win/CustomActions/C#/Utils/Properties/AssemblyInfo.cs b/build/install/win/CustomActions/C#/Utils/Properties/AssemblyInfo.cs index 1c22b2f432..9be1eb3545 100644 --- a/build/install/win/CustomActions/C#/Utils/Properties/AssemblyInfo.cs +++ b/build/install/win/CustomActions/C#/Utils/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/build/install/win/CustomActions/C#/Utils/Utils.csproj b/build/install/win/CustomActions/C#/Utils/Utils.csproj index 2506c1d8be..d6d06a4b91 100644 --- a/build/install/win/CustomActions/C#/Utils/Utils.csproj +++ b/build/install/win/CustomActions/C#/Utils/Utils.csproj @@ -32,6 +32,9 @@ 4 + + packages\RabbitMQ.Client.3.6.5\lib\net45\RabbitMQ.Client.dll + @@ -46,8 +49,12 @@ + + + + \ No newline at end of file diff --git a/build/install/win/CustomActions/C#/Utils/packages.config b/build/install/win/CustomActions/C#/Utils/packages.config new file mode 100644 index 0000000000..97a4445b50 --- /dev/null +++ b/build/install/win/CustomActions/C#/Utils/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/build/install/win/CustomActions/C#/Utils/redis-sharp.cs b/build/install/win/CustomActions/C#/Utils/redis-sharp.cs new file mode 100644 index 0000000000..f0f9eedd1f --- /dev/null +++ b/build/install/win/CustomActions/C#/Utils/redis-sharp.cs @@ -0,0 +1,937 @@ +// +// redis-sharp.cs: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Miguel de Icaza (miguel@gnome.org) +// +// Copyright 2010 Novell, Inc. +// +// Licensed under the same terms of reddis: new BSD license. +// +#define DEBUG + +using System; +using System.IO; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Text; +using System.Diagnostics; +using System.Linq; + +public class Redis : IDisposable { + Socket socket; + BufferedStream bstream; + + public enum KeyType { + None, String, List, Set + } + + public class ResponseException : Exception { + public ResponseException (string code) : base ("Response error") + { + Code = code; + } + + public string Code { get; private set; } + } + + public Redis (string host, int port) + { + if (host == null) + throw new ArgumentNullException ("host"); + + Host = host; + Port = port; + SendTimeout = -1; + } + + public Redis (string host) : this (host, 6379) + { + } + + public Redis () : this ("localhost", 6379) + { + } + + public string Host { get; private set; } + public int Port { get; private set; } + public int RetryTimeout { get; set; } + public int RetryCount { get; set; } + public int SendTimeout { get; set; } + public string Password { get; set; } + + int db; + public int Db { + get { + return db; + } + + set { + db = value; + SendExpectSuccess ("SELECT", db); + } + } + + public string this [string key] { + get { return GetString (key); } + set { Set (key, value); } + } + + public void Set (string key, string value) + { + if (key == null) + throw new ArgumentNullException ("key"); + if (value == null) + throw new ArgumentNullException ("value"); + + Set (key, Encoding.UTF8.GetBytes (value)); + } + + + public void Set (string key, byte [] value) + { + if (key == null) + throw new ArgumentNullException ("key"); + if (value == null) + throw new ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new ArgumentException ("value exceeds 1G", "value"); + + if (!SendDataCommand (value, "SET", key)) + throw new Exception ("Unable to connect"); + ExpectSuccess (); + } + + public bool SetNX (string key, string value) + { + if (key == null) + throw new ArgumentNullException ("key"); + if (value == null) + throw new ArgumentNullException ("value"); + + return SetNX (key, Encoding.UTF8.GetBytes (value)); + } + + public bool SetNX (string key, byte [] value) + { + if (key == null) + throw new ArgumentNullException ("key"); + if (value == null) + throw new ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new ArgumentException ("value exceeds 1G", "value"); + + return SendDataExpectInt (value, "SETNX", key) > 0 ? true : false; + } + + public void Set (IDictionary dict) + { + if (dict == null) + throw new ArgumentNullException ("dict"); + + Set (dict.ToDictionary(k => k.Key, v => Encoding.UTF8.GetBytes(v.Value))); + } + + public void Set (IDictionary dict) + { + if (dict == null) + throw new ArgumentNullException ("dict"); + + MSet (dict.Keys.ToArray (), dict.Values.ToArray ()); + } + + public void MSet (string [] keys, byte [][] values) + { + if (keys.Length != values.Length) + throw new ArgumentException ("keys and values must have the same size"); + + byte [] nl = Encoding.UTF8.GetBytes ("\r\n"); + MemoryStream ms = new MemoryStream (); + + for (int i = 0; i < keys.Length; i++) { + byte [] key = Encoding.UTF8.GetBytes(keys[i]); + byte [] val = values[i]; + byte [] kLength = Encoding.UTF8.GetBytes ("$" + key.Length + "\r\n"); + byte [] k = Encoding.UTF8.GetBytes (keys[i] + "\r\n"); + byte [] vLength = Encoding.UTF8.GetBytes ("$" + val.Length + "\r\n"); + ms.Write (kLength, 0, kLength.Length); + ms.Write (k, 0, k.Length); + ms.Write (vLength, 0, vLength.Length); + ms.Write (val, 0, val.Length); + ms.Write (nl, 0, nl.Length); + } + + SendDataRESP (ms.ToArray (), "*" + (keys.Length * 2 + 1) + "\r\n$4\r\nMSET\r\n"); + ExpectSuccess (); + } + + public byte [] Get (string key) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectData ("GET", key); + } + + public string Ping(String key) + { + return Encoding.UTF8.GetString (SendExpectData("PING", key)); + } + + public string GetString (string key) + { + if (key == null) + throw new ArgumentNullException ("key"); + return Encoding.UTF8.GetString (Get (key)); + } + + public byte [][] Sort (SortOptions options) + { + return Sort (options.Key, options.StoreInKey, options.ToArgs()); + } + + public byte [][] Sort (string key, string destination, params object [] options) + { + if (key == null) + throw new ArgumentNullException ("key"); + + int offset = string.IsNullOrEmpty (destination) ? 1 : 3; + object [] args = new object [offset + options.Length]; + + args [0] = key; + Array.Copy (options, 0, args, offset, options.Length); + if (offset == 1) { + return SendExpectDataArray ("SORT", args); + } + else { + args [1] = "STORE"; + args [2] = destination; + int n = SendExpectInt ("SORT", args); + return new byte [n][]; + } + } + + public byte [] GetSet (string key, byte [] value) + { + if (key == null) + throw new ArgumentNullException ("key"); + if (value == null) + throw new ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new ArgumentException ("value exceeds 1G", "value"); + + if (!SendDataCommand (value, "GETSET", key)) + throw new Exception ("Unable to connect"); + + return ReadData (); + } + + public string GetSet (string key, string value) + { + if (key == null) + throw new ArgumentNullException ("key"); + if (value == null) + throw new ArgumentNullException ("value"); + return Encoding.UTF8.GetString (GetSet (key, Encoding.UTF8.GetBytes (value))); + } + + string ReadLine () + { + StringBuilder sb = new StringBuilder (); + int c; + + while ((c = bstream.ReadByte ()) != -1){ + if (c == '\r') + continue; + if (c == '\n') + break; + sb.Append ((char) c); + } + return sb.ToString (); + } + + void Connect () + { + socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + socket.NoDelay = true; + socket.SendTimeout = SendTimeout; + socket.Connect (Host, Port); + if (!socket.Connected){ + socket.Close (); + socket = null; + return; + } + bstream = new BufferedStream (new NetworkStream (socket), 16*1024); + + if (Password != null) + SendExpectSuccess ("AUTH", Password); + } + + byte [] end_data = new byte [] { (byte) '\r', (byte) '\n' }; + + bool SendDataCommand (byte [] data, string cmd, params object [] args) + { + string resp = "*" + (1 + args.Length + 1).ToString () + "\r\n"; + resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; + foreach (object arg in args) { + string argStr = arg.ToString (); + int argStrLength = Encoding.UTF8.GetByteCount(argStr); + resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; + } + resp += "$" + data.Length + "\r\n"; + + return SendDataRESP (data, resp); + } + + bool SendDataRESP (byte [] data, string resp) + { + if (socket == null) + Connect (); + if (socket == null) + return false; + + byte [] r = Encoding.UTF8.GetBytes (resp); + try { + Log ("C", resp); + socket.Send (r); + if (data != null){ + socket.Send (data); + socket.Send (end_data); + } + } catch (SocketException){ + // timeout; + socket.Close (); + socket = null; + + return false; + } + return true; + } + + bool SendCommand (string cmd, params object [] args) + { + if (socket == null) + Connect (); + if (socket == null) + return false; + + string resp = "*" + (1 + args.Length).ToString () + "\r\n"; + resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; + foreach (object arg in args) { + string argStr = arg.ToString (); + int argStrLength = Encoding.UTF8.GetByteCount(argStr); + resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; + } + + byte [] r = Encoding.UTF8.GetBytes (resp); + try { + Log ("C", resp); + socket.Send (r); + } catch (SocketException){ + // timeout; + socket.Close (); + socket = null; + + return false; + } + return true; + } + + [Conditional ("DEBUG")] + void Log (string id, string message) + { + Console.WriteLine(id + ": " + message.Trim().Replace("\r\n", " ")); + } + + void ExpectSuccess () + { + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + Log ("S", (char)c + s); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + } + + void SendExpectSuccess (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new Exception ("Unable to connect"); + + ExpectSuccess (); + } + + int SendDataExpectInt (byte[] data, string cmd, params object [] args) + { + if (!SendDataCommand (data, cmd, args)) + throw new Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + Log ("S", (char)c + s); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == ':'){ + int i; + if (int.TryParse (s, out i)) + return i; + } + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + int SendExpectInt (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + Log ("S", (char)c + s); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == ':'){ + int i; + if (int.TryParse (s, out i)) + return i; + } + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + string SendExpectString (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + Log ("S", (char)c + s); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == '+') + return s; + + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + // + // This one does not throw errors + // + string SendGetString (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new Exception ("Unable to connect"); + + return ReadLine (); + } + + byte [] SendExpectData (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new Exception ("Unable to connect"); + + return ReadData (); + } + + byte [] ReadData () + { + string s = ReadLine (); + Log ("S", s); + if (s.Length == 0) + throw new ResponseException ("Zero length respose"); + + char c = s [0]; + if (c == '-') + throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); + + if (c == '$'){ + if (s == "$-1") + return null; + int n; + + if (Int32.TryParse (s.Substring (1), out n)){ + byte [] retbuf = new byte [n]; + + int bytesRead = 0; + do { + int read = bstream.Read (retbuf, bytesRead, n - bytesRead); + if (read < 1) + throw new ResponseException("Invalid termination mid stream"); + bytesRead += read; + } + while (bytesRead < n); + if (bstream.ReadByte () != '\r' || bstream.ReadByte () != '\n') + throw new ResponseException ("Invalid termination"); + return retbuf; + } + throw new ResponseException ("Invalid length"); + } + + /* don't treat arrays here because only one element works -- use DataArray! + //returns the number of matches + if (c == '*') { + int n; + if (Int32.TryParse(s.Substring(1), out n)) + return n <= 0 ? new byte [0] : ReadData(); + + throw new ResponseException ("Unexpected length parameter" + r); + } + */ + + throw new ResponseException ("Unexpected reply: " + s); + } + + public bool ContainsKey (string key) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("EXISTS", key) == 1; + } + + public bool Remove (string key) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("DEL", key) == 1; + } + + public int Remove (params string [] args) + { + if (args == null) + throw new ArgumentNullException ("args"); + return SendExpectInt ("DEL", args); + } + + public int Increment (string key) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("INCR", key); + } + + public int Increment (string key, int count) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("INCRBY", key, count); + } + + public int Decrement (string key) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("DECR", key); + } + + public int Decrement (string key, int count) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("DECRBY", key, count); + } + + public KeyType TypeOf (string key) + { + if (key == null) + throw new ArgumentNullException ("key"); + switch (SendExpectString ("TYPE", key)) { + case "none": + return KeyType.None; + case "string": + return KeyType.String; + case "set": + return KeyType.Set; + case "list": + return KeyType.List; + } + throw new ResponseException ("Invalid value"); + } + + public string RandomKey () + { + return SendExpectString ("RANDOMKEY"); + } + + public bool Rename (string oldKeyname, string newKeyname) + { + if (oldKeyname == null) + throw new ArgumentNullException ("oldKeyname"); + if (newKeyname == null) + throw new ArgumentNullException ("newKeyname"); + return SendGetString ("RENAME", oldKeyname, newKeyname) [0] == '+'; + } + + public bool Expire (string key, int seconds) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("EXPIRE", key, seconds) == 1; + } + + public bool ExpireAt (string key, int time) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("EXPIREAT", key, time) == 1; + } + + public int TimeToLive (string key) + { + if (key == null) + throw new ArgumentNullException ("key"); + return SendExpectInt ("TTL", key); + } + + public int DbSize { + get { + return SendExpectInt ("DBSIZE"); + } + } + + public void Save () + { + SendExpectSuccess ("SAVE"); + } + + public void BackgroundSave () + { + SendExpectSuccess ("BGSAVE"); + } + + public void Shutdown () + { + SendCommand ("SHUTDOWN"); + try { + // the server may return an error + string s = ReadLine (); + Log ("S", s); + if (s.Length == 0) + throw new ResponseException ("Zero length respose"); + throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); + } catch (IOException) { + // this is the expected good result + socket.Close (); + socket = null; + } + } + + public void FlushAll () + { + SendExpectSuccess ("FLUSHALL"); + } + + public void FlushDb () + { + SendExpectSuccess ("FLUSHDB"); + } + + const long UnixEpoch = 621355968000000000L; + + public DateTime LastSave { + get { + int t = SendExpectInt ("LASTSAVE"); + + return new DateTime (UnixEpoch) + TimeSpan.FromSeconds (t); + } + } + + public Dictionary GetInfo () + { + byte [] r = SendExpectData ("INFO"); + var dict = new Dictionary(); + + foreach (var line in Encoding.UTF8.GetString (r).Split ('\n')){ + int p = line.IndexOf (':'); + if (p == -1) + continue; + dict.Add (line.Substring (0, p), line.Substring (p+1)); + } + return dict; + } + + public string [] Keys { + get { + return GetKeys("*"); + } + } + + public string [] GetKeys (string pattern) + { + if (pattern == null) + throw new ArgumentNullException ("pattern"); + + return SendExpectStringArray ("KEYS", pattern); + } + + public byte [][] MGet (params string [] keys) + { + if (keys == null) + throw new ArgumentNullException ("keys"); + if (keys.Length == 0) + throw new ArgumentException ("keys"); + + return SendExpectDataArray ("MGET", keys); + } + + + public string [] SendExpectStringArray (string cmd, params object [] args) + { + byte [][] reply = SendExpectDataArray (cmd, args); + string [] keys = new string [reply.Length]; + for (int i = 0; i < reply.Length; i++) + keys[i] = Encoding.UTF8.GetString (reply[i]); + return keys; + } + + public byte[][] SendExpectDataArray (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new Exception("Unable to connect"); + int c = bstream.ReadByte(); + if (c == -1) + throw new ResponseException("No more data"); + + string s = ReadLine(); + Log("S", (char)c + s); + if (c == '-') + throw new ResponseException(s.StartsWith("ERR ") ? s.Substring(4) : s); + if (c == '*') { + int count; + if (int.TryParse (s, out count)) { + byte [][] result = new byte [count][]; + + for (int i = 0; i < count; i++) + result[i] = ReadData(); + + return result; + } + } + throw new ResponseException("Unknown reply on multi-request: " + c + s); + } + + #region List commands + public byte[][] ListRange(string key, int start, int end) + { + return SendExpectDataArray ("LRANGE", key, start, end); + } + + public void LeftPush(string key, string value) + { + LeftPush(key, Encoding.UTF8.GetBytes (value)); + } + + public void LeftPush(string key, byte [] value) + { + SendDataCommand (value, "LPUSH", key); + ExpectSuccess(); + } + + public void RightPush(string key, string value) + { + RightPush(key, Encoding.UTF8.GetBytes (value)); + } + + public void RightPush(string key, byte [] value) + { + SendDataCommand (value, "RPUSH", key); + ExpectSuccess(); + } + + public int ListLength (string key) + { + return SendExpectInt ("LLEN", key); + } + + public byte[] ListIndex (string key, int index) + { + SendCommand ("LINDEX", key, index); + return ReadData (); + } + + public byte[] LeftPop(string key) + { + SendCommand ("LPOP", key); + return ReadData (); + } + + public byte[] RightPop(string key) + { + SendCommand ("RPOP", key); + return ReadData (); + } + #endregion + + #region Set commands + public bool AddToSet (string key, byte[] member) + { + return SendDataExpectInt(member, "SADD", key) > 0; + } + + public bool AddToSet (string key, string member) + { + return AddToSet (key, Encoding.UTF8.GetBytes(member)); + } + + public int CardinalityOfSet (string key) + { + return SendExpectInt ("SCARD", key); + } + + public bool IsMemberOfSet (string key, byte[] member) + { + return SendDataExpectInt (member, "SISMEMBER", key) > 0; + } + + public bool IsMemberOfSet(string key, string member) + { + return IsMemberOfSet(key, Encoding.UTF8.GetBytes(member)); + } + + public byte[][] GetMembersOfSet (string key) + { + return SendExpectDataArray ("SMEMBERS", key); + } + + public byte[] GetRandomMemberOfSet (string key) + { + return SendExpectData ("SRANDMEMBER", key); + } + + public byte[] PopRandomMemberOfSet (string key) + { + return SendExpectData ("SPOP", key); + } + + public bool RemoveFromSet (string key, byte[] member) + { + return SendDataExpectInt (member, "SREM", key) > 0; + } + + public bool RemoveFromSet (string key, string member) + { + return RemoveFromSet (key, Encoding.UTF8.GetBytes(member)); + } + + public byte[][] GetUnionOfSets (params string[] keys) + { + if (keys == null) + throw new ArgumentNullException(); + + return SendExpectDataArray ("SUNION", keys); + + } + + void StoreSetCommands (string cmd, params string[] keys) + { + if (String.IsNullOrEmpty(cmd)) + throw new ArgumentNullException ("cmd"); + + if (keys == null) + throw new ArgumentNullException ("keys"); + + SendExpectSuccess (cmd, keys); + } + + public void StoreUnionOfSets (params string[] keys) + { + StoreSetCommands ("SUNIONSTORE", keys); + } + + public byte[][] GetIntersectionOfSets (params string[] keys) + { + if (keys == null) + throw new ArgumentNullException(); + + return SendExpectDataArray ("SINTER", keys); + } + + public void StoreIntersectionOfSets (params string[] keys) + { + StoreSetCommands ("SINTERSTORE", keys); + } + + public byte[][] GetDifferenceOfSets (params string[] keys) + { + if (keys == null) + throw new ArgumentNullException(); + + return SendExpectDataArray ("SDIFF", keys); + } + + public void StoreDifferenceOfSets (params string[] keys) + { + StoreSetCommands ("SDIFFSTORE", keys); + } + + public bool MoveMemberToSet (string srcKey, string destKey, byte[] member) + { + return SendDataExpectInt (member, "SMOVE", srcKey, destKey) > 0; + } + #endregion + + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + + ~Redis () + { + Dispose (false); + } + + protected virtual void Dispose (bool disposing) + { + if (disposing){ + SendCommand ("QUIT"); + ExpectSuccess (); + socket.Close (); + socket = null; + } + } +} + +public class SortOptions { + public string Key { get; set; } + public bool Descending { get; set; } + public bool Lexographically { get; set; } + public Int32 LowerLimit { get; set; } + public Int32 UpperLimit { get; set; } + public string By { get; set; } + public string StoreInKey { get; set; } + public string Get { get; set; } + + public object [] ToArgs () + { + System.Collections.ArrayList args = new System.Collections.ArrayList(); + + if (LowerLimit != 0 || UpperLimit != 0) { + args.Add ("LIMIT"); + args.Add (LowerLimit); + args.Add (UpperLimit); + } + if (Lexographically) + args.Add("ALPHA"); + if (!string.IsNullOrEmpty (By)) { + args.Add("BY"); + args.Add(By); + } + if (!string.IsNullOrEmpty (Get)) { + args.Add("GET"); + args.Add(Get); + } + return args.ToArray (); + } +} diff --git a/build/install/win/DocSpace.aip b/build/install/win/DocSpace.aip index 30308dae5f..bf153527c1 100644 --- a/build/install/win/DocSpace.aip +++ b/build/install/win/DocSpace.aip @@ -28,7 +28,9 @@ + + @@ -56,8 +58,10 @@ + + @@ -71,8 +75,11 @@ + + + @@ -83,82 +90,163 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -174,20 +262,17 @@ - - + - - - - + + @@ -222,15 +307,79 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -255,7 +404,6 @@ - @@ -265,6 +413,7 @@ + @@ -578,12 +727,72 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -594,6 +803,7 @@ + @@ -620,13 +830,17 @@ + + + + @@ -639,9 +853,10 @@ + - + @@ -675,8 +890,7 @@ - - + @@ -691,8 +905,68 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -702,8 +976,8 @@ - - + + @@ -724,35 +998,42 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - + + + - + + @@ -762,6 +1043,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -795,12 +1143,14 @@ + + - + @@ -811,35 +1161,38 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + + - + @@ -847,25 +1200,28 @@ + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + @@ -930,19 +1286,15 @@ - - - - - + + + - - - - - + + + diff --git a/build/install/win/backend-build.bat b/build/install/win/backend-build.bat index f30c9733ca..f7f5256ec3 100644 --- a/build/install/win/backend-build.bat +++ b/build/install/win/backend-build.bat @@ -4,14 +4,12 @@ echo ##################### echo # build backend # echo ##################### +set SRC_PATH=%~s2 + pushd %~1 - call dotnet build ASC.Web.sln - - echo "== Build ASC.Thumbnails ==" - pushd common\ASC.Thumbnails - call yarn install --frozen-lockfile - popd + call dotnet build ASC.Web.slnf + call dotnet build ASC.Migrations.sln -o %SRC_PATH%\services\ASC.Migration.Runner\service echo "== Build ASC.UrlShortener ==" pushd common\ASC.UrlShortener diff --git a/build/install/win/build-batch.bat b/build/install/win/build-batch.bat index 91a8546f6c..efe031de69 100644 --- a/build/install/win/build-batch.bat +++ b/build/install/win/build-batch.bat @@ -1,88 +1,61 @@ REM echo ######## Set variables ######## set "publisher="Ascensio System SIA"" -set "zookeeper_version=3.7.1" -set "kafka_version=2.8.0" set "nginx_version=1.21.1" -set "scala_version=2.12" +set "nuget="%cd%\thirdparty\SimpleRestServices\src\.nuget\NuGet.exe"" REM echo ######## Extracting and preparing files to build ######## %sevenzip% x build\install\win\nginx-%nginx_version%.zip -o"build\install\win\Files" -y xcopy "build\install\win\Files\nginx-%nginx_version%" "build\install\win\Files\nginx" /s /y /b /i rmdir build\install\win\Files\nginx-%nginx_version% /s /q -rmdir build\install\win\kafka-zookeeper /s /q -md build\install\win\kafka-zookeeper md build\install\win\Files\nginx\temp md build\install\win\Files\nginx\logs -%tar% -xvf build\install\win\apache-zookeeper-%zookeeper_version%-bin.tar.gz -C build\install\win\kafka-zookeeper -%tar% -xvf build\install\win\kafka_%scala_version%-%kafka_version%.tgz -C build\install\win\kafka-zookeeper -ren build\install\win\kafka-zookeeper\apache-zookeeper-%zookeeper_version%-bin zookeeper -ren build\install\win\kafka-zookeeper\kafka_%scala_version%-%kafka_version% kafka -md build\install\win\kafka-zookeeper\kafka\tools md build\install\win\Files\tools -copy build\install\win\WinSW.NET4new.exe "build\install\win\kafka-zookeeper\kafka\tools\kafka.exe" /y -copy build\install\win\WinSW.NET4new.exe "build\install\win\kafka-zookeeper\kafka\tools\zookeeper.exe" /y -copy build\install\win\tools\zookeeper.xml "build\install\win\kafka-zookeeper\kafka\tools\zookeeper.xml" /y -copy build\install\win\tools\kafka.xml "build\install\win\kafka-zookeeper\kafka\tools\kafka.xml" /y -del /f /q build\install\win\apache-zookeeper-%zookeeper_version%-bin.* -del /f /q build\install\win\kafka_%scala_version%-%kafka_version%.* -copy build\install\win\WinSW.NET4.exe "build\install\win\Files\tools\proxy.exe" /y -copy build\install\win\tools\proxy.xml "build\install\win\Files\tools\proxy.xml" /y +md build\install\win\Files\Logs +md build\install\win\Files\service\ +md build\install\win\Files\products\ASC.Files\server\temp +md build\install\win\Files\products\ASC.People\server\temp +md build\install\win\Files\services\ASC.Data.Backup\service\temp +md build\install\win\Files\services\ASC.Files.Service\service\temp +md build\install\win\Files\services\ASC.Notify\service\temp +md build\install\win\Files\services\ASC.Studio.Notify\service\temp +md build\install\win\Files\services\ASC.TelegramService\service\temp +md build\install\win\Files\services\ASC.Data.Backup.BackgroundTasks\service\temp +md build\install\win\Files\services\ASC.ClearEvents\service\temp +md build\install\win\Files\services\ASC.Migration\service\temp +md build\install\win\Files\services\ASC.Webhooks.Service\service\temp +md build\install\win\Files\services\ASC.Web.Api\service\temp +md build\install\win\Files\services\ASC.Web.Studio\service\temp +copy build\install\win\WinSW.NET4.exe "build\install\win\Files\tools\Proxy.exe" /y +copy build\install\win\tools\Proxy.xml "build\install\win\Files\tools\Proxy.xml" /y +copy build\install\win\WinSW3.0.0.exe "build\install\win\Files\tools\Socket.IO.exe" /y +copy build\install\win\tools\Socket.IO.xml "build\install\win\Files\tools\Socket.IO.xml" /y +copy build\install\win\WinSW3.0.0.exe "build\install\win\Files\tools\UrlShortener.exe" /y +copy build\install\win\tools\UrlShortener.xml "build\install\win\Files\tools\UrlShortener.xml" /y +copy build\install\win\WinSW3.0.0.exe "build\install\win\Files\tools\SsoAuth.exe" /y +copy build\install\win\tools\SsoAuth.xml "build\install\win\Files\tools\SsoAuth.xml" /y +copy build\install\win\WinSW3.0.0.exe "build\install\win\Files\tools\DocEditor.exe" /y +copy build\install\win\tools\DocEditor.xml "build\install\win\Files\tools\DocEditor.xml" /y +copy build\install\win\WinSW3.0.0.exe "build\install\win\Files\tools\Login.exe" /y +copy build\install\win\tools\Login.xml "build\install\win\Files\tools\Login.xml" /y copy "build\install\win\nginx.conf" "build\install\win\Files\nginx\conf\nginx.conf" /y -copy "build\install\win\kafka-zookeeper\zookeeper\conf\zoo_sample.cfg" "build\install\win\kafka-zookeeper\zookeeper\conf\zoo.cfg" /y -del /f /q "build\install\win\kafka-zookeeper\zookeeper\conf\zoo_sample.cfg" rmdir build\install\win\publish /s /q +del /f /q build\install\win\Files\nginx\conf\onlyoffice-login.conf + REM echo ######## Build Utils ######## +%nuget% install %cd%\build\install\win\CustomActions\C#\Utils\packages.config -OutputDirectory %cd%\build\install\win\CustomActions\C#\Utils\packages %msbuild% build\install\win\CustomActions\C#\Utils\Utils.csproj copy build\install\win\CustomActions\C#\Utils\bin\Debug\Utils.CA.dll build\install\win\Utils.CA.dll /y rmdir build\install\win\CustomActions\C#\Utils\bin /s /q rmdir build\install\win\CustomActions\C#\Utils\obj /s /q -REM echo ######## Edit zookeeper/kafka cfg and proprties files ######## -%sed% -i "s/\(dataDir\).*/\1=.\/..\/zookeeper\/Data/g" build/install/win/kafka-zookeeper/zookeeper/conf/zoo.cfg -%sed% -i "s/\(log.dirs\)=.*/\1=kafka-logs/g" build/install/win/kafka-zookeeper/kafka/config/server.properties -%sed% -i "s/\(zookeeper.connect\)=.*/\1=localhost:2181/g" build/install/win/kafka-zookeeper/kafka/config/server.properties -%sed% -i "s/\(clientPort\)=.*/\1=2181/g" build/install/win/kafka-zookeeper/kafka/config/zookeeper.properties -%sed% -i "s/\(dataDir\).*/\1=.\/..\/zookeeper\/Data/g" build/install/win/kafka-zookeeper/kafka/config/zookeeper.properties -%sed% -i "s/\(bootstrap.servers\)=.*/\1=localhost:9092/g" build/install/win/kafka-zookeeper/kafka/config/consumer.properties -%sed% -i "s/\(bootstrap.servers\)=.*/\1=localhost:9092/g" build/install/win/kafka-zookeeper/kafka/config/connect-standalone.properties -%sed% -i "s/\(offset.storage.file.filename\)=.*/\1=kafka-offsets/g" build/install/win/kafka-zookeeper/kafka/config/connect-standalone.properties -%sed% -i "s/\(logger.kafka.controller\)=.*,/\1=INFO,/g" build/install/win/kafka-zookeeper/kafka/config/log4j.properties -%sed% -i "s/\(logger.state.change.logger\)=.*,/\1=INFO,/g" build/install/win/kafka-zookeeper/kafka/config/log4j.properties -echo log4j.logger.kafka.producer.async.DefaultEventHandler=INFO, kafkaAppender >> build/install/win/kafka-zookeeper/kafka/config/log4j.properties -echo exit /b 1 >> build/install/win/kafka-zookeeper/kafka/bin/windows/zookeeper-server-start.bat -echo exit /b 1 >> build/install/win/kafka-zookeeper/kafka/bin/windows/kafka-server-start.bat - -REM echo ######## Edit nginx conf files ######## -%sed% -i "s!#rewrite!rewrite!g" build/install/win/Files/nginx/conf/onlyoffice.conf -%sed% -i "s!/etc/nginx/includes!includes!g" build/install/win/Files/nginx/conf/onlyoffice.conf -%sed% -i "s!/var/www!..!g" build/install/win/Files/nginx/conf/onlyoffice-*.conf -%sed% -i "s!/var/www!..!g" build/install/win/Files/nginx/conf/includes/onlyoffice-*.conf - -REM echo ######## Edit json files ######## -%sed% -i "s!\(\"machinekey\":\).\".*\"!\1 \"1123askdasjklasbnd\"!g" build/install/win/Files/config/appsettings*.json -%sed% -i "s!\(\"folder\":\).\".*\"!\1 \"{APPDIRCONF}products\"!g" build/install/win/Files/config/appsettings*.json -%sed% -i "s!\(\"path\":\).\".*\"!\1 \"{APPDIRCONF}services\/ASC.Socket.IO\/service\"!g" build/install/win/Files/config/socket*.json -%sed% -i "s!\(\"path\":\).\".*\"!\1 \"{APPDIRCONF}services\/ASC.Thumbnails\/service\"!g" build/install/win/Files/config/thumb*.json -%sed% -i "s!\(\"path\":\).\".*\"!\1 \"{APPDIRCONF}services\/ASC.UrlShortener\/service\/index.js\"!g" build/install/win/Files/config/urlshortener*.json -%sed% -i "s!\(\"path\":\).\".*\"!\1 \"{APPDIRCONF}services\/ASC.SsoAuth\/service\"!g" build/install/win/Files/config/ssoauth*.json -%sed% -i "s!\(\"path\":\).\".*\"!\1 \"{APPDIRCONF}services\/ASC.UrlShortener\/service\/index.js\"!g" build/install/win/Files/config/appsettings.services.json -%sed% -i "s!\(\"log\":\).\".*\"!\1 \"{APPDIRCONF}Logs\/urlshortener.log\"!g" build/install/win/Files/config/appsettings.services.json - REM echo ######## Delete temp files ######## del /f /q build\install\win\Files\config\sed* del /f /q build\install\win\Files\nginx\conf\sed* del /f /q build\install\win\Files\nginx\conf\includes\sed* -del /f /q build\install\win\kafka-zookeeper\zookeeper\conf\sed* -del /f /q build\install\win\kafka-zookeeper\kafka\config\sed* +del /f /q build\install\win\Files\services\*\service\config\sed* del /f /q build\install\win\*.back.* -REM echo ######## Build kafka/zookeeper ######## -%AdvancedInstaller% /rebuild "build\install\win\Apache ZooKeeper.aip" -copy "build\install\win\publish\Apache ZooKeeper.msi" "build\install\win\Apache ZooKeeper.msi" /y -%AdvancedInstaller% /rebuild "build\install\win\Apache Kafka.aip" -copy "build\install\win\publish\Apache Kafka.msi" "build\install\win\Apache Kafka.msi" /y - REM echo ######## Build MySQL Server Installer ######## iscc /Qp /S"byparam="signtool" sign /a /n "%publisher%" /t http://timestamp.digicert.com $f" "build\install\win\MySQL Server Installer Runner.iss" diff --git a/build/install/win/build-download-prereq.ps1 b/build/install/win/build-download-prereq.ps1 index 0e1eb64c36..e7d48bdf35 100644 --- a/build/install/win/build-download-prereq.ps1 +++ b/build/install/win/build-download-prereq.ps1 @@ -29,9 +29,6 @@ function DownloadComponents { } } -$zookeeper_version = '3.7.1' -$kafka_version = '2.8.0' -$scala_version = '2.12' $nginx_version = '1.21.1' $path_prereq = "${pwd}\build\install\win\" @@ -43,17 +40,6 @@ $prerequisites = @( link = "https://nginx.org/download/nginx-${nginx_version}.zip"; } - @{ - download_allways = $false; - name = "apache-zookeeper-${zookeeper_version}-bin.tar.gz"; - link = "https://dlcdn.apache.org/zookeeper/zookeeper-${zookeeper_version}/apache-zookeeper-${zookeeper_version}-bin.tar.gz"; - } - @{ - download_allways = $false; - name = "kafka_${scala_version}-${kafka_version}.tgz"; - link = "https://archive.apache.org/dist/kafka/${kafka_version}/kafka_${scala_version}-${kafka_version}.tgz"; - } - @{ download_allways = $false; name = "WinSW.NET4new.exe"; @@ -61,4 +47,16 @@ $prerequisites = @( } ) +$path_nuget_packages = "${pwd}\.nuget\packages\" + +$nuget_packages = @( + @{ + download_allways = $false; + name = "rabbitmq.client.3.6.5.nupkg"; + link = "https://www.nuget.org/api/v2/package/RabbitMQ.Client/3.6.5"; + } +) + DownloadComponents $prerequisites $path_prereq + +DownloadComponents $nuget_packages $path_nuget_packages diff --git a/build/install/win/frontend-build.bat b/build/install/win/frontend-build.bat index 5719cddbab..86fd784611 100644 --- a/build/install/win/frontend-build.bat +++ b/build/install/win/frontend-build.bat @@ -4,9 +4,12 @@ echo ###################### echo # build frontend # echo ###################### -pushd %~1 +set DEBUG_INFO=%~2 + +pushd %~s1 call yarn install + if "%DEBUG_INFO%"=="true" yarn debug-info call yarn build call yarn deploy diff --git a/build/install/win/frontend-copy.bat b/build/install/win/frontend-copy.bat index 4703698f89..2fb192bb0d 100644 --- a/build/install/win/frontend-copy.bat +++ b/build/install/win/frontend-copy.bat @@ -17,10 +17,6 @@ if defined SecondArg ( ) xcopy "%PathToRepository%\build\deploy\public" "%PathToAppFolder%\public" /s /y /b /i -xcopy "%PathToRepository%\build\deploy\studio\client" "%PathToAppFolder%\studio\client" /s /y /b /i -xcopy "%PathToRepository%\build\deploy\studio\login" "%PathToAppFolder%\studio\login" /s /y /b /i -xcopy "%PathToRepository%\build\deploy\products\ASC.Files\client" "%PathToAppFolder%\products\ASC.Files\client" /s /y /b /i -xcopy "%PathToRepository%\build\deploy\products\ASC.Files\editor" "%PathToAppFolder%\products\ASC.Files\editor" /s /y /b /i -xcopy "%PathToRepository%\build\deploy\products\ASC.People\client" "%PathToAppFolder%\products\ASC.People\client" /s /y /b /i +xcopy "%PathToRepository%\build\deploy\client" "%PathToAppFolder%\client" /s /y /b /i xcopy "%PathToRepository%\config\nginx" "%PathToAppFolder%\nginx\conf" /s /y /b /i xcopy "%PathToRepository%\config\*" "%PathToAppFolder%\config" /y /b /i diff --git a/build/install/win/nginx.conf b/build/install/win/nginx.conf index e345859092..0044e55a8c 100644 --- a/build/install/win/nginx.conf +++ b/build/install/win/nginx.conf @@ -32,54 +32,7 @@ http { keepalive_timeout 65; #gzip on; - - server { - - set $public_root ../public; - listen 80; - server_name localhost; - - #charset koi8-r; - - #access_log logs/host.access.log main; - - location / { - root html; - index index.html index.htm; - } - - #error_page 404 /404.html; - - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root html; - } - } include onlyoffice.conf; include onlyoffice-*.conf; - - # HTTPS server - # - #server { - # listen 443 ssl; - # server_name localhost; - - # ssl_certificate cert.pem; - # ssl_certificate_key cert.key; - - # ssl_session_cache shared:SSL:1m; - # ssl_session_timeout 5m; - - # ssl_ciphers HIGH:!aNULL:!MD5; - # ssl_prefer_server_ciphers on; - - # location / { - # root html; - # index index.html index.htm; - # } - #} - } diff --git a/build/install/win/publish-script.bat b/build/install/win/publish-script.bat index 462d4af518..e406797a56 100644 --- a/build/install/win/publish-script.bat +++ b/build/install/win/publish-script.bat @@ -16,34 +16,24 @@ if defined SecondArg ( set PathToAppFolder=%FirstArg%\publish ) -rem publish in directory 'products' -REM dotnet publish "%PathToRepository%\products\ASC.Calendar\server\ASC.Calendar.csproj" -c Release --self-contained false -o "%PathToAppFolder%\products\ASC.Calendar\server" -REM dotnet publish "%PathToRepository%\products\ASC.CRM\server\ASC.CRM.csproj" -c Release --self-contained false -o "%PathToAppFolder%\products\ASC.CRM\server" +rem backend services (dotnet) in directory 'products' dotnet publish "%PathToRepository%\products\ASC.Files\server\ASC.Files.csproj" -c Release --self-contained false -o "%PathToAppFolder%\products\ASC.Files\server" -REM dotnet publish "%PathToRepository%\products\ASC.Mail\server\ASC.Mail.csproj" -c Release --self-contained false -o "%PathToAppFolder%\products\ASC.Mail\server" dotnet publish "%PathToRepository%\products\ASC.People\server\ASC.People.csproj" -c Release --self-contained false -o "%PathToAppFolder%\products\ASC.People\server" -REM dotnet publish "%PathToRepository%\products\ASC.Projects\server\ASC.Projects.csproj" -c Release --self-contained false -o "%PathToAppFolder%\products\ASC.Projects\server" -rem publish in directory 'services' -dotnet publish "%PathToRepository%\common\services\ASC.ApiSystem\ASC.ApiSystem.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.ApiSystem\service" +rem backend services (dotnet) in directory 'services' dotnet publish "%PathToRepository%\common\services\ASC.Data.Backup\ASC.Data.Backup.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Data.Backup\service" -dotnet publish "%PathToRepository%\common\services\ASC.Data.Storage.Encryption\ASC.Data.Storage.Encryption.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Data.Storage.Encryption\service" dotnet publish "%PathToRepository%\products\ASC.Files\service\ASC.Files.Service.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Files.Service\service" -dotnet publish "%PathToRepository%\common\services\ASC.Data.Storage.Migration\ASC.Data.Storage.Migration.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Data.Storage.Migration\service" dotnet publish "%PathToRepository%\common\services\ASC.Notify\ASC.Notify.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Notify\service" -dotnet publish "%PathToRepository%\common\services\ASC.Socket.IO.Svc\ASC.Socket.IO.Svc.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Socket.IO.Svc\service" dotnet publish "%PathToRepository%\common\services\ASC.Studio.Notify\ASC.Studio.Notify.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Studio.Notify\service" dotnet publish "%PathToRepository%\common\services\ASC.TelegramService\ASC.TelegramService.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.TelegramService\service" -dotnet publish "%PathToRepository%\common\services\ASC.Thumbnails.Svc\ASC.Thumbnails.Svc.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Thumbnails.Svc\service" -dotnet publish "%PathToRepository%\common\services\ASC.UrlShortener.Svc\ASC.UrlShortener.Svc.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.UrlShortener.Svc\service" +dotnet publish "%PathToRepository%\common\services\ASC.Data.Backup.BackgroundTasks\ASC.Data.Backup.BackgroundTasks.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Data.Backup.BackgroundTasks\service" +dotnet publish "%PathToRepository%\common\services\ASC.ClearEvents\ASC.ClearEvents.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.ClearEvents\service" +dotnet publish "%PathToRepository%\common\ASC.Migration\ASC.Migration.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Migration\service" +dotnet publish "%PathToRepository%\common\services\ASC.Webhooks.Service\ASC.Webhooks.Service.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Webhooks.Service\service" dotnet publish "%PathToRepository%\web\ASC.Web.Api\ASC.Web.Api.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Web.Api\service" dotnet publish "%PathToRepository%\web\ASC.Web.Studio\ASC.Web.Studio.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.Web.Studio\service" -dotnet publish "%PathToRepository%\common\services\ASC.SsoAuth.Svc\ASC.SsoAuth.Svc.csproj" -c Release --self-contained false -o "%PathToAppFolder%\services\ASC.SsoAuth.Svc\service" - -rem Publish backend services (Nodejs) -mkdir "%PathToAppFolder%\services\ASC.Thumbnails\service" -xcopy "%PathToRepository%\common\ASC.Thumbnails" "%PathToAppFolder%\services\ASC.Thumbnails\service" /s /y /b /i +rem backend services (Nodejs) in directory 'services' mkdir "%PathToAppFolder%\services\ASC.UrlShortener\service" xcopy "%PathToRepository%\common\ASC.UrlShortener" "%PathToAppFolder%\services\ASC.UrlShortener\service" /s /y /b /i @@ -52,3 +42,10 @@ xcopy "%PathToRepository%\common\ASC.Socket.IO" "%PathToAppFolder%\services\ASC. mkdir "%PathToAppFolder%\services\ASC.SsoAuth\service" xcopy "%PathToRepository%\common\ASC.SsoAuth" "%PathToAppFolder%\services\ASC.SsoAuth\service" /s /y /b /i + +rem backend services (Nodejs) in directory 'products' +mkdir "%PathToAppFolder%\products\ASC.Login\login" +xcopy "%PathToRepository%\build\deploy\login" "%PathToAppFolder%\products\ASC.Login\login" /s /y /b /i + +mkdir "%PathToAppFolder%\products\ASC.Files\editor" +xcopy "%PathToRepository%\build\deploy\editor" "%PathToAppFolder%\products\ASC.Files\editor" /s /y /b /i diff --git a/build/install/win/tools/DocEditor.xml b/build/install/win/tools/DocEditor.xml new file mode 100644 index 0000000000..c6d4b9665f --- /dev/null +++ b/build/install/win/tools/DocEditor.xml @@ -0,0 +1,14 @@ + + ASC.DocEditor + ASC.DocEditor + This service runs ASC.DocEditor + RealTime + Automatic + + node + "{APPDIR}products\ASC.Files\editor\server.js" + + 10240 + 8 + + \ No newline at end of file diff --git a/build/install/win/tools/Login.xml b/build/install/win/tools/Login.xml new file mode 100644 index 0000000000..d1dedd1284 --- /dev/null +++ b/build/install/win/tools/Login.xml @@ -0,0 +1,14 @@ + + ASC.Login + ASC.Login + This service runs ASC.Login + RealTime + Automatic + + node + "{APPDIR}products\ASC.Login\login\server.js" + + 10240 + 8 + + \ No newline at end of file diff --git a/build/install/win/tools/Socket.IO.xml b/build/install/win/tools/Socket.IO.xml new file mode 100644 index 0000000000..bc17e822f9 --- /dev/null +++ b/build/install/win/tools/Socket.IO.xml @@ -0,0 +1,14 @@ + + ASC.Socket.IO + ASC.Socket.IO + This service runs ASC.Socket.IO + RealTime + Automatic + + node + "{APPDIR}services\ASC.Socket.IO\service\server.js" + + 10240 + 8 + + \ No newline at end of file diff --git a/build/install/win/tools/SsoAuth.xml b/build/install/win/tools/SsoAuth.xml new file mode 100644 index 0000000000..0b724a9a34 --- /dev/null +++ b/build/install/win/tools/SsoAuth.xml @@ -0,0 +1,14 @@ + + ASC.SsoAuth + ASC.SsoAuth + This service runs ASC.SsoAuth + RealTime + Automatic + + node + "{APPDIR}services\ASC.SsoAuth\service\app.js" + + 10240 + 8 + + \ No newline at end of file diff --git a/build/install/win/tools/UrlShortener.xml b/build/install/win/tools/UrlShortener.xml new file mode 100644 index 0000000000..d30234fabb --- /dev/null +++ b/build/install/win/tools/UrlShortener.xml @@ -0,0 +1,14 @@ + + ASC.UrlShortener + ASC.UrlShortener + This service runs ASC.UrlShortener + RealTime + Automatic + + node + "{APPDIR}services\ASC.UrlShortener\service\index.js" + + 10240 + 8 + + \ No newline at end of file diff --git a/build/install/win/tools/kafka.xml b/build/install/win/tools/kafka.xml deleted file mode 100644 index 5667b177a4..0000000000 --- a/build/install/win/tools/kafka.xml +++ /dev/null @@ -1,15 +0,0 @@ - - kafka - kafka - This service runs kafka - RealTime - Automatic - - {WindowsVolume}Apache\kafka\bin\windows\kafka-server-start.bat - {WindowsVolume}Apache\kafka\config\server.properties - {WindowsVolume}Apache\kafka - - 10240 - 8 - - \ No newline at end of file diff --git a/build/install/win/tools/zookeeper.xml b/build/install/win/tools/zookeeper.xml deleted file mode 100644 index 9a8d0a8f3d..0000000000 --- a/build/install/win/tools/zookeeper.xml +++ /dev/null @@ -1,15 +0,0 @@ - - zookeeper - zookeeper - This service runs zookeeper - RealTime - Automatic - - {WindowsVolume}Apache\kafka\bin\windows\zookeeper-server-start.bat - {WindowsVolume}Apache\kafka\config\zookeeper.properties - {WindowsVolume}Apache\kafka - - 10240 - 8 - - \ No newline at end of file diff --git a/build/run/macos/api.sh b/build/run/macos/api.sh new file mode 100755 index 0000000000..238d3cee69 --- /dev/null +++ b/build/run/macos/api.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Run script directory:" $rd + +dir=$(builtin cd $rd/../../../; pwd) +echo "Root directory:" $dir + +# Web API Root +dotnet $dir/web/ASC.Web.Api/bin/Debug/ASC.Web.Api.dll urls=http://0.0.0.0:5000 $STORAGE_ROOT=$dir/Data log:dir=$dir/Logs log:name=api pathToConf=$dir/config core:products:folder=$dir/products \ No newline at end of file diff --git a/build/run/macos/backend.sh b/build/run/macos/backend.sh new file mode 100755 index 0000000000..6bde5ca7d6 --- /dev/null +++ b/build/run/macos/backend.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Root directory:" $dir + +$dir/api.sh & +$dir/studio.sh & +$dir/people.sh & +$dir/files.sh & +$dir/files.service.sh & \ No newline at end of file diff --git a/build/run/macos/docker-tools/docker-compose.yml b/build/run/macos/docker-tools/docker-compose.yml new file mode 100644 index 0000000000..1f5378f8e8 --- /dev/null +++ b/build/run/macos/docker-tools/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + rhonda_rabbitmq: + image: rabbitmq:3-management-alpine + container_name: "rabbitmq" + ports: + - 5672:5672 + - 15672:15672 + volumes: + - ~/.container-data/rabbitmq/data/:/var/lib/rabbitmq/ + - ~/.container-data/rabbitmq/log/:/var/log/rabbitmq + redis: + image: redis:latest + volumes: + - ~/.container-data/redis/log/:/var/log/redis + ports: + - 6379:6379 +volumes: + container-data: diff --git a/build/run/macos/files.service.sh b/build/run/macos/files.service.sh new file mode 100755 index 0000000000..ae06ee9944 --- /dev/null +++ b/build/run/macos/files.service.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Run script directory:" $rd + +dir=$(builtin cd $rd/../../../; pwd) +echo "Root directory:" $dir + +# Web API Files.Service +# set servicepath=%cd%\products\ASC.Files\Service\bin\Debug\ASC.Files.Service.exe urls=http://0.0.0.0:5009 $STORAGE_ROOT=%cd%\Data log:dir=%cd%\Logs log:name=files.service pathToConf=%cd%\config core:products:folder=%cd%\products +dotnet $dir/products/ASC.Files/Service/bin/Debug/ASC.Files.Service.dll urls=http://0.0.0.0:5009 $STORAGE_ROOT=$dir/Data log:dir=$dir/Logs log:name=files.service pathToConf=$dir/config core:products:folder=$dir/products \ No newline at end of file diff --git a/build/run/macos/files.sh b/build/run/macos/files.sh new file mode 100755 index 0000000000..dbba634148 --- /dev/null +++ b/build/run/macos/files.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Run script directory:" $rd + +dir=$(builtin cd $rd/../../../; pwd) +echo "Root directory:" $dir + +# Web API Files +# set servicepath=%cd%\products\ASC.Files\Server\bin\Debug\ASC.Files.exe urls=http://0.0.0.0:5007 $STORAGE_ROOT=%cd%\Data log:dir=%cd%\Logs log:name=files pathToConf=%cd%\config core:products:folder=%cd%\products +dotnet $dir/products/ASC.Files/Server/bin/Debug/ASC.Files.dll urls=http://0.0.0.0:5007 $STORAGE_ROOT=$dir/Data log:dir=$dir/Logs log:name=files pathToConf=$dir/config core:products:folder=$dir/products \ No newline at end of file diff --git a/build/run/macos/people.sh b/build/run/macos/people.sh new file mode 100755 index 0000000000..6e14fd0b53 --- /dev/null +++ b/build/run/macos/people.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Run script directory:" $rd + +dir=$(builtin cd $rd/../../../; pwd) +echo "Root directory:" $dir + +# Web API People +# set servicepath=%cd%\products\ASC.People\Server\bin\Debug\ASC.People.exe urls=http://0.0.0.0:5004 $STORAGE_ROOT=%cd%\Data log:dir=%cd%\Logs log:name=people pathToConf=%cd%\config core:products:folder=%cd%\products +dotnet $dir/products/ASC.People/Server/bin/Debug/ASC.People.dll urls=http://0.0.0.0:5004 $STORAGE_ROOT=$dir/Data log:dir=$dir/Logs log:name=people pathToConf=$dir/config core:products:folder=$dir/products \ No newline at end of file diff --git a/build/run/macos/socket.sh b/build/run/macos/socket.sh new file mode 100755 index 0000000000..903b8ee958 --- /dev/null +++ b/build/run/macos/socket.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Run script directory:" $rd + +dir=$(builtin cd $rd/../../../; pwd) +echo "Root directory:" $dir + +# Web Socket IO +node $dir/common/ASC.Socket.IO/server.js --logPath=$dir/Logs \ No newline at end of file diff --git a/build/run/macos/studio.sh b/build/run/macos/studio.sh new file mode 100755 index 0000000000..38001b0206 --- /dev/null +++ b/build/run/macos/studio.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Run script directory:" $rd + +dir=$(builtin cd $rd/../../../; pwd) +echo "Root directory:" $dir + +# Web Studio +# set servicepath=%cd%\web\ASC.Web.Studio\bin\Debug\ASC.Web.Studio.exe urls=http://0.0.0.0:5003 $STORAGE_ROOT=%cd%\Data log:dir=%cd%\Logs log:name=studio pathToConf=%cd%\config core:products:folder=%cd%\products +dotnet $dir/web/ASC.Web.Studio/bin/Debug/ASC.Web.Studio.dll urls=http://0.0.0.0:5003 $STORAGE_ROOT=$dir/Data log:dir=$dir/Logs log:name=studio pathToConf=$dir/config core:products:folder=$dir/products \ No newline at end of file diff --git a/build/runMigrations.sh b/build/runMigrations.sh new file mode 100644 index 0000000000..7758e245c1 --- /dev/null +++ b/build/runMigrations.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +echo "MIGRATIONS" +echo off + +rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +echo "Run script directory:" $rd + +dir=$(builtin cd $rd/../; pwd) +echo "Root directory:" $dir + +dotnet build $dir/asc.web.slnf +dotnet build $dir/ASC.Migrations.sln + +pushd $dir/common/Tools/ASC.Migration.Runner/bin/Debug/net6.0 +dotnet ASC.Migration.Runner.dll \ No newline at end of file diff --git a/build/start/command.ps1 b/build/start/command.ps1 index 8eef959102..d542fc0239 100644 --- a/build/start/command.ps1 +++ b/build/start/command.ps1 @@ -13,7 +13,7 @@ $CommandName = "$($args[0])"; #Write-Output "Starting $($CommandName) services at time: $(Get-Date -Format HH:mm:ss)" #Write-Output "" -Get-ChildItem -Path $WorkDir | ForEach-Object -ThrottleLimit 20 -Parallel { +Get-ChildItem -Path $WorkDir -File | ForEach-Object -ThrottleLimit 20 -Parallel { $ServiceName = "Onlyoffice$([System.IO.Path]::GetFileNameWithoutExtension($_))"; switch ( $Using:CommandName ) diff --git a/common/ASC.Api.Core/Model/EmployeeFullDto.cs b/common/ASC.Api.Core/Model/EmployeeFullDto.cs index 5dfedcd472..105a43acf6 100644 --- a/common/ASC.Api.Core/Model/EmployeeFullDto.cs +++ b/common/ASC.Api.Core/Model/EmployeeFullDto.cs @@ -198,7 +198,7 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper await Init(result, userInfo); var quotaSettings = _settingsManager.Load(); - + if (quotaSettings.EnableUserQuota) { result.UsedSpace = Math.Max(0, _userManager.FindUserQuotaRows(_context.Tenant.Id, userInfo.Id).Where(r => !string.IsNullOrEmpty(r.Tag)).Sum(r => r.Counter)); diff --git a/packages/client/public/images/empty_screen_personal.svg b/packages/client/public/images/empty_screen_personal.svg new file mode 100644 index 0000000000..1d2705c925 --- /dev/null +++ b/packages/client/public/images/empty_screen_personal.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/client/src/pages/PortalSettings/categories/security/sub-components/svg/plus.react.svg b/packages/client/public/images/plus.react.svg similarity index 100% rename from packages/client/src/pages/PortalSettings/categories/security/sub-components/svg/plus.react.svg rename to packages/client/public/images/plus.react.svg diff --git a/packages/client/public/locales/az/Files.json b/packages/client/public/locales/az/Files.json index 5239d22488..72afb656fb 100644 --- a/packages/client/public/locales/az/Files.json +++ b/packages/client/public/locales/az/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Köçür və ya Kopyala", "MoveTo": "Yerini dəyiş", "MoveToFolderMessage": "Qovluğu alt qovluğuna köçürə bilməzsiniz ", - "MyEmptyContainerDescription": "Yaratdığınız və ya yüklədiyiniz sənədlər 'Sənədlərim' bölməsində saxlanılır. Onları redaktə edə, paylaşa və kollektiv iş edə bilərsiniz.", "New": "Yeni", "NewDocument": "Yeni sənəd", "NewFolder": "Yeni qovluq", diff --git a/packages/client/public/locales/bg/Files.json b/packages/client/public/locales/bg/Files.json index 39d3fd4b96..587b1937d1 100644 --- a/packages/client/public/locales/bg/Files.json +++ b/packages/client/public/locales/bg/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Преместване или копиране", "MoveTo": "Премести в", "MoveToFolderMessage": "Не можете да преместите папката в нейната подпапка", - "MyEmptyContainerDescription": "Документите, които създавате или качвате, се пазят в раздела 'Моите документи'. Можете да ги редактирате, споделяте или да си сътрудничите за тях с отбора си.", "New": "Нов", "NewDocument": "Нов документ", "NewFolder": "Нова папка", diff --git a/packages/client/public/locales/cs/Files.json b/packages/client/public/locales/cs/Files.json index c866637469..2b4e1ef002 100644 --- a/packages/client/public/locales/cs/Files.json +++ b/packages/client/public/locales/cs/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Přesunout nebo kopírovat", "MoveTo": "Přesunout do", "MoveToFolderMessage": "Složku nelze přesunout do podsložky", - "MyEmptyContainerDescription": "Dokumenty, které jste vytvořili nebo nahráli, jsou uchovávány v sekci 'Moje dokumenty'. Můžete je upravovat, sdílet a spolupracovat na nich se svým týmem.", "New": "Nový", "NewDocument": "Nový dokument", "NewFolder": "Nová složka", diff --git a/packages/client/public/locales/de/Files.json b/packages/client/public/locales/de/Files.json index ffb518d724..047172ac4f 100644 --- a/packages/client/public/locales/de/Files.json +++ b/packages/client/public/locales/de/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Verschieben oder kopieren", "MoveTo": "Verschieben", "MoveToFolderMessage": "Ordner können nicht in Unterordner verschoben werden", - "MyEmptyContainerDescription": "Von Ihnen erstellte oder hochgeladene Dokumente werden im Abschnitt \"Meine Dokumente\" gespeichert. Sie können diese bearbeiten, freigeben und mit Ihrem Team an diesen zusammenarbeiten.", "New": "Erstellen", "NewDocument": "Neues Dokument", "NewFolder": "Neuer Ordner", diff --git a/packages/client/public/locales/el-GR/Files.json b/packages/client/public/locales/el-GR/Files.json index bf602aea76..a74d4f48a1 100644 --- a/packages/client/public/locales/el-GR/Files.json +++ b/packages/client/public/locales/el-GR/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Μετακίνηση ή αντιγραφή", "MoveTo": "Μετακίνηση σε", "MoveToFolderMessage": "Δεν μπορείτε να μετακινήσετε τον φάκελο στον υποφάκελό του", - "MyEmptyContainerDescription": "Τα έγγραφα που δημιουργείτε ή μεταφορτώνετε διατηρούνται στην ενότητα «Τα έγγραφά μου». Μπορείτε να τα επεξεργαστείτε, να τα μοιραστείτε και να συνεργαστείτε με την ομάδα σας.", "New": "Νέο", "NewDocument": "Νέο έγγραφο", "NewFolder": "Νέος φάκελος", diff --git a/packages/client/public/locales/en/Files.json b/packages/client/public/locales/en/Files.json index 03174cd3b5..d3e619460e 100644 --- a/packages/client/public/locales/en/Files.json +++ b/packages/client/public/locales/en/Files.json @@ -55,7 +55,6 @@ "MoveOrCopy": "Move or copy", "MoveTo": "Move to", "MoveToFolderMessage": "You can't move the folder to its subfolder", - "MyEmptyContainerDescription": "Documents you create or upload are kept in 'My Documents' section. You can edit, share, and collaborate on them with your team.", "New": "New", "NewDocument": "New document", "NewFolder": "New folder", @@ -67,6 +66,7 @@ "NoSubfolders": "No subfolders", "Open": "Open", "OpenLocation": "Open location", + "PersonalEmptyContainerDescription": "Drop files here or create new ones.", "Pin": "Pin to top", "Presentation": "Presentation", "Preview": "Preview", diff --git a/packages/client/public/locales/es/Files.json b/packages/client/public/locales/es/Files.json index 904947a619..3e6a0e6fd4 100644 --- a/packages/client/public/locales/es/Files.json +++ b/packages/client/public/locales/es/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Mover o Copiar", "MoveTo": "Mover a", "MoveToFolderMessage": "No se puede mover la carpeta a su subcarpeta", - "MyEmptyContainerDescription": "Los documentos que usted crea o sube se guardan en la sección 'Mis Documentos'. Usted puede editar, compartir y colaborar en ellos con su equipo.", "New": "Nuevo", "NewDocument": "Nuevo documento", "NewFolder": "Nueva carpeta", diff --git a/packages/client/public/locales/fi/Files.json b/packages/client/public/locales/fi/Files.json index b63ab42f57..e8ed2610c7 100644 --- a/packages/client/public/locales/fi/Files.json +++ b/packages/client/public/locales/fi/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Siirrä tai Kopioi", "MoveTo": "Siirrä", "MoveToFolderMessage": "Et voi siirtää kansiota sen alikansioon", - "MyEmptyContainerDescription": "Luomasi tai lataamasi asiakirjat säilytetään Omat asiakirjat-osiossa. Voit muokata, jakaa ja tehdä yhteistyötä tiimisi kanssa niillä.\n", "New": "Uusi", "NewDocument": "Uusi asiakirja", "NewFolder": "Uusi kansio", diff --git a/packages/client/public/locales/fr/Files.json b/packages/client/public/locales/fr/Files.json index ce13a565de..22e12e0fbb 100644 --- a/packages/client/public/locales/fr/Files.json +++ b/packages/client/public/locales/fr/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Déplacer ou copier", "MoveTo": "Déplacer vers", "MoveToFolderMessage": "Vous ne pouvez pas déplacer le dossier vers son sous-dossier", - "MyEmptyContainerDescription": "Les documents que vous créez ou téléchargez sont conservés dans la section \"Mes documents\". Vous pouvez les modifier, les partager et collaborer avec votre équipe.", "New": "nouveau", "NewDocument": "Nouveau document", "NewFolder": "Nouveau dossier", diff --git a/packages/client/public/locales/hy-AM/Files.json b/packages/client/public/locales/hy-AM/Files.json index 1a0f389422..1b98d16eb4 100644 --- a/packages/client/public/locales/hy-AM/Files.json +++ b/packages/client/public/locales/hy-AM/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Տեղափոխել կամ պատճենել", "MoveTo": "Տեղափոխել", "MoveToFolderMessage": "Դուք չեք կարող պանակը տեղափոխել իր ենթաթղթապանակ", - "MyEmptyContainerDescription": "Ձեր ստեղծած կամ վերբեռնած փաստաթղթերը պահվում են'Իմ փաստաթղթերը' բաժնում: Դուք կարող եք խմբագրել, համօգտագործել և գործակցել դրանք Ձեր թիմի հետ:", "New": "Նոր", "NewDocument": "Նոր փաստաթուղթ", "NewFolder": "Նոր պանակ", diff --git a/packages/client/public/locales/it/Files.json b/packages/client/public/locales/it/Files.json index 3059b86c32..619a02d221 100644 --- a/packages/client/public/locales/it/Files.json +++ b/packages/client/public/locales/it/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Sposta o Copia", "MoveTo": "Sposta in", "MoveToFolderMessage": "Non puoi spostare la cartella nella sua sottocartella", - "MyEmptyContainerDescription": "I documenti che crei o carichi vengono conservati nella sezione \"Miei Documenti\". Puoi modificarli, condividerli e collaborare con il tuo team.", "New": "Nuovo", "NewDocument": "Nuovo documento", "NewFolder": "Nuova cartella", diff --git a/packages/client/public/locales/ja-JP/Files.json b/packages/client/public/locales/ja-JP/Files.json index 861c1d8b0e..308c218826 100644 --- a/packages/client/public/locales/ja-JP/Files.json +++ b/packages/client/public/locales/ja-JP/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "移動またはコピーする", "MoveTo": "に移動します", "MoveToFolderMessage": "フォルダをそのサブフォルダに移動できません。", - "MyEmptyContainerDescription": "作成されたまたはアップロードされたドキュメントは、「マイドキュメント」セクションに保管されます。チームで編集、共有、共同作業ができます。", "New": "新しい", "NewDocument": "新しいドキュメント", "NewFolder": "新しいフォルダ", diff --git a/packages/client/public/locales/ko-KR/Files.json b/packages/client/public/locales/ko-KR/Files.json index c5c59205c3..8ef1b7e4df 100644 --- a/packages/client/public/locales/ko-KR/Files.json +++ b/packages/client/public/locales/ko-KR/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "이동 또는 복사", "MoveTo": "이동 위치", "MoveToFolderMessage": "해당 폴더를 하위 폴더로 이동할 수 없습니다", - "MyEmptyContainerDescription": "생성 또는 업로드한 문서는 '내 문서' 섹션에 보관됩니다. 팀원과 함께 문서를 편집, 공유, 공동 작업하세요.", "New": "신규", "NewDocument": "새 문서", "NewFolder": "새 폴더", diff --git a/packages/client/public/locales/lo-LA/Files.json b/packages/client/public/locales/lo-LA/Files.json index c163db5157..1916c80222 100644 --- a/packages/client/public/locales/lo-LA/Files.json +++ b/packages/client/public/locales/lo-LA/Files.json @@ -38,7 +38,6 @@ "MoveOrCopy": "ຍ້າຍ ຫຼື ສຳເນົາ", "MoveTo": "ຍ້າຍ​ໄປ", "MoveToFolderMessage": "ທ່ານບໍ່ສາມາດຍ້າຍໂຟຣເດີໄປທີ່ໂຟຣເດີຍ່ອຍຂອງມັນ", - "MyEmptyContainerDescription": "ເອກະສານທີ່ທ່ານສ້າງ ຫລື ອັບໂຫລດແມ່ນຖືກເກັບຢູ່ໃນສ່ວນ 'ເອກະສານຂອງຂ້ອຍ'. ທ່ານສາມາດແກ້ໄຂ, ແບ່ງປັນ ແລະ ເຮັດວຽກຮ່ວມກັບທີມງານຂອງທ່ານໄດ້.", "New": "ໃໝ່", "NewDocument": "ເອກະສານໃໝ່", "NewFolder": "ແຟ້ມໃໝ່", diff --git a/packages/client/public/locales/lv/Files.json b/packages/client/public/locales/lv/Files.json index 7925f352c5..24da8a2ae1 100644 --- a/packages/client/public/locales/lv/Files.json +++ b/packages/client/public/locales/lv/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Pārvietot vai kopēt", "MoveTo": "Pārvietot uz", "MoveToFolderMessage": "Jūs nevarat pārvietot mapi uz tās apakšmapi", - "MyEmptyContainerDescription": "Jūsu izveidotie vai augšupielādētie dokumenti tiek glabāti sadaļā Mani dokumenti. Jūs varat tos rediģēt, koplietot un sadarboties ar savu darba grupu.", "New": "Jauns", "NewDocument": "Jauns dokuments", "NewFolder": "Jauna mape", diff --git a/packages/client/public/locales/nl/Files.json b/packages/client/public/locales/nl/Files.json index bcb79a8737..baa5a09dad 100644 --- a/packages/client/public/locales/nl/Files.json +++ b/packages/client/public/locales/nl/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Verplaats of kopieer", "MoveTo": "Verplaats naar", "MoveToFolderMessage": "U kunt de map niet naar zijn submap verplaatsen", - "MyEmptyContainerDescription": "Documenten die u maakt of uploadt, worden bewaard in het gedeelte 'Mijn Documenten'. U kunt ze bewerken, delen en er met uw team aan samenwerken.", "New": "Nieuw", "NewDocument": "Nieuw document", "NewFolder": "Nieuwe map", diff --git a/packages/client/public/locales/pl/Files.json b/packages/client/public/locales/pl/Files.json index 8ea7750f7d..99d29e41e7 100644 --- a/packages/client/public/locales/pl/Files.json +++ b/packages/client/public/locales/pl/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Przenieś lub kopiuj", "MoveTo": "Przenieś do", "MoveToFolderMessage": "Nie można przenieść folderu do jego podfolderu", - "MyEmptyContainerDescription": "Tworzone lub wgrywane przez Ciebie dokumenty są przechowywane w sekcji 'Moje dokumenty'. Możesz je edytować, udostępniać i współpracować nad nimi ze swoim zespołem.", "New": "Nowe", "NewDocument": "Nowy dokument", "NewFolder": "Nowy folder", diff --git a/packages/client/public/locales/pt-BR/Files.json b/packages/client/public/locales/pt-BR/Files.json index f5d874c616..0dba84e302 100644 --- a/packages/client/public/locales/pt-BR/Files.json +++ b/packages/client/public/locales/pt-BR/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Mover ou copiar", "MoveTo": "Mover para", "MoveToFolderMessage": "Você não pode mover a pasta para sua subpasta", - "MyEmptyContainerDescription": "Os documentos que você cria ou carrega são mantidos na seção 'Meus Documentos'. Você pode editar, compartilhar e colaborar neles com sua equipe.", "New": "Novo", "NewDocument": "Novo Documento", "NewFolder": "Nova pasta", diff --git a/packages/client/public/locales/pt/Files.json b/packages/client/public/locales/pt/Files.json index 089d7e1a1f..c819f07342 100644 --- a/packages/client/public/locales/pt/Files.json +++ b/packages/client/public/locales/pt/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Mover ou copiar", "MoveTo": "Mover para", "MoveToFolderMessage": "Não pode mover a pasta para a sua subpasta", - "MyEmptyContainerDescription": "Os documentos que cria ou envia são mantidos na secção 'Meus Documentos'. Pode editar, partilhar e colaborar neles com a sua equipa.", "New": "Novo", "NewDocument": "Novo documento", "NewFolder": "Nova pasta", diff --git a/packages/client/public/locales/ro/Files.json b/packages/client/public/locales/ro/Files.json index bee73fbb4f..777840cc1c 100644 --- a/packages/client/public/locales/ro/Files.json +++ b/packages/client/public/locales/ro/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Mutare sau copiere", "MoveTo": "Mutare în", "MoveToFolderMessage": "Nu puteți muta dosarul în subdosarul propriu", - "MyEmptyContainerDescription": "Documentele pe care le creați sau încărcați sunt stocate în Documentele mele. Puteți edita, partaja și colaborați asupra documentelor cu echipa dvs.", "New": "Nou", "NewDocument": "Document nou", "NewFolder": "Dosar nou", diff --git a/packages/client/public/locales/ru/Files.json b/packages/client/public/locales/ru/Files.json index 23ea6c91b1..ce38376c9e 100644 --- a/packages/client/public/locales/ru/Files.json +++ b/packages/client/public/locales/ru/Files.json @@ -43,7 +43,6 @@ "MoveOrCopy": "Переместить или скопировать", "MoveTo": "Переместить", "MoveToFolderMessage": "Нельзя перенести папку в свою дочернюю папку", - "MyEmptyContainerDescription": "Документы и файлы изображений, которые вы создаете или загружаете на портал, хранятся здесь, в разделе «Мои документы». Вы можете открывать и редактировать их с помощью редактора портала ONLYOFFICE ™, делиться ими с друзьями или коллегами, организовывать в папки. Перетащите файлы со своего компьютера сюда, чтобы загрузить их на свой портал еще проще.", "New": "Новое", "NewDocument": "Новый документ", "NewFolder": "Новая папка", diff --git a/packages/client/public/locales/sk/Files.json b/packages/client/public/locales/sk/Files.json index 19be0a244b..895d4c9303 100644 --- a/packages/client/public/locales/sk/Files.json +++ b/packages/client/public/locales/sk/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Presunúť alebo Kopírovať", "MoveTo": "Presunúť do", "MoveToFolderMessage": "Priečinok nemôžete presunúť do jeho podpriečinka", - "MyEmptyContainerDescription": "Dokumenty, ktoré vytvoríte alebo nahráte, sa uložia v sekcii „Moje dokumenty“. Môžete ich upravovať, zdieľať a spolupracovať na nich so svojim tímom.", "New": "Nový", "NewDocument": "Nový dokument", "NewFolder": "Nový priečinok", diff --git a/packages/client/public/locales/sl/Files.json b/packages/client/public/locales/sl/Files.json index 74aafccc62..97b5f03d32 100644 --- a/packages/client/public/locales/sl/Files.json +++ b/packages/client/public/locales/sl/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Premakni ali kopiraj", "MoveTo": "Premakni v", "MoveToFolderMessage": "Mape ne morete premakniti v njeno podmapo", - "MyEmptyContainerDescription": "Dokumenti, ki jih ustvarite ali naložite, so shranjeni v razdelku »Moji dokumenti«. Lahko jih urejate, delite in sodelujete z drugimi v svoji skupini.", "New": "Novo", "NewDocument": "Nov dokument", "NewFolder": "Nova mapa", diff --git a/packages/client/public/locales/tr/Files.json b/packages/client/public/locales/tr/Files.json index 3e3d4a3004..d30d07a40d 100644 --- a/packages/client/public/locales/tr/Files.json +++ b/packages/client/public/locales/tr/Files.json @@ -40,8 +40,6 @@ "MoveOrCopy": "Taşı veya kopyala", "MoveTo": "Şuraya taşı", "MoveToFolderMessage": "Klasörü alt klasörüne taşıyamazsınız", - "MyEmptyContainerDescription": "Oluşturduğunuz veya yüklediğiniz belgeler 'Belgelerim' bölümünde tutulur. Bunları düzenleyebilir, paylaşabilir ve ekibinizle üzerinde çalışabilirsiniz.", - "New": "Yeni", "NewDocument": "Yeni Belge", "NewFolder": "Yeni dosya", "NewMasterForm": "Yeni Form şablonu", diff --git a/packages/client/public/locales/uk-UA/Files.json b/packages/client/public/locales/uk-UA/Files.json index 72c2d989e6..203644c30d 100644 --- a/packages/client/public/locales/uk-UA/Files.json +++ b/packages/client/public/locales/uk-UA/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Перемістити або скопіювати", "MoveTo": "Перемістити до", "MoveToFolderMessage": "Ви не можете перемістити папку до її підпапки", - "MyEmptyContainerDescription": "Документи, які ви створюєте або передаєте, зберігаються в розділі \"Мої документи\". Ви можете редагувати їх, надавати до них спільний доступ та спільно працювати над ними зі своєю командою.", "New": "Створити", "NewDocument": "Документ", "NewFolder": "Папку", diff --git a/packages/client/public/locales/vi/Files.json b/packages/client/public/locales/vi/Files.json index 696555df77..46fdc148de 100644 --- a/packages/client/public/locales/vi/Files.json +++ b/packages/client/public/locales/vi/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "Di chuyển hoặc Sao chép", "MoveTo": "Chuyển tới", "MoveToFolderMessage": "Bạn không thể chuyển thư mục sang thư mục con của nó", - "MyEmptyContainerDescription": "Tài liệu bạn tạo hoặc tải lên được lưu giữ trong phần 'Tài liệu của tôi'. Bạn có thể chỉnh sửa, chia sẻ và cùng làm việc trên chúng với nhóm của mình. ", "New": "Mới", "NewDocument": "Tài liệu mới", "NewFolder": "Thư mục mới", diff --git a/packages/client/public/locales/zh-CN/Files.json b/packages/client/public/locales/zh-CN/Files.json index afa3068dae..ea22c602c1 100644 --- a/packages/client/public/locales/zh-CN/Files.json +++ b/packages/client/public/locales/zh-CN/Files.json @@ -40,7 +40,6 @@ "MoveOrCopy": "移动或复制", "MoveTo": "移动至", "MoveToFolderMessage": "您不能将文件夹移至其子文件夹", - "MyEmptyContainerDescription": "您所创建或上传的文档将被保存在'我的文档'部分。您可对其进行编辑和分享,或与团队成员就其展开协作。", "New": "新建", "NewDocument": "新文档", "NewFolder": "新文件夹", diff --git a/packages/client/src/components/Article/Body/Items.js b/packages/client/src/components/Article/Body/Items.js index ba7d9c9281..4c4bc6c432 100644 --- a/packages/client/src/components/Article/Body/Items.js +++ b/packages/client/src/components/Article/Body/Items.js @@ -342,7 +342,7 @@ const Items = ({ /> ); - items.splice(3, 0, filesHeader); + isAdmin && items.splice(3, 0, filesHeader); items.unshift(roomsHeader); items.push(otherHeader); @@ -363,6 +363,7 @@ const Items = ({ startUpload, uploadEmptyFolders, trashIsEmpty, + isAdmin, ] ); diff --git a/packages/client/src/components/Article/MainButton/index.js b/packages/client/src/components/Article/MainButton/index.js index 4fb9dcd2b8..f0e257bfb3 100644 --- a/packages/client/src/components/Article/MainButton/index.js +++ b/packages/client/src/components/Article/MainButton/index.js @@ -526,7 +526,7 @@ export default inject( )( withTranslation(["Article", "UploadPanel", "Common", "Files", "People"])( withLoader(observer(withRouter(ArticleMainButtonContent)))( - + ) ) ); diff --git a/packages/client/src/components/EmptyContainer/EmptyContainer.js b/packages/client/src/components/EmptyContainer/EmptyContainer.js index 280746f9a2..3ffd106382 100644 --- a/packages/client/src/components/EmptyContainer/EmptyContainer.js +++ b/packages/client/src/components/EmptyContainer/EmptyContainer.js @@ -2,7 +2,12 @@ import React from "react"; import styled, { css } from "styled-components"; import EmptyScreenContainer from "@docspace/components/empty-screen-container"; import NoUserSelect from "@docspace/components/utils/commonStyles"; -import { tablet, smallTablet } from "@docspace/components/utils/device"; +import { + tablet, + smallTablet, + desktop, +} from "@docspace/components/utils/device"; +import { isMobile } from "react-device-detect"; const EmptyPageStyles = css` padding: 44px 0px 64px 0px; @@ -10,9 +15,24 @@ const EmptyPageStyles = css` grid-column-gap: 40px; grid-template-columns: 100px 1fr; + .empty-folder_link:not(:last-child) { + margin-bottom: 10px; + } + + .empty-folder_link { + margin-right: 9px; + } + + @media ${desktop} { + .empty-folder_link:not(:last-child) { + margin-bottom: 2px; + } + } + @media ${tablet} { - padding: 44px 0px 64px 93px; + padding: 44px 0px 64px 0px; grid-column-gap: 33px; + margin-left: auto; } @media ${smallTablet} { @@ -39,6 +59,7 @@ const EmptyFolderWrapper = styled.div` } .empty-folder_container-image { + margin-top: 3px; cursor: pointer; } @@ -53,6 +74,9 @@ const EmptyFolderWrapper = styled.div` line-height: unset; ${NoUserSelect} } + .empty-folder_container_up-image { + ${NoUserSelect} + } .empty-folder_container-icon { height: 20px; @@ -66,6 +90,13 @@ const EmptyFolderWrapper = styled.div` bottom: 16px; } + @media screen and (max-width: 1325px) { + ${!isMobile && + css` + margin-left: 98px; + `}; + } + ${(props) => props.isEmptyPage && `${EmptyPageStyles}`} } `; diff --git a/packages/client/src/components/EmptyContainer/EmptyFolderContainer.js b/packages/client/src/components/EmptyContainer/EmptyFolderContainer.js index 0787be9a55..47e5943878 100644 --- a/packages/client/src/components/EmptyContainer/EmptyFolderContainer.js +++ b/packages/client/src/components/EmptyContainer/EmptyFolderContainer.js @@ -93,9 +93,11 @@ export default inject(({ filesStore, selectedFolderStore }) => { let isRootRoom, isRoom, id; if (navigationPath && navigationPath.length) { - isRootRoom = navigationPath[navigationPath.length - 1].isRootRoom; - isRoom = navigationPath[navigationPath.length - 1].isRoom; - id = navigationPath[navigationPath.length - 1].id; + const elem = navigationPath[0]; + + isRootRoom = elem.isRootRoom; + isRoom = elem.isRoom; + id = elem.id; } return { diff --git a/packages/client/src/components/EmptyContainer/RootFolderContainer.js b/packages/client/src/components/EmptyContainer/RootFolderContainer.js index 02d2c9adad..b957fd30b1 100644 --- a/packages/client/src/components/EmptyContainer/RootFolderContainer.js +++ b/packages/client/src/components/EmptyContainer/RootFolderContainer.js @@ -1,4 +1,5 @@ import React from "react"; +import styled from "styled-components"; import { FolderType } from "@docspace/common/constants"; import { inject, observer } from "mobx-react"; import { withTranslation, Trans } from "react-i18next"; @@ -13,6 +14,13 @@ import { getCategoryUrl } from "SRC_DIR/helpers/utils"; import { AppServerConfig } from "@docspace/common/constants"; import history from "@docspace/common/history"; import config from "PACKAGE_FILE"; +import PlusIcon from "@docspace/client/public/images/plus.react.svg"; + +const StyledPlusIcon = styled(PlusIcon)` + path { + fill: #657077; + } +`; const RootFolderContainer = (props) => { const { @@ -37,11 +45,13 @@ const RootFolderContainer = (props) => { fetchRooms, setAlreadyFetchingRooms, categoryType, + isEmptyPage, + setIsEmptyPage, } = props; - const myDescription = t("MyEmptyContainerDescription"); + const personalDescription = t("PersonalEmptyContainerDescription"); const shareDescription = t("SharedEmptyContainerDescription"); const commonDescription = t("CommonEmptyContainerDescription"); - const trashHeader = t("EmptyScreenFolder"); + const emptyScreenHeader = t("EmptyScreenFolder"); const archiveHeader = t("ArchiveEmptyScreenHeader"); const noFilesHeader = t("NoFilesHereYet"); const trashDescription = t("TrashEmptyDescription"); @@ -59,18 +69,20 @@ const RootFolderContainer = (props) => { t("PrivateRoomDescriptionUnbreakable"), ]; + const roomHeader = "Welcome to DocSpace"; + const [showLoader, setShowLoader] = React.useState(false); - const [isEmptyPage, setIsEmptyPage] = React.useState(false); React.useEffect(() => { - if ( - rootFolderType !== FolderType.USER && - rootFolderType !== FolderType.COMMON - ) { + if (rootFolderType !== FolderType.COMMON) { setIsEmptyPage(true); } else { setIsEmptyPage(false); } + + return () => { + setIsEmptyPage(false); + }; }, [isEmptyPage, setIsEmptyPage, rootFolderType]); const onGoToPersonal = () => { @@ -106,8 +118,9 @@ const RootFolderContainer = (props) => { switch (rootFolderType) { case FolderType.USER: return { - descriptionText: myDescription, - imageSrc: "/static/images/empty_screen.png", + headerText: emptyScreenHeader, + descriptionText: personalDescription, + imageSrc: "images/empty_screen_personal.svg", buttons: commonButtons, }; case FolderType.SHARE: @@ -143,7 +156,7 @@ const RootFolderContainer = (props) => { }; case FolderType.TRASH: return { - headerText: trashHeader, + headerText: emptyScreenHeader, descriptionText: trashDescription, style: { gridColumnGap: "39px", gridTemplateColumns: "150px" }, imageSrc: theme.isBase @@ -153,7 +166,7 @@ const RootFolderContainer = (props) => { }; case FolderType.Rooms: return { - headerText: "Welcome to DocSpace!", + headerText: roomHeader, descriptionText: roomsDescription, imageSrc: "images/empty_screen_corporate.png", buttons: roomsButtons, @@ -207,13 +220,13 @@ const RootFolderContainer = (props) => { const commonButtons = (
- plus_icon + {t("Document")}, @@ -222,7 +235,7 @@ const RootFolderContainer = (props) => { {t("Spreadsheet")}, - {t("Presentation")} + {t("Presentation")}, {t("Translations:NewForm")} @@ -231,9 +244,8 @@ const RootFolderContainer = (props) => {
- plus_icon @@ -267,7 +279,7 @@ const RootFolderContainer = (props) => { alt="plus_icon" /> - Create room + {t("CreateEditRoomDialog:CreateRoom")}
); @@ -353,6 +365,8 @@ export default inject( fetchRooms, categoryType, setAlreadyFetchingRooms, + isEmptyPage, + setIsEmptyPage, } = filesStore; const { title, rootFolderType } = selectedFolderStore; const { isPrivacyFolder, myFolderId } = treeFoldersStore; @@ -375,6 +389,12 @@ export default inject( fetchRooms, categoryType, setAlreadyFetchingRooms, + isEmptyPage, + setIsEmptyPage, }; } -)(withTranslation("Files")(observer(RootFolderContainer))); +)( + withTranslation(["Files", "CreateEditRoomDialog"])( + observer(RootFolderContainer) + ) +); diff --git a/packages/client/src/components/FilesPanels/index.js b/packages/client/src/components/FilesPanels/index.js index 09b6f11c0c..e2f1cd60bb 100644 --- a/packages/client/src/components/FilesPanels/index.js +++ b/packages/client/src/components/FilesPanels/index.js @@ -101,7 +101,7 @@ const Panels = (props) => { onSelectFile={createMasterForm} isPanelVisible={selectFileDialogVisible} onClose={onClose} - foldersType="exceptPrivacyTrashFolders" + filteredType="exceptPrivacyTrashArchiveFolders" ByExtension searchParam={".docx"} dialogName={t("Translations:CreateMasterFormFromFile")} diff --git a/packages/client/src/components/FolderTreeBody/TreeFolders.js b/packages/client/src/components/FolderTreeBody/TreeFolders.js index 4e867d6300..148f6bab15 100644 --- a/packages/client/src/components/FolderTreeBody/TreeFolders.js +++ b/packages/client/src/components/FolderTreeBody/TreeFolders.js @@ -2,21 +2,12 @@ import React from "react"; import TreeMenu from "@docspace/components/tree-menu"; import TreeNode from "@docspace/components/tree-menu/sub-components/tree-node"; import styled from "styled-components"; -import { - ConflictResolveType, - FolderType, - ShareAccessRights, -} from "@docspace/common/constants"; -import toastr from "@docspace/components/toast/toastr"; - +import { FolderType, ShareAccessRights } from "@docspace/common/constants"; import { onConvertFiles } from "../../helpers/files-converter"; import { ReactSVG } from "react-svg"; import ExpanderDownIcon from "PUBLIC_DIR/images/expander-down.react.svg"; import ExpanderRightIcon from "PUBLIC_DIR/images/expander-right.react.svg"; import commonIconsStyles from "@docspace/components/utils/common-icons-style"; -import withLoader from "../../HOCs/withLoader"; -import Loaders from "@docspace/common/components/Loaders"; - import { observer, inject } from "mobx-react"; import { runInAction } from "mobx"; import { withTranslation } from "react-i18next"; @@ -83,7 +74,7 @@ class TreeFolders extends React.Component { constructor(props) { super(props); - this.state = { isExpand: false }; + this.state = { isExpand: false, isLoading: false }; this.selectionFoldersId = []; } @@ -107,7 +98,7 @@ class TreeFolders extends React.Component { }; getFolderIcon = (item) => { - let iconUrl = "images/catalog.folder.react.svg"; + let iconUrl = "/static/images/catalog.folder.react.svg"; switch (item.rootFolderType) { case FolderType.USER: @@ -181,14 +172,7 @@ class TreeFolders extends React.Component { }; showDragItems = (item) => { - const { - isAdmin, - myId, - commonId, - //rootFolderType, - currentId, - draggableItems, - } = this.props; + const { isAdmin, myId, commonId, currentId, draggableItems } = this.props; if (item.id === currentId) { return false; } @@ -196,10 +180,6 @@ class TreeFolders extends React.Component { if (!draggableItems || draggableItems.find((x) => x.id === item.id)) return false; - // const isMy = rootFolderType === FolderType.USER; - // const isCommon = rootFolderType === FolderType.COMMON; - // const isShare = rootFolderType === FolderType.SHARE; - if ( item.rootFolderType === FolderType.SHARE && item.access === ShareAccessRights.FullAccess @@ -208,7 +188,6 @@ class TreeFolders extends React.Component { } if (isAdmin) { - //if (isMy || isCommon || isShare) { if ( (item.pathParts && (item.pathParts[0] === myId || item.pathParts[0] === commonId)) || @@ -217,24 +196,19 @@ class TreeFolders extends React.Component { ) { return true; } - //} } else { - //if (isMy || isCommon || isShare) { if ( (item.pathParts && item.pathParts[0] === myId) || item.rootFolderType === FolderType.USER ) { return true; } - //} } return false; }; getItems = (data) => { - const { theme } = this.props; - return data.map((item) => { const dragging = this.props.dragging ? this.showDragItems(item) : false; const showBadge = false; @@ -245,9 +219,18 @@ class TreeFolders extends React.Component { disableNodeValue = ""; if (dragging) value = `${item.id} dragging ${provider}`; + const { roomsFolderId, expandedPanelKeys } = this.props; + + let isDisabledNode = false; + if (item.id == roomsFolderId) { + isDisabledNode = expandedPanelKeys?.includes(roomsFolderId + ""); + } + if (this.selectionFoldersId && this.selectionFoldersId.includes(item.id)) disableNodeValue = "disable-node"; + if (isDisabledNode) disableNodeValue += " disable-folder "; + if ((item.folders && item.folders.length > 0) || serviceFolder) { return ( { - const { data: incomingDate, certainFolders } = this.props; + const { data: incomingDate, certainFolders, roomsFolderId } = this.props; isExpand && this.setState({ isExpand: true }); - this.props.setIsLoading && this.props.setIsLoading(true); //console.log("load data...", treeNode); + this.setState({ + isLoading: true, + }); + if (this.state.isExpand && !isExpand) { return Promise.resolve(); } @@ -418,25 +404,47 @@ class TreeFolders extends React.Component { this.getNewTreeData(treeData, listIds, data.folders, data.level); !certainFolders && this.props.setTreeFolders(treeData); + + if (data.listIds[0] == roomsFolderId && this.props.onSelect) { + const roomsIndex = treeData.findIndex((f) => f.id == roomsFolderId); + const firstRoomsNodeId = treeData[roomsIndex]?.folders[0]?.id; + this.props.onSelect([firstRoomsNodeId], treeNode); + } }) .catch((err) => console.log("err", err)) .finally(() => { - this.setState({ isExpand: false }); - this.props.setIsLoading && this.props.setIsLoading(false); + this.setState({ isExpand: false, isLoading: false }); }); }; - onExpand = (expandedKeys, treeNode) => { + onExpand = (expandedKeys, treeNode, isRoom = false) => { this.expand = true; if (treeNode.node && !treeNode.node.children) { if (treeNode.expanded) { this.onLoadData(treeNode.node, true); } + } else if (isRoom) { + this.props.onSelect([treeNode.node.children[0].id], treeNode); } this.props.setExpandedPanelKeys(expandedKeys); }; + onSelect = (folder, treeNode) => { + const { onSelect, expandedPanelKeys, roomsFolderId } = this.props; + + const newExpandedPanelKeys = JSON.parse(JSON.stringify(expandedPanelKeys)); + newExpandedPanelKeys.push(folder[0]); + + if (folder[0] == roomsFolderId) { + this.onExpand(newExpandedPanelKeys, treeNode, true); + + return; + } + + onSelect && onSelect(folder, treeNode); + }; + onDragOver = (data) => { const parentElement = data.event.target.parentElement; const existElement = parentElement.classList.contains( @@ -486,22 +494,21 @@ class TreeFolders extends React.Component { firstLoadScroll(); } }; + render() { const { selectedKeys, - isLoading, - onSelect, dragging, - expandedPanelKeys, treeFolders, data, disabled, - theme, isPanel, isLoadingNodes, } = this.props; + const { isLoading } = this.state; + return ( { - const { - selection, - setIsLoading, - isLoading, - dragging, - setDragging, - } = filesStore; + const { selection, dragging, setDragging } = filesStore; const { treeFolders, setTreeFolders, myFolderId, commonFolderId, - isPrivacyFolder, setExpandedPanelKeys, getSubfolders, setIsLoadingNodes, isLoadingNodes, + roomsFolderId, } = treeFoldersStore; - const { - id, - parentId: selectedNodeParentId /* rootFolderType */, - } = selectedFolderStore; + const { id, parentId: selectedNodeParentId } = selectedFolderStore; return { isAdmin: auth.isAdmin, isDesktop: auth.settingsStore.isDesktopClient, dragging, - //rootFolderType, currentId: id, myId: myFolderId, commonId: commonFolderId, - isPrivacy: isPrivacyFolder, draggableItems: dragging ? selection : null, treeFolders, - isLoading, selectedKeys: useDefaultSelectedKeys ? treeFoldersStore.selectedKeys : selectedKeys, setDragging, - setIsLoading, setTreeFolders, setExpandedPanelKeys, getSubfolders, setIsLoadingNodes, isLoadingNodes, selectedNodeParentId, + roomsFolderId, }; } )(withTranslation(["Files", "Common"])(observer(TreeFolders))); diff --git a/packages/client/src/components/GlobalEvents/CreateEvent.js b/packages/client/src/components/GlobalEvents/CreateEvent.js index c49763e2bd..72b8c86d46 100644 --- a/packages/client/src/components/GlobalEvents/CreateEvent.js +++ b/packages/client/src/components/GlobalEvents/CreateEvent.js @@ -46,8 +46,10 @@ const CreateEvent = ({ replaceFileStream, setEncryptionAccess, + + setEventDialogVisible, + eventDialogVisible, }) => { - const [visible, setVisible] = React.useState(false); const [headerTitle, setHeaderTitle] = React.useState(null); const [startValue, setStartValue] = React.useState(""); @@ -65,7 +67,7 @@ const CreateEvent = ({ } setHeaderTitle(defaultName); - setVisible(true); + setEventDialogVisible(true); }, [extension, title, fromTemplate]); const onSave = (e, value, open = true) => { @@ -132,7 +134,7 @@ const CreateEvent = ({ toastr.error(t("Translations:FileProtected"), t("Common:Warning")); - setVisible(false); + setEventDialogVisible(false); setFormCreationInfo({ newTitle: `${newValue}.${extension}`, @@ -233,7 +235,7 @@ const CreateEvent = ({ return ( 1, - oauthHref: item.length > 1 ? item[1] : "", + isOauth: item.length > 1 && item[0] !== "WebDav", + oauthHref: item.length > 1 && item[0] !== "WebDav" ? item[1] : "", + ...(item[0] === "WebDav" && { + category: item[item.length - 1], + }), } ) .filter((item) => !!item); diff --git a/packages/client/src/components/GlobalEvents/RenameEvent.js b/packages/client/src/components/GlobalEvents/RenameEvent.js index ba7bfa997b..d9efdae0b8 100644 --- a/packages/client/src/components/GlobalEvents/RenameEvent.js +++ b/packages/client/src/components/GlobalEvents/RenameEvent.js @@ -21,9 +21,10 @@ const RenameEvent = ({ editCompleteAction, clearActiveOperations, -}) => { - const [visible, setVisible] = React.useState(false); + setEventDialogVisible, + eventDialogVisible, +}) => { const [startValue, setStartValue] = React.useState(""); const { t } = useTranslation(["Files"]); @@ -31,7 +32,7 @@ const RenameEvent = ({ React.useEffect(() => { setStartValue(getTitleWithoutExst(item, false)); - setVisible(true); + setEventDialogVisible(true); }, [item]); const onUpdate = React.useCallback((e, value) => { @@ -47,6 +48,8 @@ const RenameEvent = ({ if (isSameTitle) { setStartValue(originalTitle); + setIsLoading(false); + onClose(); return editCompleteAction(item, type); } else { @@ -112,7 +115,7 @@ const RenameEvent = ({ return ( { - const { setIsLoading, addActiveItems, updateFile, renameFolder } = filesStore; +export default inject( + ({ filesStore, filesActionsStore, uploadDataStore, dialogsStore }) => { + const { + setIsLoading, + addActiveItems, + updateFile, + renameFolder, + } = filesStore; - const { editCompleteAction } = filesActionsStore; + const { editCompleteAction } = filesActionsStore; - const { clearActiveOperations } = uploadDataStore; + const { clearActiveOperations } = uploadDataStore; + const { setEventDialogVisible, eventDialogVisible } = dialogsStore; - return { - setIsLoading, - addActiveItems, - updateFile, - renameFolder, + return { + setIsLoading, + addActiveItems, + updateFile, + renameFolder, - editCompleteAction, + editCompleteAction, - clearActiveOperations, - }; -})(observer(RenameEvent)); + clearActiveOperations, + setEventDialogVisible, + eventDialogVisible, + }; + } +)(observer(RenameEvent)); diff --git a/packages/client/src/components/GlobalEvents/sub-components/Dialog.js b/packages/client/src/components/GlobalEvents/sub-components/Dialog.js index ee4a0b4bdd..6ce44093fd 100644 --- a/packages/client/src/components/GlobalEvents/sub-components/Dialog.js +++ b/packages/client/src/components/GlobalEvents/sub-components/Dialog.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useCallback, useState } from "react"; import { inject, observer } from "mobx-react"; import toastr from "@docspace/components/toast/toastr"; @@ -6,6 +6,7 @@ import ModalDialog from "@docspace/components/modal-dialog"; import TextInput from "@docspace/components/text-input"; import Button from "@docspace/components/button"; import ComboBox from "@docspace/components/combobox"; +import { isSafari, isTablet } from "react-device-detect"; const Dialog = ({ t, @@ -20,14 +21,29 @@ const Dialog = ({ onCancel, onClose, }) => { - const [value, setValue] = React.useState(""); - const [isDisabled, setIsDisabled] = React.useState(false); + const [value, setValue] = useState(""); + const [isDisabled, setIsDisabled] = useState(false); - React.useEffect(() => { + useEffect(() => { if (startValue) setValue(startValue); }, [startValue]); - const onChange = React.useCallback((e) => { + const onKeyUpHandler = useCallback( + (e) => { + if (e.keyCode === 27) onCancelAction(e); + if (e.keyCode === 13) onSaveAction(e); + }, + [value] + ); + useEffect(() => { + document.addEventListener("keyup", onKeyUpHandler, false); + + return () => { + document.removeEventListener("keyup", onKeyUpHandler, false); + }; + }, [onKeyUpHandler]); + + const onChange = useCallback((e) => { let newValue = e.target.value; if (newValue.match(folderFormValidation)) { @@ -39,28 +55,39 @@ const Dialog = ({ setValue(newValue); }, []); - const onFocus = React.useCallback((e) => { + const onFocus = useCallback((e) => { e.target.select(); }, []); - const onSaveAction = React.useCallback( + const returnWindowPositionAfterKeyboard = () => { + isSafari && isTablet && window.scrollTo(0, 0); + }; + + const onSaveAction = useCallback( (e) => { + returnWindowPositionAfterKeyboard(); setIsDisabled(true); onSave && onSave(e, value); }, [onSave, value] ); - const onCancelAction = React.useCallback((e) => { + const onCancelAction = useCallback((e) => { + returnWindowPositionAfterKeyboard(); onCancel && onCancel(e); }, []); + const onCloseAction = useCallback((e) => { + returnWindowPositionAfterKeyboard(); + onClose && onClose(e); + }, []); + return ( {title} diff --git a/packages/client/src/components/NavMenu/sub-components/nav.js b/packages/client/src/components/NavMenu/sub-components/nav.js index 49da9d4c05..09151e0cad 100644 --- a/packages/client/src/components/NavMenu/sub-components/nav.js +++ b/packages/client/src/components/NavMenu/sub-components/nav.js @@ -2,7 +2,7 @@ import React from "react"; import PropTypes from "prop-types"; import styled from "styled-components"; import Scrollbar from "@docspace/components/scrollbar"; -import { isMobileOnly, isDesktop } from "react-device-detect"; +import { isMobileOnly, isMobile } from "react-device-detect"; import { Base } from "@docspace/components/themes"; const StyledNav = styled.nav` @@ -29,7 +29,7 @@ const StyledNav = styled.nav` ${(props) => props.numberOfModules && `@media (max-height: ${props.numberOfModules * 52 + 80}px) { - position: ${isDesktop && "relative"}; + position: ${!isMobile && "relative"}; margin-top: 16px; }`} diff --git a/packages/client/src/components/dialogs/BackupCodesDialog/index.js b/packages/client/src/components/dialogs/BackupCodesDialog/index.js index 1d8cad282f..b25d8f604e 100644 --- a/packages/client/src/components/dialogs/BackupCodesDialog/index.js +++ b/packages/client/src/components/dialogs/BackupCodesDialog/index.js @@ -8,7 +8,7 @@ import { withTranslation } from "react-i18next"; import ModalDialogContainer from "../ModalDialogContainer"; import toastr from "@docspace/components/toast/toastr"; import Link from "@docspace/components/link"; -import { isDesktop } from "react-device-detect"; +import { isMobile } from "react-device-detect"; const StyledModal = styled(ModalDialogContainer)` .backup-codes-counter { @@ -118,7 +118,7 @@ class BackupCodesDialogComponent extends React.Component { size="normal" onClick={this.props.onClose} /> - {isDesktop && ( + {!isMobile && (
({ ...item, - title: connectedCloudsTypeTitleTranslation(item.providerName, t), + title: item.category + ? item.category + : connectedCloudsTypeTitleTranslation(item.providerName, t), })); const [isOpen, setIsOpen] = useState(false); diff --git a/packages/client/src/components/panels/OperationsPanel/index.js b/packages/client/src/components/panels/OperationsPanel/index.js index 5b5cd78e8f..889b6d92ab 100644 --- a/packages/client/src/components/panels/OperationsPanel/index.js +++ b/packages/client/src/components/panels/OperationsPanel/index.js @@ -153,7 +153,7 @@ const OperationsPanelComponent = (props) => { diff --git a/packages/client/src/components/panels/SelectFileDialog/ModalView.js b/packages/client/src/components/panels/SelectFileDialog/ModalView.js deleted file mode 100644 index e211695539..0000000000 --- a/packages/client/src/components/panels/SelectFileDialog/ModalView.js +++ /dev/null @@ -1,280 +0,0 @@ -import React from "react"; -import { StyledAsidePanel, StyledSelectFilePanel } from "../StyledPanels"; -import ModalDialog from "@docspace/components/modal-dialog"; -import SelectFolderDialog from "../SelectFolderDialog"; -import FolderTreeBody from "../../FolderTreeBody"; -import FilesListBody from "./FilesListBody"; -import Button from "@docspace/components/button"; -import Text from "@docspace/components/text"; -import { isArrayEqual } from "@docspace/components/utils/array"; -import { getFoldersTree } from "@docspace/common/api/files"; -import { - exceptSortedByTagsFolders, - exceptPrivacyTrashFolders, -} from "../SelectFolderDialog/ExceptionFoldersConstants"; - -class SelectFileDialogModalView extends React.Component { - constructor(props) { - super(props); - this.state = { - isLoading: true, - isAvailable: true, - }; - this.folderList = ""; - this.noTreeSwitcher = false; - } - - componentDidMount() { - const { onSetLoadingData } = this.props; - this.setState({ isLoadingData: true }, function () { - onSetLoadingData && onSetLoadingData(true); - - this.trySwitch(); - }); - } - trySwitch = async () => { - const { - foldersType, - onSelectFolder, - selectedFolder, - passedId, - foldersList, - } = this.props; - switch (foldersType) { - case "exceptSortedByTags": - try { - const foldersTree = await getFoldersTree(); - [ - this.folderList, - this.noTreeSwitcher, - ] = SelectFolderDialog.convertFolders( - foldersTree, - exceptSortedByTagsFolders - ); - this.onSetSelectedFolder(); - } catch (err) { - console.error(err); - } - - this.loadersCompletes(); - break; - case "exceptPrivacyTrashFolders": - try { - const foldersTree = await getFoldersTree(); - [ - this.folderList, - this.noTreeSwitcher, - ] = SelectFolderDialog.convertFolders( - foldersTree, - exceptPrivacyTrashFolders - ); - this.onSetSelectedFolder(); - } catch (err) { - console.error(err); - } - this.loadersCompletes(); - break; - case "common": - try { - this.folderList = await SelectFolderDialog.getCommonFolders(); - - !selectedFolder && - onSelectFolder && - onSelectFolder( - `${ - selectedFolder - ? selectedFolder - : passedId - ? passedId - : this.folderList[0].id - }` - ); - } catch (err) { - console.error(err); - } - - this.loadersCompletes(); - break; - case "third-party": - try { - this.folderList = foldersList - ? foldersList - : await SelectFolderDialog.getCommonThirdPartyList(); - this.folderList.length !== 0 - ? this.onSetSelectedFolder() - : this.setState({ isAvailable: false }); - } catch (err) { - console.error(err); - } - - this.loadersCompletes(); - break; - } - }; - - loadersCompletes = () => { - const { onSetLoadingData } = this.props; - onSetLoadingData && onSetLoadingData(false); - - this.setState({ - isLoading: false, - }); - }; - - onSetSelectedFolder = () => { - const { onSelectFolder, selectedFolder, passedId } = this.props; - - onSelectFolder && - onSelectFolder( - `${ - selectedFolder - ? selectedFolder - : passedId - ? passedId - : this.folderList[0].id - }` - ); - }; - onSelect = (folder) => { - const { onSelectFolder, selectedFolder } = this.props; - - if (isArrayEqual([folder[0]], [selectedFolder])) { - return; - } - - onSelectFolder && onSelectFolder(folder[0]); - }; - - onMouseEvent = (event) => { - event.stopPropagation(); - }; - render() { - const { - t, - isPanelVisible, - onClose, - zIndex, - withoutProvider, - expandedKeys, - filter, - onSelectFile, - filesList, - hasNextPage, - isNextPageLoading, - loadNextPage, - selectedFolder, - titleFilesList, - loadingText, - selectedFile, - onClickSave, - headerName, - primaryButtonName, - theme, - } = this.props; - - const { isLoading, isAvailable } = this.state; - - const isHeaderChildren = !!titleFilesList; - - return ( - - - - {headerName ? headerName : t("SelectFile")} - - - -
-
- -
-
- <> - {titleFilesList && ( - - {titleFilesList} - - )} - {selectedFolder && ( - - )} - -
-
-
-
- -
@@ -578,6 +580,7 @@ export default inject( setAlreadyFetchingRooms, categoryType, + isEmptyPage, } = filesStore; const { @@ -676,6 +679,7 @@ export default inject( enablePlugins, setRestoreAllPanelVisible, + isEmptyPage, }; } )( diff --git a/packages/client/src/pages/Home/index.js b/packages/client/src/pages/Home/index.js index 64d4455255..767b652b6e 100644 --- a/packages/client/src/pages/Home/index.js +++ b/packages/client/src/pages/Home/index.js @@ -479,7 +479,7 @@ class PureHome extends React.Component { showFilter, frameConfig, withPaging, - isEmptyFilesList, + isEmptyPage, } = this.props; if (window.parent && !frameConfig) { @@ -524,7 +524,7 @@ class PureHome extends React.Component { )} - {!isEmptyFilesList && ( + {!isEmptyPage && ( {isFrame ? ( showFilter && @@ -607,7 +607,7 @@ export default inject( createRoom, refreshFiles, setViewAs, - isEmptyFilesList, + isEmptyPage, } = filesStore; const { gallerySelected } = oformsStore; @@ -741,7 +741,7 @@ export default inject( refreshFiles, setViewAs, withPaging, - isEmptyFilesList, + isEmptyPage, }; } )(withRouter(observer(Home))); diff --git a/packages/client/src/pages/PortalSettings/categories/common/branding.js b/packages/client/src/pages/PortalSettings/categories/common/branding.js index add67e2ab8..b14a626dee 100644 --- a/packages/client/src/pages/PortalSettings/categories/common/branding.js +++ b/packages/client/src/pages/PortalSettings/categories/common/branding.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import { withTranslation } from "react-i18next"; import { inject, observer } from "mobx-react"; -import { isDesktop } from "react-device-detect"; +import { isMobile } from "react-device-detect"; import withLoading from "SRC_DIR/HOCs/withLoading"; import Whitelabel from "./settingsBranding/whitelabel"; @@ -49,7 +49,7 @@ const StyledComponent = styled.div` const Branding = (props) => { const [isPortalPaid, setIsPortalPaid] = useState(true); - if (!isDesktop) return ; + if (isMobile) return ; return ( diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/index.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/index.js index 1f3cd5816e..e3bd28397e 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/index.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/index.js @@ -213,12 +213,14 @@ class AutomaticBackup extends React.PureComponent { toDefault, isCheckedThirdParty, isCheckedDocuments, - setResetProcess, + resetNewFolderPath, + + defaultFolderId, } = this.props; - + console.log("defaultFolderId", defaultFolderId); toDefault(); - - (isCheckedThirdParty || isCheckedDocuments) && setResetProcess(true); + (isCheckedThirdParty || isCheckedDocuments) && + resetNewFolderPath(defaultFolderId); this.setState({ ...(isError && { isError: false }), }); @@ -326,11 +328,11 @@ class AutomaticBackup extends React.PureComponent { isCheckedThirdParty, isCheckedDocuments, - setSavingProcess, + updateBaseFolderPath, } = this.props; try { - (isCheckedThirdParty || isCheckedDocuments) && setSavingProcess(true); + (isCheckedThirdParty || isCheckedDocuments) && updateBaseFolderPath(); await createBackupSchedule( storageType, @@ -354,7 +356,7 @@ class AutomaticBackup extends React.PureComponent { } catch (e) { toastr.error(e); - (isCheckedThirdParty || isCheckedDocuments) && setSavingProcess(true); + (isCheckedThirdParty || isCheckedDocuments) && updateBaseFolderPath(); this.setState({ isLoadingData: false, @@ -498,12 +500,7 @@ class AutomaticBackup extends React.PureComponent { {t("ThirdPartyResourceDescription")} {isCheckedThirdParty && ( - + )} @@ -545,91 +542,100 @@ class AutomaticBackup extends React.PureComponent { ); } } -export default inject(({ auth, backup, treeFoldersStore }) => { - const { language, settingsStore } = auth; - const { organizationName, theme } = settingsStore; - const { - downloadingProgress, - backupSchedule, - //commonThirdPartyList, - clearProgressInterval, - deleteSchedule, - getProgress, - setThirdPartyStorage, - setDefaultOptions, - setBackupSchedule, - selectedStorageType, - seStorageType, - //setCommonThirdPartyList, - selectedPeriodLabel, - selectedWeekdayLabel, - selectedWeekday, - selectedHour, - selectedMonthDay, - selectedMaxCopiesNumber, - selectedPeriodNumber, - selectedFolderId, - selectedStorageId, - toDefault, - isFormReady, - getStorageParams, - setSelectedEnableSchedule, - selectedEnableSchedule, - updatePathSettings, - setSavingProcess, - setResetProcess, - setStorageRegions, - } = backup; +export default inject( + ({ auth, backup, treeFoldersStore, selectFolderDialogStore }) => { + const { language, settingsStore } = auth; + const { organizationName, theme } = settingsStore; + const { + downloadingProgress, + backupSchedule, + //commonThirdPartyList, + clearProgressInterval, + deleteSchedule, + getProgress, + setThirdPartyStorage, + setDefaultOptions, + setBackupSchedule, + selectedStorageType, + seStorageType, + //setCommonThirdPartyList, + selectedPeriodLabel, + selectedWeekdayLabel, + selectedWeekday, + selectedHour, + selectedMonthDay, + selectedMaxCopiesNumber, + selectedPeriodNumber, + selectedFolderId, + selectedStorageId, + toDefault, + isFormReady, + getStorageParams, + setSelectedEnableSchedule, + selectedEnableSchedule, + updatePathSettings, - const isCheckedDocuments = selectedStorageType === `${DocumentModuleType}`; - const isCheckedThirdParty = selectedStorageType === `${ResourcesModuleType}`; - const isCheckedThirdPartyStorage = - selectedStorageType === `${StorageModuleType}`; + setStorageRegions, + defaultFolderId, + } = backup; - const { rootFoldersTitles, fetchTreeFolders } = treeFoldersStore; - return { - fetchTreeFolders, - rootFoldersTitles, - downloadingProgress, - theme, - language, - isFormReady, - organizationName, - backupSchedule, - //commonThirdPartyList, - clearProgressInterval, - deleteSchedule, - getProgress, - setThirdPartyStorage, - setDefaultOptions, - setBackupSchedule, - selectedStorageType, - seStorageType, - //setCommonThirdPartyList, - selectedPeriodLabel, - selectedWeekdayLabel, - selectedWeekday, - selectedHour, - selectedMonthDay, - selectedMaxCopiesNumber, - selectedPeriodNumber, - selectedFolderId, - selectedStorageId, + const { + updateBaseFolderPath, + resetNewFolderPath, + } = selectFolderDialogStore; - toDefault, + const isCheckedDocuments = selectedStorageType === `${DocumentModuleType}`; + const isCheckedThirdParty = + selectedStorageType === `${ResourcesModuleType}`; + const isCheckedThirdPartyStorage = + selectedStorageType === `${StorageModuleType}`; - isCheckedThirdPartyStorage, - isCheckedThirdParty, - isCheckedDocuments, + const { rootFoldersTitles, fetchTreeFolders } = treeFoldersStore; + return { + defaultFolderId, + fetchTreeFolders, + rootFoldersTitles, + downloadingProgress, + theme, + language, + isFormReady, + organizationName, + backupSchedule, + //commonThirdPartyList, + clearProgressInterval, + deleteSchedule, + getProgress, + setThirdPartyStorage, + setDefaultOptions, + setBackupSchedule, + selectedStorageType, + seStorageType, + //setCommonThirdPartyList, + selectedPeriodLabel, + selectedWeekdayLabel, + selectedWeekday, + selectedHour, + selectedMonthDay, + selectedMaxCopiesNumber, + selectedPeriodNumber, + selectedFolderId, + selectedStorageId, - getStorageParams, + toDefault, - setSelectedEnableSchedule, - selectedEnableSchedule, + isCheckedThirdPartyStorage, + isCheckedThirdParty, + isCheckedDocuments, - updatePathSettings, - setSavingProcess, - setResetProcess, - setStorageRegions, - }; -})(withTranslation(["Settings", "Common"])(observer(AutomaticBackup))); + getStorageParams, + + setSelectedEnableSchedule, + selectedEnableSchedule, + + updatePathSettings, + resetNewFolderPath, + setStorageRegions, + updateBaseFolderPath, + }; + } +)(withTranslation(["Settings", "Common"])(observer(AutomaticBackup))); diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ButtonContainer.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ButtonContainer.js index a28fe46129..aa392982cc 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ButtonContainer.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ButtonContainer.js @@ -9,18 +9,11 @@ const ButtonContainer = ({ onCancelModuleSettings, isChanged, isThirdStorageChanged, - setSavingProcess, t, - setResetProcess, }) => { const prevChange = useRef(); - useEffect(() => { - if (!isChanged && isChanged !== prevChange.current) { - setSavingProcess(false); - setResetProcess(false); - } - }, [isChanged]); + useEffect(() => { prevChange.current = isChanged; @@ -50,17 +43,10 @@ const ButtonContainer = ({ }; export default inject(({ backup }) => { - const { - isChanged, - isThirdStorageChanged, - setSavingProcess, - setResetProcess, - } = backup; + const { isChanged, isThirdStorageChanged } = backup; return { isChanged, isThirdStorageChanged, - setSavingProcess, - setResetProcess, }; })(observer(ButtonContainer)); diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/RoomsModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/RoomsModule.js index 495a520a8b..5cf324af06 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/RoomsModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/RoomsModule.js @@ -50,12 +50,11 @@ class RoomsModule extends React.PureComponent { onClickInput={this.onClickInput} isPanelVisible={isPanelVisible} isError={isError} - foldersType="exceptSortedByTags" + filteredType="exceptSortedByTags" withoutProvider isDisabled={isLoadingData} id={passedId} isReset={isResetProcess} - isSuccessSave={isSavingProcess} withoutBasicSelection={isDocumentsDefault ? false : true} /> diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ThirdPartyModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ThirdPartyModule.js index 455dec81d6..b7ba5eaf0b 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ThirdPartyModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ThirdPartyModule.js @@ -55,39 +55,16 @@ class ThirdPartyModule extends React.PureComponent { isError, isLoadingData, isReset, - isSuccessSave, + passedId, //commonThirdPartyList, isResourcesDefault, - isResetProcess, - isSavingProcess, t, ...rest } = this.props; return ( <> - {/* {!isDocSpace ? ( -
- -
- ) : ( */} - - {/* )} */} -
@@ -115,8 +90,6 @@ export default inject(({ backup }) => { defaultStorageType, commonThirdPartyList, defaultFolderId, - isResetProcess, - isSavingProcess, } = backup; const isResourcesDefault = @@ -124,8 +97,6 @@ export default inject(({ backup }) => { const passedId = isResourcesDefault ? defaultFolderId : ""; return { - isResetProcess, - isSavingProcess, setSelectedFolder, passedId, commonThirdPartyList, diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/common-container/DirectThirdPartyConnection.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/common-container/DirectThirdPartyConnection.js index e419c613aa..ade0c237f1 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/common-container/DirectThirdPartyConnection.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/common-container/DirectThirdPartyConnection.js @@ -33,8 +33,6 @@ const DirectThirdPartyConnection = (props) => { isPanelVisible, isError, id, - isReset, - isSuccessSave, withoutBasicSelection, onSelectFile, isFileSelection = false, @@ -337,7 +335,7 @@ const DirectThirdPartyConnection = (props) => { {isFileSelection ? ( { } isPanelVisible={isPanelVisible} isError={isError} - foldersList={[folderList]} + passedFoldersTree={[folderList]} withoutBasicSelection={withoutBasicSelection} - isReset={isReset} - isSuccessSave={isSuccessSave} isWaitingUpdate={isInitialLoading || isUpdatingInfo ? true : false} /> )} diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/RoomsModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/RoomsModule.js index 8ce73ca695..693a73132b 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/RoomsModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/RoomsModule.js @@ -80,7 +80,7 @@ class RoomsModule extends React.Component { onClickInput={this.onClickInput} isPanelVisible={isPanelVisible} isDisabled={isModuleDisabled} - foldersType="exceptSortedByTags" + filteredType="exceptSortedByTags" {...(selectedFolder && { id: selectedFolder })} withoutBasicSelection={selectedFolder ? false : true} /> diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/ThirdPartyModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/ThirdPartyModule.js index 9d7fd8ef5e..a3464778fd 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/ThirdPartyModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/ThirdPartyModule.js @@ -122,42 +122,6 @@ class ThirdPartyModule extends React.Component { const isModuleDisabled = !isMaxProgress || isStartCopy || isLoadingData; return ( - // !isDocSpace ? ( - // <> - //
- // - //
- //
- //
- // - // ) : (
diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/LocalFileModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/LocalFileModule.js index 3792bd279c..9edea936e8 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/LocalFileModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/LocalFileModule.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import FileInput from "@docspace/components/file-input"; const LocalFile = ({ onSelectLocalFile, hasError }) => { @@ -8,6 +8,12 @@ const LocalFile = ({ onSelectLocalFile, hasError }) => { onSelectLocalFile(data); }; + + useEffect(() => { + return () => { + onSelectLocalFile(""); + }; + }, []); return ( diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/ThirdPartyResourcesModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/ThirdPartyResourcesModule.js index adb03e2820..2f435b0d80 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/ThirdPartyResourcesModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/ThirdPartyResourcesModule.js @@ -4,18 +4,6 @@ import DirectThirdPartyConnection from "../../common-container/DirectThirdPartyC const ThirdPartyResources = (props) => { return ( - // return !isDocSpace ? ( - // - // ) : (
{ const { load, serviceProviderSettings, spMetadata } = props; const { t } = useTranslation("SingleSignOn"); - if (!isDesktop) return ; + if (isMobile) return ; useEffect(() => { load(); diff --git a/packages/client/src/pages/PortalSettings/categories/security/sub-components/svg/index.js b/packages/client/src/pages/PortalSettings/categories/security/sub-components/svg/index.js index af0f357f5f..b8c9d797f9 100644 --- a/packages/client/src/pages/PortalSettings/categories/security/sub-components/svg/index.js +++ b/packages/client/src/pages/PortalSettings/categories/security/sub-components/svg/index.js @@ -1,2 +1 @@ -export { default as PlusIcon } from "./plus.react.svg"; export { default as TrashIcon } from "./trash.react.svg"; diff --git a/packages/client/src/pages/PortalSettings/categories/security/sub-components/user-fields.js b/packages/client/src/pages/PortalSettings/categories/security/sub-components/user-fields.js index aadde073c4..82d3612d0c 100644 --- a/packages/client/src/pages/PortalSettings/categories/security/sub-components/user-fields.js +++ b/packages/client/src/pages/PortalSettings/categories/security/sub-components/user-fields.js @@ -1,7 +1,8 @@ import React, { useState, useEffect, useRef } from "react"; import styled from "styled-components"; import commonIconsStyles from "@docspace/components/utils/common-icons-style"; -import { PlusIcon, TrashIcon } from "./svg"; +import { TrashIcon } from "./svg"; +import PlusIcon from "@docspace/client/public/images/plus.react.svg"; import Link from "@docspace/components/link"; import TextInput from "@docspace/components/text-input"; import { Base } from "@docspace/components/themes"; diff --git a/packages/client/src/pages/PortalSettings/index.js b/packages/client/src/pages/PortalSettings/index.js index afa4d187d1..4c4a027656 100644 --- a/packages/client/src/pages/PortalSettings/index.js +++ b/packages/client/src/pages/PortalSettings/index.js @@ -4,6 +4,8 @@ import { withRouter } from "react-router"; import Layout from "./Layout"; import { combineUrl } from "@docspace/common/utils"; import AppServerConfig from "@docspace/common/constants/AppServerConfig"; +import Panels from "../../components/FilesPanels"; + const SecuritySettings = lazy(() => import("./categories/security/index.js")); const TfaPage = lazy(() => import("./categories/security/access-portal/tfa")); @@ -159,6 +161,7 @@ const ERROR_404_URL = combineUrl(AppServerConfig.proxyURL, "/error/404"); const Settings = () => { return ( + diff --git a/packages/client/src/pages/PreparationPortal/StyledPreparationPortal.js b/packages/client/src/pages/PreparationPortal/StyledPreparationPortal.js index 5c78aa5cab..015168e788 100644 --- a/packages/client/src/pages/PreparationPortal/StyledPreparationPortal.js +++ b/packages/client/src/pages/PreparationPortal/StyledPreparationPortal.js @@ -4,22 +4,19 @@ import { Base } from "@docspace/components/themes"; const StyledBodyPreparationPortal = styled.div` margin-bottom: 24px; - // display: flex; width: 100%; max-width: ${(props) => (props.errorMessage ? "560px" : "480px")}; padding: 0 24px; box-sizing: border-box; align-items: center; - position: relative; .preparation-portal_progress { display: flex; margin-bottom: 16px; + position: relative; .preparation-portal_progress-bar { border-radius: 2px; - margin-right: 8px; width: 100%; - height: 24px; background-color: #f3f4f4; } @@ -34,14 +31,14 @@ const StyledBodyPreparationPortal = styled.div` } .preparation-portal_percent { position: absolute; - right: 50%; - ${(props) => props.percent > 50 && "color: white"} + ${(props) => props.percent > 50 && "color: white"}; + top: 2px; + left: calc(50% - 9px); } } .preparation-portal_text { text-align: center; - color: ${(props) => props.theme.text.disableColor}; } `; diff --git a/packages/client/src/pages/Profile/Section/Header/index.js b/packages/client/src/pages/Profile/Section/Header/index.js index edd7cd7ef7..a5066aef86 100644 --- a/packages/client/src/pages/Profile/Section/Header/index.js +++ b/packages/client/src/pages/Profile/Section/Header/index.js @@ -22,7 +22,9 @@ const Header = (props) => { history, isAdmin, filter, + setFilter, + profile, isMe, setChangeEmailVisible, @@ -80,9 +82,9 @@ const Header = (props) => { return ( - {!profile.isVisitor && ( + {isAdmin && ( { export default withRouter( inject(({ auth, peopleStore }) => { const { isAdmin } = auth; + const { targetUserStore, filterStore } = peopleStore; const { filter, setFilterParams } = filterStore; @@ -137,7 +140,9 @@ export default withRouter( return { isAdmin, filter, + setFilter: setFilterParams, + profile: targetUser, isMe, setChangeEmailVisible, diff --git a/packages/client/src/store/BackupStore.js b/packages/client/src/store/BackupStore.js index 7101ed5004..62fd973616 100644 --- a/packages/client/src/store/BackupStore.js +++ b/packages/client/src/store/BackupStore.js @@ -62,9 +62,6 @@ class BackupStore { selectedEnableSchedule = false; defaultEnableSchedule = false; - isSavingProcess = false; - isResetProcess = false; - storageRegions = []; constructor() { @@ -574,13 +571,6 @@ class BackupStore { const isEnable = this.selectedEnableSchedule; this.selectedEnableSchedule = !isEnable; }; - setSavingProcess = (process) => { - if (process !== this.isSavingProcess) this.isSavingProcess = process; - }; - - setResetProcess = (process) => { - if (process !== this.isResetProcess) this.isResetProcess = process; - }; convertServiceName = (serviceName) => { //Docusign, OneDrive, Wordpress diff --git a/packages/client/src/store/ContextOptionsStore.js b/packages/client/src/store/ContextOptionsStore.js index 0f02c576f7..193b811af7 100644 --- a/packages/client/src/store/ContextOptionsStore.js +++ b/packages/client/src/store/ContextOptionsStore.js @@ -551,7 +551,7 @@ class ContextOptionsStore { { key: "room-info", label: "Info", - icon: "/static/images/info.react.svg", + icon: "/static/images/info.outline.react.svg", onClick: this.onShowInfoPanel, disabled: false, }, diff --git a/packages/client/src/store/DialogsStore.js b/packages/client/src/store/DialogsStore.js index b863ad7a8c..8a5f1e6290 100644 --- a/packages/client/src/store/DialogsStore.js +++ b/packages/client/src/store/DialogsStore.js @@ -29,6 +29,7 @@ class DialogsStore { isFolderActions = false; roomCreation = false; restoreAllPanelVisible = false; + eventDialogVisible = false; removeItem = null; connectItem = null; @@ -116,6 +117,10 @@ class DialogsStore { this.deleteDialogVisible = deleteDialogVisible; }; + setEventDialogVisible = (eventDialogVisible) => { + this.eventDialogVisible = eventDialogVisible; + }; + setDownloadDialogVisible = (downloadDialogVisible) => { !downloadDialogVisible && this.deselectActiveFiles(); this.downloadDialogVisible = downloadDialogVisible; @@ -263,7 +268,8 @@ class DialogsStore { this.convertDialogVisible || this.selectFileDialogVisible || this.authStore.settingsStore.hotkeyPanelVisible || - this.versionHistoryStore.isVisible + this.versionHistoryStore.isVisible || + this.eventDialogVisible ); } diff --git a/packages/client/src/store/FilesActionsStore.js b/packages/client/src/store/FilesActionsStore.js index 7087421e2f..6b856760a5 100644 --- a/packages/client/src/store/FilesActionsStore.js +++ b/packages/client/src/store/FilesActionsStore.js @@ -664,7 +664,7 @@ class FilesActionStore { toastr.success(translations.successRemoveFolder); } else { this.updateFilesAfterDelete([itemId]); - this.filesStore.removeFiles([itemId], null, () => + this.filesStore.removeFiles(null, [itemId], () => toastr.success(translations.successRemoveFolder) ); } @@ -689,17 +689,13 @@ class FilesActionStore { }; finalizeVersionAction = (id) => { - const { setFile, setIsLoading } = this.filesStore; + const { setFile } = this.filesStore; - setIsLoading(true); - - return finalizeVersion(id, 0, false) - .then((res) => { - if (res && res[0]) { - setFile(res[0]); - } - }) - .finally(() => setIsLoading(false)); + return finalizeVersion(id, 0, false).then((res) => { + if (res && res[0]) { + setFile(res[0]); + } + }); }; duplicateAction = (item, label) => { @@ -1159,8 +1155,6 @@ class FilesActionStore { const { userAccess } = this.filesStore; switch (option) { - case "share": - return isAccessedSelected && !personal; //isFavoritesFolder ||isRecentFolder case "showInfo": case "copy": case "download": @@ -1276,16 +1270,6 @@ class FilesActionStore { } = this.dialogsStore; switch (option) { - case "share": - if (!this.isAvailableOption("share")) return null; - else - return { - label: t("Share"), - onClick: () => setSharingPanelVisible(true), - iconUrl: "/static/images/share.react.svg", - title: t("Translations:ButtonShareAccess"), - }; - case "copy": if (!this.isAvailableOption("copy")) return null; else @@ -1416,7 +1400,6 @@ class FilesActionStore { }; getAnotherFolderOptions = (itemsCollection, t) => { - const share = this.getOption("share", t); const download = this.getOption("download", t); const downloadAs = this.getOption("downloadAs", t); const moveTo = this.getOption("moveTo", t); @@ -1425,7 +1408,6 @@ class FilesActionStore { const showInfo = this.getOption("showInfo", t); itemsCollection - .set("share", share) .set("download", download) .set("downloadAs", downloadAs) .set("moveTo", moveTo) @@ -1437,14 +1419,13 @@ class FilesActionStore { }; getRecentFolderOptions = (itemsCollection, t) => { - const share = this.getOption("share", t); const download = this.getOption("download", t); const downloadAs = this.getOption("downloadAs", t); const copy = this.getOption("copy", t); const showInfo = this.getOption("showInfo", t); itemsCollection - .set("share", share) + .set("download", download) .set("downloadAs", downloadAs) .set("copy", copy) @@ -1456,14 +1437,13 @@ class FilesActionStore { getShareFolderOptions = (itemsCollection, t) => { const { setDeleteDialogVisible, setUnsubscribe } = this.dialogsStore; - const share = this.getOption("share", t); const download = this.getOption("download", t); const downloadAs = this.getOption("downloadAs", t); const copy = this.getOption("copy", t); const showInfo = this.getOption("showInfo", t); itemsCollection - .set("share", share) + .set("download", download) .set("downloadAs", downloadAs) .set("copy", copy) @@ -1497,15 +1477,12 @@ class FilesActionStore { getFavoritesFolderOptions = (itemsCollection, t) => { const { selection } = this.filesStore; - - const share = this.getOption("share", t); const download = this.getOption("download", t); const downloadAs = this.getOption("downloadAs", t); const copy = this.getOption("copy", t); const showInfo = this.getOption("showInfo", t); itemsCollection - .set("share", share) .set("download", download) .set("downloadAs", downloadAs) .set("copy", copy) diff --git a/packages/client/src/store/FilesStore.js b/packages/client/src/store/FilesStore.js index 98e68a215c..0e82331ec4 100644 --- a/packages/client/src/store/FilesStore.js +++ b/packages/client/src/store/FilesStore.js @@ -88,6 +88,8 @@ class FilesStore { trashIsEmpty = false; filesIsLoading = false; + isEmptyPage = false; + constructor( authStore, selectedFolderStore, @@ -315,6 +317,10 @@ class FilesStore { this.startDrag = startDrag; }; + setIsEmptyPage = (isEmptyPage) => { + this.isEmptyPage = isEmptyPage; + }; + get tooltipOptions() { if (!this.dragging) return null; @@ -654,6 +660,9 @@ class FilesStore { const filterData = filter ? filter.clone() : FilesFilter.getDefault(); filterData.folder = folderId; + if (folderId === "@my" && this.authStore.userStore.user.isVisitor) + return this.fetchRooms(); + const filterStorageItem = this.authStore.userStore.user?.id && localStorage.getItem(`UserFilter=${this.authStore.userStore.user.id}`); @@ -1693,7 +1702,8 @@ class FilesStore { removeFiles = (fileIds, folderIds, showToast) => { const newFilter = this.filter.clone(); - const deleteCount = fileIds.length + folderIds.length; + const deleteCount = (fileIds?.length ?? 0) + (folderIds?.length ?? 0); + newFilter.startIndex = (newFilter.page + 1) * newFilter.pageCount - deleteCount; newFilter.pageCount = deleteCount; @@ -1703,10 +1713,10 @@ class FilesStore { .then((res) => { const files = fileIds ? this.files.filter((x) => !fileIds.includes(x.id)) - : []; + : this.files; const folders = folderIds ? this.folders.filter((x) => !folderIds.includes(x.id)) - : []; + : this.folders; const newFiles = [...files, ...res.files]; const newFolders = [...folders, ...res.folders]; @@ -2592,13 +2602,13 @@ class FilesStore { // const filterTotal = isRoom ? this.roomsFilterTotal : this.filterTotal; const filterTotal = isRooms ? this.roomsFilter.total : this.filter.total; - console.log("hasMoreFiles isRooms", isRooms); - console.log("hasMoreFiles filesList", this.filesList.length); - console.log("hasMoreFiles this.filterTotal", this.filterTotal); - console.log("hasMoreFiles this.roomsFilterTotal", this.roomsFilterTotal); - console.log("hasMoreFiles filterTotal", filterTotal); - console.log("hasMoreFiles", this.filesList.length < filterTotal); - console.log("----------------------------"); + // console.log("hasMoreFiles isRooms", isRooms); + // console.log("hasMoreFiles filesList", this.filesList.length); + // console.log("hasMoreFiles this.filterTotal", this.filterTotal); + // console.log("hasMoreFiles this.roomsFilterTotal", this.roomsFilterTotal); + // console.log("hasMoreFiles filterTotal", filterTotal); + // console.log("hasMoreFiles", this.filesList.length < filterTotal); + // console.log("----------------------------"); if (this.isLoading) return false; return this.filesList.length < filterTotal; diff --git a/packages/client/src/store/HotkeyStore.js b/packages/client/src/store/HotkeyStore.js index d225a22381..4f66a5649e 100644 --- a/packages/client/src/store/HotkeyStore.js +++ b/packages/client/src/store/HotkeyStore.js @@ -580,6 +580,10 @@ class HotkeyStore { // } } + if (nextForTileDown.isFolder === undefined) { + nextForTileDown.isFolder = !!nextForTileDown.parentId; + } + return nextForTileDown; } @@ -611,6 +615,11 @@ class HotkeyStore { } else if (!prevTileFile) { prevForTileUp = hotkeyCaret; } + + if (prevForTileUp.isFolder === undefined) { + prevForTileUp.isFolder = !!prevForTileUp.parentId; + } + return prevForTileUp; } diff --git a/packages/client/src/store/ProfileActionsStore.js b/packages/client/src/store/ProfileActionsStore.js index 7ee310b91d..f277ca82e4 100644 --- a/packages/client/src/store/ProfileActionsStore.js +++ b/packages/client/src/store/ProfileActionsStore.js @@ -60,8 +60,14 @@ class ProfileActionsStore { }; onProfileClick = () => { - this.selectedFolderStore.setSelectedFolder(null); - this.treeFoldersStore.setSelectedNode(["accounts"]); + //TODO: add check manager + const { isAdmin, isOwner } = this.authStore.userStore.user; + + if (isAdmin || isOwner) { + this.selectedFolderStore.setSelectedFolder(null); + this.treeFoldersStore.setSelectedNode(["accounts"]); + } + history.push(PROFILE_SELF_URL); }; @@ -129,7 +135,6 @@ class ProfileActionsStore { icon: "/static/images/catalog.settings.react.svg", label: t("Common:Settings"), onClick: () => this.onSettingsClick(settingsUrl), - url: settingsUrl, } : null; @@ -156,7 +161,6 @@ class ProfileActionsStore { icon: "/static/images/profile.react.svg", label: t("Common:Profile"), onClick: this.onProfileClick, - url: PROFILE_SELF_URL, }, settings, { @@ -164,33 +168,29 @@ class ProfileActionsStore { icon: "/static/images/payments.react.svg", label: t("Common:PaymentsTitle"), onClick: this.onPaymentsClick, - url: PAYMENTS_URL, }, { key: "HelpCenterBtn", icon: "/static/images/help.center.react.svg", label: t("Common:HelpCenter"), onClick: this.onHelpCenterClick, - url: HELP_URL, }, { key: "SupportBtn", icon: "/static/images/support.react.svg", label: t("Common:FeedbackAndSupport"), onClick: this.onSupportClick, - url: SUPPORT_URL, }, { key: "VideoBtn", icon: "/static/images/video.guides.react.svg", label: t("Common:VideoGuides"), onClick: this.onVideoGuidesClick, - url: VIDEO_GUIDES_URL, }, hotkeys, { key: "AboutBtn", - icon: "/static/images/info.react.svg", + icon: "/static/images/info.outline.react.svg", label: t("Common:AboutCompanyTitle"), onClick: this.onAboutClick, }, @@ -210,7 +210,7 @@ class ProfileActionsStore { if (debugInfo) { actions.splice(3, 0, { key: "DebugBtn", - icon: "/static/images/info.react.svg", + icon: "/static/images/info.outline.react.svg", label: "Debug Info", onClick: this.onDebugClick, }); diff --git a/packages/client/src/store/SelectFileDialogStore.js b/packages/client/src/store/SelectFileDialogStore.js index 55374d483e..acb6f76f8b 100644 --- a/packages/client/src/store/SelectFileDialogStore.js +++ b/packages/client/src/store/SelectFileDialogStore.js @@ -1,17 +1,11 @@ -import { makeObservable, action, observable } from "mobx"; +import { makeAutoObservable } from "mobx"; class SelectFileDialogStore { folderId = null; - fileInfo = null; + fileInfo = {}; constructor() { - makeObservable(this, { - fileInfo: observable, - folderId: observable, - - setFolderId: action, - setFile: action, - }); + makeAutoObservable(this); } setFolderId = (id) => { diff --git a/packages/client/src/store/SelectFolderDialogStore.js b/packages/client/src/store/SelectFolderDialogStore.js index cd8ef2c852..0bd2c2f967 100644 --- a/packages/client/src/store/SelectFolderDialogStore.js +++ b/packages/client/src/store/SelectFolderDialogStore.js @@ -1,27 +1,55 @@ -import { makeObservable, action, observable } from "mobx"; +import { makeAutoObservable } from "mobx"; class SelectFolderDialogStore { - folderId = null; + resultingFolderId = null; fileInfo = null; folderTitle = ""; providerKey = null; baseFolderPath = ""; + newFolderPath = ""; + isPathError = false; + isLoading = false; + resultingFolderTree = []; constructor() { - makeObservable(this, { - fileInfo: observable, - folderId: observable, - folderTitle: observable, - providerKey: observable, - - setFolderId: action, - setProviderKey: action, - setFolderTitle: action, - }); + makeAutoObservable(this); } - setFolderId = (id) => { - this.folderId = id; + toDefault = () => { + this.resultingFolderId = null; + this.resultingFolderTree = []; + this.baseFolderPath = ""; + this.newFolderPath = ""; + this.folderTitle = ""; + this.isLoading = false; + this.isPathError = false; + this.setProviderKey(null); + }; + + updateBaseFolderPath = () => { + this.baseFolderPath = this.newFolderPath; + this.setIsPathError(false); + }; + + resetNewFolderPath = (id) => { + this.newFolderPath = this.baseFolderPath; + this.setIsPathError(false); + this.setResultingFolderId(id); + }; + + setBaseFolderPath = (baseFolderPath) => { + this.baseFolderPath = baseFolderPath; + }; + + setIsPathError = (isPathError) => { + this.isPathError = isPathError; + }; + + setNewFolderPath = (newFolderPath) => { + this.newFolderPath = newFolderPath; + }; + setResultingFolderId = (id) => { + this.resultingFolderId = id; }; setFolderTitle = (title) => { @@ -31,6 +59,14 @@ class SelectFolderDialogStore { setProviderKey = (providerKey) => { this.providerKey = providerKey; }; + + setIsLoading = (isLoading) => { + this.isLoading = isLoading; + }; + + setResultingFoldersTree = (tree) => { + this.resultingFolderTree = tree; + }; } export default new SelectFolderDialogStore(); diff --git a/packages/client/src/store/UploadDataStore.js b/packages/client/src/store/UploadDataStore.js index 1262798826..991970536c 100644 --- a/packages/client/src/store/UploadDataStore.js +++ b/packages/client/src/store/UploadDataStore.js @@ -935,7 +935,7 @@ class UploadDataStore { withPaging && fetchFiles(toFolderId, filter); if (toFolderId) { - const { socketHelper } = this.filesStore.settingsStore; + const { socketHelper } = this.authStore.settingsStore; socketHelper.emit({ command: "refresh-folder", diff --git a/packages/common/components/Article/index.js b/packages/common/components/Article/index.js index b545dfaace..7a3ae9c4e7 100644 --- a/packages/common/components/Article/index.js +++ b/packages/common/components/Article/index.js @@ -2,7 +2,6 @@ import React from "react"; import { inject, observer } from "mobx-react"; import PropTypes from "prop-types"; import { isMobile, isMobileOnly } from "react-device-detect"; -import { Resizable } from "re-resizable"; import { isDesktop as isDesktopUtils, @@ -17,13 +16,7 @@ import SubArticleBody from "./sub-components/article-body"; import ArticleProfile from "./sub-components/article-profile"; import { StyledArticle } from "./styled-article"; - -const enable = { - top: false, - right: false, - bottom: false, - left: false, -}; +import HideArticleMenuButton from "./sub-components/article-hide-menu-button"; const Article = ({ showText, @@ -117,7 +110,7 @@ const Article = ({ isBannerVisible={isBannerVisible} {...rest} > - + {articleHeaderContent ? articleHeaderContent.props.children : null} {articleMainButtonContent && !isMobileOnly && !isMobileUtils() ? ( @@ -127,6 +120,10 @@ const Article = ({ ) : null} {articleBodyContent ? articleBodyContent.props.children : null} + {!hideProfileBlock && !isMobileOnly && ( )} diff --git a/packages/common/components/Article/styled-article.js b/packages/common/components/Article/styled-article.js index ae7a052265..6961142869 100644 --- a/packages/common/components/Article/styled-article.js +++ b/packages/common/components/Article/styled-article.js @@ -1,20 +1,13 @@ import styled, { css } from "styled-components"; -import { - isMobile, - isMobileOnly, - isTablet, - isDesktop, -} from "react-device-detect"; +import { isMobile, isMobileOnly, isTablet } from "react-device-detect"; import { mobile, tablet, isMobile as isMobileUtils, } from "@docspace/components/utils/device"; -import Heading from "@docspace/components/heading"; import { Base } from "@docspace/components/themes"; - import MenuIcon from "@docspace/components/public/static/images/menu.react.svg"; import CrossIcon from "@docspace/components/public/static/images/cross.react.svg"; @@ -84,7 +77,6 @@ const StyledArticle = styled.article` .scroll-body { overflow-x: hidden !important; height: calc(100% - 200px); - ${!isDesktop && "padding-top: 16px"}; padding: 0 20px; @media ${tablet} { @@ -297,6 +289,10 @@ const StyledArticleProfile = styled.div` padding: 16px 14px; `} + .profile-avatar { + cursor: pointer; + } + .option-button { margin-left: auto; height: 32px; diff --git a/packages/common/components/Article/sub-components/article-header.js b/packages/common/components/Article/sub-components/article-header.js index 75bbf84e55..4349b928a5 100644 --- a/packages/common/components/Article/sub-components/article-header.js +++ b/packages/common/components/Article/sub-components/article-header.js @@ -23,11 +23,7 @@ const ArticleHeader = ({ const history = useHistory(); const isTabletView = (isTabletUtils() || isTablet) && !isMobileOnly; - - const onLogoClick = () => { - if (showText && isTabletView) onClick(); - else history.push("/"); - }; + const onLogoClick = () => history.push("/"); if (isMobileOnly) return <>; return ( @@ -36,7 +32,7 @@ const ArticleHeader = ({ ) : ( - + )} diff --git a/packages/common/components/Article/sub-components/article-hide-menu-button.js b/packages/common/components/Article/sub-components/article-hide-menu-button.js new file mode 100644 index 0000000000..0fb4efc3cf --- /dev/null +++ b/packages/common/components/Article/sub-components/article-hide-menu-button.js @@ -0,0 +1,111 @@ +import React from "react"; +import styled, { css } from "styled-components"; +import Text from "@docspace/components/text"; +import { ReactSVG } from "react-svg"; +import { desktop, mobile, tablet } from "@docspace/components/utils/device"; +import { isTablet, isMobileOnly } from "react-device-detect"; +import { useTranslation } from "react-i18next"; + +const StyledHideArticleMenuButton = styled.div` + display: flex; + align-items: center; + position: fixed; + height: 44px; + z-index: 209; + bottom: 89px; + left: 0; + cursor: pointer; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + + min-width: ${({ showText }) => (showText ? "243px" : "60px")}; + max-width: ${({ showText }) => (showText ? "243px" : "60px")}; + + @media ${desktop} { + ${!isTablet && + css` + display: none; + `} + } + + @media ${mobile} { + display: none; + } + + ${isMobileOnly && + css` + display: none; + `} + + .article-hide-menu-container { + align-items: center; + margin-left: 16px; + + .article-hide-menu-text { + margin-left: 8px; + } + + @media ${tablet} { + display: ${({ showText }) => (showText ? "flex" : "none")}; + } + + ${isTablet && + css` + display: ${({ showText }) => (showText ? "flex" : "none")}; + `} + } + + .article-show-menu-container { + justify-content: center; + width: 100%; + + @media ${tablet} { + display: ${({ showText }) => (showText ? "none" : "flex")}; + } + + ${isTablet && + css` + display: ${({ showText }) => (showText ? "none" : "flex")}; + `} + } + + .article-hide-menu-icon_svg, + .article-show-menu-icon_svg { + height: 28px; + } +`; + +const HideArticleMenuButton = ({ showText, toggleShowText }) => { + const { t } = useTranslation("Common"); + + return ( + + {showText ? ( +
+ + + {t("HideArticleMenu")} + +
+ ) : ( +
+ +
+ )} +
+ ); +}; + +export default HideArticleMenuButton; diff --git a/packages/common/components/Article/sub-components/article-profile.js b/packages/common/components/Article/sub-components/article-profile.js index a8d29dc932..c0e6398cbc 100644 --- a/packages/common/components/Article/sub-components/article-profile.js +++ b/packages/common/components/Article/sub-components/article-profile.js @@ -15,7 +15,7 @@ import { } from "../styled-article"; const ArticleProfile = (props) => { - const { user, showText, getUserRole, getActions } = props; + const { user, showText, getUserRole, getActions, onProfileClick } = props; const { t } = useTranslation("Common"); const [isOpen, setIsOpen] = useState(false); const ref = useRef(null); @@ -31,7 +31,11 @@ const ArticleProfile = (props) => { }; const onAvatarClick = (e) => { - if (isTabletView && !showText) toggle(e, !isOpen); + if (isTabletView && !showText) { + toggle(e, !isOpen); + } else { + onProfileClick(); + } }; const onHide = () => { @@ -46,6 +50,7 @@ const ArticleProfile = (props) => {
{ export default withRouter( inject(({ auth, profileActionsStore }) => { - const { getActions, getUserRole } = profileActionsStore; + const { getActions, getUserRole, onProfileClick } = profileActionsStore; return { + onProfileClick, user: auth.userStore.user, getUserRole, getActions, diff --git a/packages/common/components/FilterInput/StyledFilterInput.js b/packages/common/components/FilterInput/StyledFilterInput.js index c01f46a81b..9684102fb5 100644 --- a/packages/common/components/FilterInput/StyledFilterInput.js +++ b/packages/common/components/FilterInput/StyledFilterInput.js @@ -1,7 +1,4 @@ import styled, { css } from "styled-components"; - -import { isDesktop } from "react-device-detect"; - import SearchInput from "@docspace/components/search-input"; const StyledFilterInput = styled.div` diff --git a/packages/common/components/FloatingButton/StyledFloatingButton.js b/packages/common/components/FloatingButton/StyledFloatingButton.js index 23c9a97bb8..8624293985 100644 --- a/packages/common/components/FloatingButton/StyledFloatingButton.js +++ b/packages/common/components/FloatingButton/StyledFloatingButton.js @@ -1,7 +1,7 @@ import Base from "@docspace/components/themes/base"; import styled, { keyframes, css } from "styled-components"; import { desktop, tablet } from "@docspace/components/utils/device"; -import { isDesktop } from "react-device-detect"; +import { isMobile } from "react-device-detect"; const StyledFloatingButtonWrapper = styled.div` @media ${desktop} { @@ -10,7 +10,7 @@ const StyledFloatingButtonWrapper = styled.div` right: 0; bottom: 0; - ${isDesktop && + ${!isMobile && css` width: 100px; height: 70px; diff --git a/packages/common/components/Navigation/Navigation.js b/packages/common/components/Navigation/Navigation.js index 3e290828d2..f924d4b93d 100644 --- a/packages/common/components/Navigation/Navigation.js +++ b/packages/common/components/Navigation/Navigation.js @@ -43,6 +43,7 @@ const Navigation = ({ titles, withMenu, onPlusClick, + isEmptyPage, ...rest }) => { const [isOpen, setIsOpen] = React.useState(false); @@ -57,7 +58,7 @@ const Navigation = ({ (!isMobile && !isTabletUtils() && !isMobileUtils()) || (isDesktopUtils() && !isMobile); - const infoPanelIsVisible = isDesktop && !isEmptyFilesList; + const infoPanelIsVisible = isDesktop && !isEmptyPage; const onMissClick = React.useCallback( (e) => { @@ -219,6 +220,7 @@ Navigation.propTypes = { getContextOptionsFolder: PropTypes.func, onBackToParentFolder: PropTypes.func, titles: PropTypes.object, + isEmptyPage: PropTypes.bool, }; export default React.memo(Navigation); diff --git a/packages/common/components/PrivateRoute/index.js b/packages/common/components/PrivateRoute/index.js index 2faade1d4a..54c91fd377 100644 --- a/packages/common/components/PrivateRoute/index.js +++ b/packages/common/components/PrivateRoute/index.js @@ -28,11 +28,13 @@ const PrivateRoute = ({ component: Component, ...rest }) => { location, tenantStatus, } = rest; - const isPortal = window.location.pathname === "/preparation-portal"; + const { params, path } = computedMatch; const { userId } = params; const renderComponent = (props) => { + const isPortalUrl = props.location.pathname === "/preparation-portal"; + if (isLoaded && !isAuthenticated) { if (personal) { window.location.replace("/"); @@ -70,7 +72,7 @@ const PrivateRoute = ({ component: Component, ...rest }) => { isLoaded && isAuthenticated && tenantStatus === TenantStatus.PortalRestore && - !isPortal + !isPortalUrl ) { return ( { ); } + if (tenantStatus !== TenantStatus.PortalRestore && isPortalUrl) { + return window.location.replace("/"); + } + if (!isLoaded) { return ; } diff --git a/packages/common/components/Section/sub-components/section-body.js b/packages/common/components/Section/sub-components/section-body.js index 3974ab43b0..6d476c516b 100644 --- a/packages/common/components/Section/sub-components/section-body.js +++ b/packages/common/components/Section/sub-components/section-body.js @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import styled, { css } from "styled-components"; //import equal from "fast-deep-equal/react"; //import { LayoutContextConsumer } from "client/Layout/context"; -import { isMobile, isMobileOnly, isDesktop } from "react-device-detect"; +import { isMobile, isMobileOnly } from "react-device-detect"; import { inject, observer } from "mobx-react"; import Scrollbar from "@docspace/components/scrollbar"; @@ -110,7 +110,7 @@ const commonStyles = css` .files-row-container { margin-top: -22px; - ${isDesktop && + ${!isMobile && css` margin-top: -17px; `} diff --git a/packages/components/avatar/styled-avatar.js b/packages/components/avatar/styled-avatar.js index 37c3d9b1de..8d1b1696a6 100644 --- a/packages/components/avatar/styled-avatar.js +++ b/packages/components/avatar/styled-avatar.js @@ -139,6 +139,8 @@ const StyledAvatar = styled.div` font-family: ${(props) => props.theme.fontFamily}; font-style: normal; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + .admin_icon { rect:nth-child(1) { fill: ${(props) => props.theme.avatar.administrator.fill}; diff --git a/packages/components/catalog-item/styled-catalog-item.js b/packages/components/catalog-item/styled-catalog-item.js index 297a8267f3..05f2a7583e 100644 --- a/packages/components/catalog-item/styled-catalog-item.js +++ b/packages/components/catalog-item/styled-catalog-item.js @@ -34,7 +34,7 @@ const StyledCatalogItemHeaderContainer = styled.div` height: 24px; - padding: 8px 12px 4px; + padding: 7px 12px 4px; box-sizing: border-box; @@ -44,7 +44,7 @@ const StyledCatalogItemHeaderContainer = styled.div` font-style: normal; font-weight: 600; font-size: 11px; - line-height: 12px; + line-height: 14px; color: #a3a9ae; } diff --git a/packages/components/drop-down-item/styled-drop-down-item.js b/packages/components/drop-down-item/styled-drop-down-item.js index d42c7add4e..f4414189aa 100644 --- a/packages/components/drop-down-item/styled-drop-down-item.js +++ b/packages/components/drop-down-item/styled-drop-down-item.js @@ -56,6 +56,20 @@ const StyledDropdownItem = styled.div` ? props.theme.dropDownItem.icon.disableColor : props.theme.dropDownItem.icon.color}; } + + circle[fill] { + fill: ${(props) => + props.disabled + ? props.theme.dropDownItem.icon.disableColor + : props.theme.dropDownItem.icon.color}; + } + + rect[fill] { + fill: ${(props) => + props.disabled + ? props.theme.dropDownItem.icon.disableColor + : props.theme.dropDownItem.icon.color}; + } } } diff --git a/packages/components/empty-screen-container/styled-empty-screen-container.js b/packages/components/empty-screen-container/styled-empty-screen-container.js index a429944e8b..dce2f94ced 100644 --- a/packages/components/empty-screen-container/styled-empty-screen-container.js +++ b/packages/components/empty-screen-container/styled-empty-screen-container.js @@ -11,19 +11,20 @@ const EmptyPageStyles = css` .ec-desc { max-width: 348px; + line-height: 16px; + margin-top: 0; } .ec-header { font-size: 16px; } - .ec-desc { - line-height: 16px; - margin-top: 0; + .ec-buttons { + max-width: 285px; } .empty-folder_container-links { - align-items: center; + align-items: start; margin: 16px 0 !important; } @@ -37,7 +38,7 @@ const EmptyPageStyles = css` } .ec-desc { - max-width: 287px; + max-width: 282px; } } @@ -45,6 +46,10 @@ const EmptyPageStyles = css` .ec-desc { max-width: 618px; } + + .ec-buttons { + max-width: none; + } } `; @@ -67,6 +72,7 @@ const EmptyContentBody = styled.div` grid-column-gap: 16px; grid-row-gap: 10px; max-width: 800px; + grid-template-rows: max-content; .ec-image { grid-area: img; @@ -74,6 +80,10 @@ const EmptyContentBody = styled.div` ${NoUserSelect} } + @media ${tablet} { + max-width: 480px; + } + .ec-header { grid-area: headerText; padding-top: 16px; @@ -99,7 +109,6 @@ const EmptyContentBody = styled.div` @media (orientation: portrait) { @media (max-width: 768px) { padding-top: 0px; - max-width: 700px; .ec-image { max-height: 100px; diff --git a/packages/components/infinite-loader/StyledInfiniteLoader.js b/packages/components/infinite-loader/StyledInfiniteLoader.js index b93f40f77a..c6fbb67a62 100644 --- a/packages/components/infinite-loader/StyledInfiniteLoader.js +++ b/packages/components/infinite-loader/StyledInfiniteLoader.js @@ -1,7 +1,8 @@ import { List } from "react-virtualized"; import styled, { css } from "styled-components"; import Base from "../themes/base"; -import { desktop, mobile, tablet } from "../utils/device"; +import { mobile, tablet } from "../utils/device"; +import { isMobile } from "react-device-detect"; const StyledScroll = styled.div` overflow: scroll; @@ -31,23 +32,39 @@ const StyledScroll = styled.div` const rowStyles = css` margin-left: -20px; - width: ${({ width }) => width + 20 + "px !important"}; + width: ${({ width }) => width + (isMobile ? 36 : 40) + "px !important"}; .ReactVirtualized__Grid__innerScrollContainer { - max-width: ${({ width }) => width + 20 + "px !important"}; + max-width: ${({ width }) => width + (isMobile ? 36 : 40) + "px !important"}; + } + + @media ${tablet} { + width: ${({ width }) => width + 36 + "px !important"}; + + .ReactVirtualized__Grid__innerScrollContainer { + max-width: ${({ width }) => width + 36 + "px !important"}; + } + } + + @media ${mobile} { + width: ${({ width }) => width + 28 + "px !important"}; + + .ReactVirtualized__Grid__innerScrollContainer { + max-width: ${({ width }) => width + 28 + "px !important"}; + } } .row-list-item { padding-left: 16px; - width: calc(100% - 16px) !important; + width: calc(100% - 32px) !important; @media ${tablet} { padding-left: 20px; - width: calc(100% - 20px) !important; + width: calc(100% - 36px) !important; } @media ${mobile} { - width: calc(100% - 20px) !important; + width: calc(100% - 28px) !important; } } `; diff --git a/packages/components/infinite-loader/infiniteLoaderUtils.js b/packages/components/infinite-loader/infiniteLoaderUtils.js index 64468785ba..4398ecfe91 100644 --- a/packages/components/infinite-loader/infiniteLoaderUtils.js +++ b/packages/components/infinite-loader/infiniteLoaderUtils.js @@ -2,7 +2,7 @@ let timer = null; const startInterval = () => { const elem = document.getElementById("infinite-page-loader"); - elem.style.display = "block"; + if (elem) elem.style.display = "block"; }; export function showLoader() { diff --git a/packages/components/row-content/styled-row-content.js b/packages/components/row-content/styled-row-content.js index 86adc444bb..af62f9682f 100644 --- a/packages/components/row-content/styled-row-content.js +++ b/packages/components/row-content/styled-row-content.js @@ -79,7 +79,9 @@ const MainContainerWrapper = styled.div` props.widthProp && props.widthProp < size.tablet) || props.isMobile - ? `${mainWrapperTabletStyle}` + ? css` + ${mainWrapperTabletStyle} + ` : ` `} @media ${tablet} { diff --git a/packages/components/row/styled-row.js b/packages/components/row/styled-row.js index 17dd659d62..d26709c10f 100644 --- a/packages/components/row/styled-row.js +++ b/packages/components/row/styled-row.js @@ -77,6 +77,8 @@ const StyledCheckbox = styled.div` justify-content: center; align-items: center; + min-width: 41px; + width: 41px; ${(props) => props.mode == "modern" && !isMobile && diff --git a/packages/components/themes/dark.js b/packages/components/themes/dark.js index 9a3114c0b0..2e1407edbb 100644 --- a/packages/components/themes/dark.js +++ b/packages/components/themes/dark.js @@ -1614,10 +1614,11 @@ const Dark = { }, toggleButton: { - fillColor: grayMaxLight, + fillColor: "#F0F0F0", fillColorOff: "#292929", disableFillColor: black, + disableFillColorOff: "#545454", borderColor: "#474747", borderColorOff: "#474747", @@ -1625,11 +1626,11 @@ const Dark = { disableBorderColor: "#474747", disableBorderColorOff: "#646464", - fillCircleColor: "#FFFFFF", + fillCircleColor: "#292929", fillCircleColorOff: grayMaxLight, - disableFillCircleColor: "#333333", - disableFillCircleColorOff: "#797979", + disableFillCircleColor: "#545454", + disableFillCircleColorOff: black, }, contextMenuButton: { diff --git a/packages/components/toggle-button/index.js b/packages/components/toggle-button/index.js index 1e874a33ca..74b4534e96 100644 --- a/packages/components/toggle-button/index.js +++ b/packages/components/toggle-button/index.js @@ -1,15 +1,8 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; -import { - ToggleButtonContainer, - HiddenInput, - Container, -} from "./styled-toggle-button"; +import { ToggleButtonContainer, HiddenInput } from "./styled-toggle-button"; import Text from "../text"; -import globalColors from "../utils/globalColors"; import { motion } from "framer-motion"; -import Base from "../themes/base"; - import { ColorTheme, ThemeType } from "@docspace/common/components/ColorTheme"; const ToggleIcon = ({ isChecked, isLoading }) => { @@ -72,16 +65,12 @@ class ToggleButton extends Component { className, style, isLoading, - theme, } = this.props; - const { gray } = globalColors; - const colorProps = isDisabled ? { color: gray } : {}; //console.log("ToggleButton render"); return ( {label && ( - + {label} )} diff --git a/packages/components/toggle-button/styled-toggle-button.js b/packages/components/toggle-button/styled-toggle-button.js index 06263646d8..31bebee6e0 100644 --- a/packages/components/toggle-button/styled-toggle-button.js +++ b/packages/components/toggle-button/styled-toggle-button.js @@ -36,9 +36,12 @@ const ToggleButtonContainer = styled.label` rect { fill: ${props.isChecked ? props.theme.toggleButton.disableFillColor - : props.theme.toggleButton.fillColorOff}; + : props.theme.toggleButton.disableFillColorOff}; stroke-width: 1px; stroke-linecap: round; + stroke: ${props.isChecked + ? props.theme.toggleButton.borderColor + : props.theme.toggleButton.borderColorOff}; } circle { fill: ${props.isChecked @@ -52,6 +55,9 @@ const ToggleButtonContainer = styled.label` ? props.theme.toggleButton.fillColor : props.theme.toggleButton.fillColorOff}; stroke-width: 1px; + stroke: ${props.isChecked + ? props.theme.toggleButton.borderColor + : props.theme.toggleButton.borderColor}; } circle { fill: ${props.isChecked diff --git a/packages/components/tree-menu/index.js b/packages/components/tree-menu/index.js index 862d6d508f..a018cceb55 100644 --- a/packages/components/tree-menu/index.js +++ b/packages/components/tree-menu/index.js @@ -8,6 +8,11 @@ import Badge from "../badge"; import Base from "../themes/base"; const StyledTree = styled(Tree)` + span.rc-tree-node-content-wrapper, + span.rc-tree-switcher { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + } + .rc-tree-list-holder-inner { .disable-node { span.rc-tree-node-content-wrapper { @@ -33,6 +38,22 @@ const StyledTree = styled(Tree)` } } + .disable-folder { + span.rc-tree-node-content-wrapper { + pointer-events: none; + span.rc-tree-iconEle { + svg { + path { + fill: ${(props) => props.theme.treeNode.disableColor}; + } + } + } + span.rc-tree-title { + color: ${(props) => props.theme.treeNode.disableColor} !important; + } + } + } + .rc-tree-treenode { height: 36px; display: flex; diff --git a/packages/editor/src/client/components/SelectFileDialog.js b/packages/editor/src/client/components/SelectFileDialog.js index 19043d2317..49824d6803 100644 --- a/packages/editor/src/client/components/SelectFileDialog.js +++ b/packages/editor/src/client/components/SelectFileDialog.js @@ -22,7 +22,7 @@ const SelectFileDialog = ({ module: "./SelectFileDialog", }} resetTreeFolders - foldersType="exceptPrivacyTrashFolders" + filteredType="exceptPrivacyTrashArchiveFolders" isPanelVisible={isVisible} onClose={onCloseFileDialog} onSelectFile={onSelectFile} diff --git a/packages/editor/src/client/components/SelectFolderDialog.js b/packages/editor/src/client/components/SelectFolderDialog.js index 82dab51862..51f8a0c74c 100644 --- a/packages/editor/src/client/components/SelectFolderDialog.js +++ b/packages/editor/src/client/components/SelectFolderDialog.js @@ -63,7 +63,7 @@ const SelectFolderDialog = ({ folderId={folderId} isPanelVisible={isVisible} onClose={onCloseFolderDialog} - foldersType="exceptSortedByTags" + filteredType="exceptSortedByTags" onSave={onClickSaveSelectFolder} isDisableButton={!titleSelectorFolder.trim()} {...headerProps} diff --git a/products/ASC.Files/Core/Core/Dao/TeamlabDao/FileDao.cs b/products/ASC.Files/Core/Core/Dao/TeamlabDao/FileDao.cs index 320b0f1ebb..fa629cabac 100644 --- a/products/ASC.Files/Core/Core/Dao/TeamlabDao/FileDao.cs +++ b/products/ASC.Files/Core/Core/Dao/TeamlabDao/FileDao.cs @@ -416,12 +416,12 @@ internal class FileDao : AbstractDao, IFileDao } } - var user = _userManager.GetUsers(file.Id == default ? _authContext.CurrentAccount.ID : file.CreateBy); var quotaSettings = _settingsManager.Load(); - var userQuotaSettings = _settingsManager.LoadForUser(user); - if (quotaSettings.EnableUserQuota && userQuotaSettings.UserQuota != -1) + if (quotaSettings.EnableUserQuota) { + var user = _userManager.GetUsers(file.Id == default ? _authContext.CurrentAccount.ID : file.CreateBy); + var userQuotaSettings = _settingsManager.LoadForUser(user); var quotaLimit = userQuotaSettings.UserQuota; if (quotaLimit != -1) diff --git a/public/images/article-hide-menu.react.svg b/public/images/article-hide-menu.react.svg new file mode 100644 index 0000000000..de75c0ce87 --- /dev/null +++ b/public/images/article-hide-menu.react.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/article-show-menu.react.svg b/public/images/article-show-menu.react.svg new file mode 100644 index 0000000000..dbc3cab5dd --- /dev/null +++ b/public/images/article-show-menu.react.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/locales/en/Common.json b/public/locales/en/Common.json index 0de9e76bf4..ec6b1243fc 100644 --- a/public/locales/en/Common.json +++ b/public/locales/en/Common.json @@ -196,5 +196,6 @@ "HelpCenter": "Help center", "FeedbackAndSupport": "Feedback & Support", "VideoGuides": "Video Guides", - "SelectFile": "Select file" + "SelectFile": "Select file", + "HideArticleMenu": "Hide menu" } diff --git a/public/locales/ru/Common.json b/public/locales/ru/Common.json index 30ce6268f5..b456920ac8 100644 --- a/public/locales/ru/Common.json +++ b/public/locales/ru/Common.json @@ -179,5 +179,6 @@ "AboutCompanyTitle": "О программе", "LogoutButton": "Выйти", "HelpCenter": "Справочный центр", - "SelectFile": "Выбрать файл" + "SelectFile": "Выбрать файл", + "HideArticleMenu": "Скрыть меню" }