Merge branch 'feature/files' of https://github.com/ONLYOFFICE/AppServer into feature/files
This commit is contained in:
commit
1337df1101
@ -52,10 +52,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppLimit.CloudComputing.Sha
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Files.Service", "products\ASC.Files\Service\ASC.Files.Service.csproj", "{5D41FFFF-816C-40B2-95CD-E2DDDCB83784}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASC.ApiSystem", "common\services\ASC.ApiSystem\ASC.ApiSystem.csproj", "{C2BB03A0-C35B-433F-96D4-3A06CBC06AD7}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.ApiSystem", "common\services\ASC.ApiSystem\ASC.ApiSystem.csproj", "{C2BB03A0-C35B-433F-96D4-3A06CBC06AD7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.UrlShortener.Svc", "common\services\ASC.UrlShortener.Svc\ASC.UrlShortener.Svc.csproj", "{04A56018-C41E-4634-A185-A13E9250C75A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASC.Feed.Aggregator", "common\services\ASC.Feed.Aggregator\ASC.Feed.Aggregator.csproj", "{07CCC11F-76CB-448E-B15A-72E09FBB348B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -162,6 +164,10 @@ Global
|
||||
{04A56018-C41E-4634-A185-A13E9250C75A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04A56018-C41E-4634-A185-A13E9250C75A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04A56018-C41E-4634-A185-A13E9250C75A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{07CCC11F-76CB-448E-B15A-72E09FBB348B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{07CCC11F-76CB-448E-B15A-72E09FBB348B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{07CCC11F-76CB-448E-B15A-72E09FBB348B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{07CCC11F-76CB-448E-B15A-72E09FBB348B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -5,6 +5,7 @@ ARG RELEASE_DATE_SIGN=""
|
||||
ARG VERSION="8.9.0.190"
|
||||
ARG SOURCE_REPO_URL="deb http://static.teamlab.com.s3.amazonaws.com/repo/debian squeeze main"
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG GIT_BRANCH="master"
|
||||
|
||||
LABEL onlyoffice.community.release-date="${RELEASE_DATE}" \
|
||||
onlyoffice.community.version="${VERSION}" \
|
||||
@ -15,6 +16,8 @@ ENV LANG=en_US.UTF-8 \
|
||||
LC_ALL=en_US.UTF-8
|
||||
|
||||
RUN apt-get -y update && \
|
||||
apt-get -y upgrade && \
|
||||
apt-get -y dist-upgrade && \
|
||||
apt-get -yq install gnupg2 ca-certificates && \
|
||||
apt-get install -yq sudo locales && \
|
||||
addgroup --system --gid 107 onlyoffice && \
|
||||
@ -24,6 +27,12 @@ RUN apt-get -y update && \
|
||||
locale-gen en_US.UTF-8 && \
|
||||
apt-get -y update && \
|
||||
apt-get install -yq software-properties-common wget curl cron rsyslog && \
|
||||
curl -OL http://dev.mysql.com/get/mysql-apt-config_0.8.15-1_all.deb && \
|
||||
echo "mysql-apt-config mysql-apt-config/repo-codename select bionic" | sudo debconf-set-selections && \
|
||||
echo "mysql-apt-config mysql-apt-config/repo-distro select ubuntu" | sudo debconf-set-selections && \
|
||||
echo "mysql-apt-config mysql-apt-config/select-server select mysql-8.0" | sudo debconf-set-selections && \
|
||||
DEBIAN_FRONTEND=noninteractive dpkg -i mysql-apt-config_0.8.15-1_all.deb && \
|
||||
rm -f mysql-apt-config_0.8.15-1_all.deb && \
|
||||
wget http://nginx.org/keys/nginx_signing.key && \
|
||||
apt-key add nginx_signing.key && \
|
||||
echo "deb http://nginx.org/packages/mainline/ubuntu/ bionic nginx" >> /etc/apt/sources.list.d/nginx.list && \
|
||||
@ -33,9 +42,9 @@ RUN apt-get -y update && \
|
||||
apt-get install -yq apt-transport-https && \
|
||||
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
|
||||
echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official.list && \
|
||||
echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-6.x.list && \
|
||||
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-7.x.list && \
|
||||
apt-get -y update && \
|
||||
apt-get install -yq elasticsearch=6.5.0 && \
|
||||
apt-get install -yq elasticsearch=7.4.0 && \
|
||||
add-apt-repository -y ppa:certbot/certbot && \
|
||||
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - && \
|
||||
apt-get install -y nodejs && \
|
||||
@ -46,9 +55,9 @@ RUN apt-get -y update && \
|
||||
apt-get -y update && \
|
||||
apt-get install -yq nginx && \
|
||||
cd ~ && \
|
||||
wget http://www-us.apache.org/dist/kafka/2.2.1/kafka_2.12-2.2.1.tgz && \
|
||||
tar xzf kafka_2.12-2.2.1.tgz && \
|
||||
rm kafka_2.12-2.2.1.tgz && \
|
||||
wget https://downloads.apache.org/kafka/2.5.0/kafka_2.12-2.5.0.tgz && \
|
||||
tar xzf kafka_2.12-2.5.0.tgz && \
|
||||
rm kafka_2.12-2.5.0.tgz && \
|
||||
echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d && \
|
||||
apt-get install -yq libgdiplus \
|
||||
python-certbot-nginx \
|
||||
@ -67,19 +76,20 @@ RUN apt-get -y update && \
|
||||
mysql-client \
|
||||
mysql-server
|
||||
|
||||
RUN git clone https://github.com/ONLYOFFICE/AppServer.git /app/onlyoffice/src/
|
||||
RUN git clone https://github.com/ONLYOFFICE/AppServer.git /app/onlyoffice/src/ && \
|
||||
cd /app/onlyoffice/src/ && \
|
||||
git checkout ${GIT_BRANCH} && \
|
||||
git pull
|
||||
|
||||
RUN cd /app/onlyoffice/src/ && \
|
||||
yarn install --cwd web/ASC.Web.Components --frozen-lockfile > build/ASC.Web.Components.log && \
|
||||
npm run build --prefix web/ASC.Web.Components && \
|
||||
yarn pack --cwd web/ASC.Web.Components
|
||||
|
||||
RUN cd /app/onlyoffice/src/ && \
|
||||
component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) && \
|
||||
yarn remove asc-web-components --cwd web/ASC.Web.Common --peer && \
|
||||
yarn remove asc-web-components --cwd web/ASC.Web.Common --peer && \
|
||||
yarn add file:../../$component --cwd web/ASC.Web.Common --cache-folder ../../yarn --peer && \
|
||||
yarn install --cwd web/ASC.Web.Common --frozen-lockfile > build/ASC.Web.Common.log && \
|
||||
npm run build --prefix web/ASC.Web.Common && \
|
||||
yarn pack --cwd web/ASC.Web.Common
|
||||
|
||||
RUN cd /app/onlyoffice/src/ && \
|
||||
@ -89,10 +99,10 @@ RUN cd /app/onlyoffice/src/ && \
|
||||
|
||||
RUN cd /app/onlyoffice/src/ && \
|
||||
component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) && \
|
||||
common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) && \
|
||||
yarn remove asc-web-components asc-web-common --cwd web/ASC.Web.Client && \
|
||||
common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) && \
|
||||
yarn remove asc-web-components asc-web-common --cwd web/ASC.Web.Client && \
|
||||
yarn add ../../$component --cwd web/ASC.Web.Client --cache-folder ../../yarn && \
|
||||
yarn add ../../$common --cwd web/ASC.Web.Client --cache-folder ../../yarn && \
|
||||
yarn add ../../$common --cwd web/ASC.Web.Client --cache-folder ../../yarn && \
|
||||
yarn install --cwd web/ASC.Web.Client --frozen-lockfile || (cd web/ASC.Web.Client && npm i && cd ../../) && \
|
||||
npm run build --prefix web/ASC.Web.Client && \
|
||||
rm -rf /var/www/studio/client/* && \
|
||||
@ -101,10 +111,22 @@ RUN cd /app/onlyoffice/src/ && \
|
||||
|
||||
RUN cd /app/onlyoffice/src/ && \
|
||||
component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) && \
|
||||
common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) && \
|
||||
yarn remove asc-web-components asc-web-common --cwd products/ASC.People/Client && \
|
||||
common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) && \
|
||||
yarn remove asc-web-components asc-web-common --cwd products/ASC.Files/Client && \
|
||||
yarn add ../../../$component --cwd products/ASC.Files/Client --cache-folder ../../../yarn && \
|
||||
yarn add ../../../$common --cwd products/ASC.Files/Client --cache-folder ../../../yarn && \
|
||||
yarn install --cwd products/ASC.Files/Client --frozen-lockfile || (cd products/ASC.Files/Client && npm i && cd ../../../) && \
|
||||
npm run build --prefix products/ASC.Files/Client && \
|
||||
mkdir -p /var/www/products/ASC.Files/client && \
|
||||
cp -Rf products/ASC.Files/Client/build/* /var/www/products/ASC.Files/client && \
|
||||
mkdir -p /var/www/products/ASC.Files/client/products/files
|
||||
|
||||
RUN cd /app/onlyoffice/src/ && \
|
||||
component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) && \
|
||||
common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) && \
|
||||
yarn remove asc-web-components asc-web-common --cwd products/ASC.People/Client && \
|
||||
yarn add ../../../$component --cwd products/ASC.People/Client --cache-folder ../../../yarn && \
|
||||
yarn add ../../../$common --cwd products/ASC.People/Client --cache-folder ../../../yarn && \
|
||||
yarn add ../../../$common --cwd products/ASC.People/Client --cache-folder ../../../yarn && \
|
||||
yarn install --cwd products/ASC.People/Client --frozen-lockfile || (cd products/ASC.People/Client && npm i && cd ../../../) && \
|
||||
npm run build --prefix products/ASC.People/Client && \
|
||||
mkdir -p /var/www/products/ASC.People/client && \
|
||||
@ -113,12 +135,21 @@ RUN cd /app/onlyoffice/src/ && \
|
||||
|
||||
RUN cd /app/onlyoffice/src/ && \
|
||||
rm -f /etc/nginx/conf.d/* && \
|
||||
cp -rf config/nginx/onlyoffice*.conf /etc/nginx/conf.d/ && \
|
||||
mkdir -p /var/www/public/ && cp -f public/* /var/www/public/ && \
|
||||
mkdir -p /app/onlyoffice/config/ && cp -rf config/* /app/onlyoffice/config/ && \
|
||||
cp -f config/nginx/onlyoffice*.conf /etc/nginx/conf.d/ && \
|
||||
mkdir -p /etc/nginx/includes/ && cp -f config/nginx/includes/onlyoffice*.conf /etc/nginx/includes/ && \
|
||||
sed -e 's/#//' -i /etc/nginx/conf.d/onlyoffice.conf && \
|
||||
cd products/ASC.People/Server && \
|
||||
dotnet -d publish -o /var/www/products/ASC.People/server && \
|
||||
cd ../../../ && \
|
||||
cd products/ASC.Files/Server && \
|
||||
dotnet -d publish -o /var/www/products/ASC.Files/server && \
|
||||
cp -avrf DocStore /var/www/products/ASC.Files/server/ && \
|
||||
cd ../../../ && \
|
||||
cd products/ASC.Files/Service && \
|
||||
dotnet -d publish -o /var/www/products/ASC.Files/service && \
|
||||
cd ../../../ && \
|
||||
cd web/ASC.Web.Api && \
|
||||
dotnet -d publish -o /var/www/studio/api && \
|
||||
cd ../../ && \
|
||||
@ -128,25 +159,34 @@ RUN cd /app/onlyoffice/src/ && \
|
||||
cd common/services/ASC.Notify && \
|
||||
dotnet -d publish -o /var/www/services/notify && \
|
||||
cd ../../../ && \
|
||||
cd common/services/ASC.ApiSystem && \
|
||||
dotnet -d publish -o /var/www/services/apisystem && \
|
||||
cd ../../../ && \
|
||||
cd common/services/ASC.UrlShortener.Svc && \
|
||||
dotnet -d publish -o /services/urlshortener/service && \
|
||||
cd ../../../ && \
|
||||
yarn install --cwd common/ASC.UrlShortener --frozen-lockfile && \
|
||||
mkdir -p /var/www/services/urlshortener/client && cp -Rf common/ASC.UrlShortener/* /var/www/services/urlshortener/client && \
|
||||
cd common/services/ASC.Studio.Notify && \
|
||||
dotnet -d publish -o /var/www/services/studio.notify
|
||||
|
||||
COPY config/mysql/conf.d/mysql.cnf /etc/mysql/conf.d/mysql.cnf
|
||||
COPY config/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY config/mysql/dotnet_dump.sql /app/onlyoffice/dotnet_dump.sql
|
||||
|
||||
RUN sed -i 's/172.18.0.5/localhost/' /app/onlyoffice/config/appsettings.test.json
|
||||
RUN sed -i 's/Server=.*;Port=/Server=127.0.0.1;Port=/' /app/onlyoffice/config/appsettings.test.json
|
||||
|
||||
RUN mkdir -p /var/mysqld/ && \
|
||||
chown -R mysql:mysql /var/lib/mysql /var/run/mysqld /var/mysqld/ && \
|
||||
service mysql start && \
|
||||
sudo -u mysql bash -c "/usr/bin/pidproxy /var/mysqld/mysqld.pid /usr/bin/mysqld_safe --pid-file=/var/mysqld/mysqld.pid &" && \
|
||||
sleep 5s && \
|
||||
mysql -e "CREATE DATABASE IF NOT EXISTS onlyoffice CHARACTER SET utf8 COLLATE 'utf8_general_ci'" && \
|
||||
mysql -D "onlyoffice" < /app/onlyoffice/src/sql/app.sql && \
|
||||
mysql -D "onlyoffice" < /app/onlyoffice/src/sql/app.data.sql && \
|
||||
mysql -D "onlyoffice" < /app/onlyoffice/dotnet_dump.sql && \
|
||||
mysql -D "onlyoffice" -e 'CREATE USER IF NOT EXISTS "onlyoffice_user"@"localhost" IDENTIFIED WITH mysql_native_password BY "onlyoffice_pass";' && \
|
||||
mysql -D "onlyoffice" -e 'GRANT ALL PRIVILEGES ON *.* TO 'onlyoffice_user'@'localhost' IDENTIFIED BY "onlyoffice_pass";' && \
|
||||
mysql -D "onlyoffice" -e 'GRANT ALL PRIVILEGES ON *.* TO 'onlyoffice_user'@'localhost';' && \
|
||||
mysql -D "onlyoffice" -e 'UPDATE core_user SET email = "paul.bannov@gmail.com";' && \
|
||||
mysql -D "onlyoffice" -e 'UPDATE core_usersecurity SET pwdhash = "vLFfghR5tNV3K9DKhmwArV+SbjWAcgZZzIDTnJ0JgCo=", pwdhashsha512 = "USubvPlB+ogq0Q1trcSupg==";' && \
|
||||
service mysql stop
|
||||
killall -u mysql -n mysql
|
||||
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
262027
build/install/docker/config/mysql/dotnet_dump.sql
Normal file
262027
build/install/docker/config/mysql/dotnet_dump.sql
Normal file
File diff suppressed because one or more lines are too long
@ -9,46 +9,80 @@ environment=PATH=/usr/local/sbin:/usr/bin:/bin:/usr/local/bin
|
||||
user=mysql
|
||||
|
||||
[program:kafka]
|
||||
directory=/root/kafka_2.12-2.2.1/
|
||||
command=/root/kafka_2.12-2.2.1/bin/kafka-server-start.sh /root/kafka_2.12-2.2.1/config/server.properties
|
||||
directory=/root/kafka_2.12-2.5.0/
|
||||
command=/root/kafka_2.12-2.5.0/bin/kafka-server-start.sh /root/kafka_2.12-2.5.0/config/server.properties
|
||||
autostart=true
|
||||
autorestart=true
|
||||
|
||||
[program:kafka_zookeeper]
|
||||
directory=/root/kafka_2.12-2.2.1/
|
||||
command=/root/kafka_2.12-2.2.1/bin/zookeeper-server-start.sh /root/kafka_2.12-2.2.1/config/zookeeper.properties
|
||||
directory=/root/kafka_2.12-2.5.0/
|
||||
command=/root/kafka_2.12-2.5.0/bin/zookeeper-server-start.sh /root/kafka_2.12-2.5.0/config/zookeeper.properties
|
||||
autostart=true
|
||||
autorestart=true
|
||||
|
||||
[program:api]
|
||||
directory=/var/www/studio/api/
|
||||
command=dotnet ASC.Web.Api.dll --urls=http://0.0.0.0:5000 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --ENVIRONMENT=test
|
||||
command=dotnet ASC.Web.Api.dll --urls=http://0.0.0.0:5000 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --log:dir=/var/log/onlyoffice --log:name=api --ENVIRONMENT=test
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=root
|
||||
environment=ASPNETCORE_ENVIRONMENT=test
|
||||
|
||||
[program:notify]
|
||||
directory=/var/www/services/notify/
|
||||
command=dotnet ASC.Notify.dll --urls=http://0.0.0.0:5005 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --ENVIRONMENT=test
|
||||
[program:api_system]
|
||||
directory=/var/www/services/apisystem
|
||||
command=dotnet ASC.ApiSystem.dll --urls=http://0.0.0.0:5010 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --log:dir=/var/log/onlyoffice --log:name=apisystem --ENVIRONMENT=test
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=root
|
||||
environment=ASPNETCORE_ENVIRONMENT=test
|
||||
|
||||
[program:urlshortener]
|
||||
directory=/services/urlshortener/service
|
||||
command=dotnet ASC.UrlShortener.Svc.dll --urls=http://0.0.0.0:5015 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --log:dir=/var/log/onlyoffice --log:name=urlshortener --ENVIRONMENT=test
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=root
|
||||
environment=ASPNETCORE_ENVIRONMENT=test
|
||||
|
||||
[program:studio_notify]
|
||||
directory=/var/www/services/studio.notify/
|
||||
command=dotnet ASC.Studio.Notify.dll --urls=http://0.0.0.0:5006 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --ENVIRONMENT=test
|
||||
command=dotnet ASC.Studio.Notify.dll --urls=http://0.0.0.0:5006 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --log:dir=/var/log/onlyoffice --log:name=notify.studio --core:products:folder=/var/www/products/ --core:products:subfolder=server --ENVIRONMENT=test
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=root
|
||||
environment=ASPNETCORE_ENVIRONMENT=test
|
||||
|
||||
[program:people]
|
||||
directory=/var/www/products/ASC.People/server/
|
||||
command=dotnet ASC.People.dll --urls=http://0.0.0.0:5004 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --ENVIRONMENT=test
|
||||
command=dotnet ASC.People.dll --urls=http://0.0.0.0:5004 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --log:dir=/var/log/onlyoffice/ --log:name=people --ENVIRONMENT=test
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=root
|
||||
environment=ASPNETCORE_ENVIRONMENT=test
|
||||
|
||||
[program:files]
|
||||
directory=/var/www/products/ASC.Files/server/
|
||||
command=dotnet ASC.Files.dll --urls=http://0.0.0.0:5007 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --log:dir=/var/log/onlyoffice/ --log:name=files --ENVIRONMENT=test
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=root
|
||||
environment=ASPNETCORE_ENVIRONMENT=test
|
||||
|
||||
[program:files_services]
|
||||
directory=/var/www/products/ASC.Files/service
|
||||
command=dotnet ASC.Files.Service.dll --urls=http://0.0.0.0:5009 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --log:dir=/var/log/onlyoffice/ --log:name=filesservice --core:products:folder=/var/www/products --core:products:subfolder=server --ENVIRONMENT=test
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=root
|
||||
environment=ASPNETCORE_ENVIRONMENT=test
|
||||
|
||||
[program:studio]
|
||||
directory=/var/www/studio/server/
|
||||
command=dotnet ASC.Web.Studio.dll --urls=http://0.0.0.0:5003 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --ENVIRONMENT=test
|
||||
command=dotnet ASC.Web.Studio.dll --urls=http://0.0.0.0:5003 --pathToConf=/app/onlyoffice/config/ --$STORAGE_ROOT=/app/onlyoffice/data/ --log:dir=/var/log/onlyoffice --log:name=web --ENVIRONMENT=test
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=root
|
||||
environment=ASPNETCORE_ENVIRONMENT=test
|
||||
|
||||
[program:nginx]
|
||||
command=/usr/sbin/nginx -g "daemon off;"
|
||||
|
@ -231,6 +231,13 @@ namespace ASC.Core.Common
|
||||
|
||||
return baseUri.ToString().TrimEnd('/');
|
||||
}
|
||||
|
||||
public void Initialize(string serverUri)
|
||||
{
|
||||
var uri = new Uri(serverUri.Replace('*', 'x').Replace('+', 'x'));
|
||||
_serverRoot = new UriBuilder(uri.Scheme, LOCALHOST, uri.Port);
|
||||
_vpath = "/" + uri.AbsolutePath.Trim('/');
|
||||
}
|
||||
}
|
||||
|
||||
public static class BaseCommonLinkUtilityExtension
|
||||
|
@ -76,25 +76,34 @@ namespace ASC.Core
|
||||
|
||||
public UserManager(
|
||||
IUserService service,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
TenantManager tenantManager,
|
||||
PermissionContext permissionContext,
|
||||
UserManagerConstants userManagerConstants)
|
||||
{
|
||||
UserService = service;
|
||||
Accessor = httpContextAccessor;
|
||||
TenantManager = tenantManager;
|
||||
PermissionContext = permissionContext;
|
||||
UserManagerConstants = userManagerConstants;
|
||||
Constants = UserManagerConstants.Constants;
|
||||
}
|
||||
|
||||
public UserManager(
|
||||
IUserService service,
|
||||
TenantManager tenantManager,
|
||||
PermissionContext permissionContext,
|
||||
UserManagerConstants userManagerConstants,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
: this(service, tenantManager, permissionContext, userManagerConstants)
|
||||
{
|
||||
Accessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
if (UserService is ICachedService)
|
||||
if (UserService is ICachedService service)
|
||||
{
|
||||
((ICachedService)UserService).InvalidateCache();
|
||||
service.InvalidateCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,10 @@ namespace ASC.Core.Common.EF.Model
|
||||
[Table("feed_users")]
|
||||
public class FeedUsers : BaseEntity
|
||||
{
|
||||
[Column("feed_id")]
|
||||
public string FeedId { get; set; }
|
||||
|
||||
[Column("user_id")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
public override object[] GetKeys() => new object[] { FeedId, UserId };
|
||||
|
@ -49,7 +49,8 @@ namespace ASC.Feed.Data
|
||||
public TenantUtil TenantUtil { get; }
|
||||
public FeedDbContext FeedDbContext { get; }
|
||||
|
||||
public FeedAggregateDataProvider(DbContextManager<FeedDbContext> dbContextManager)
|
||||
public FeedAggregateDataProvider(AuthContext authContext, TenantManager tenantManager, TenantUtil tenantUtil, DbContextManager<FeedDbContext> dbContextManager)
|
||||
: this(authContext, tenantManager, tenantUtil)
|
||||
{
|
||||
FeedDbContext = dbContextManager.Get(Constants.FeedDbId);
|
||||
}
|
||||
@ -124,7 +125,10 @@ namespace ASC.Feed.Data
|
||||
if (f.ClearRightsBeforeInsert)
|
||||
{
|
||||
var fu = FeedDbContext.FeedUsers.Where(r => r.FeedId == f.Id).FirstOrDefault();
|
||||
FeedDbContext.FeedUsers.Remove(fu);
|
||||
if (fu != null)
|
||||
{
|
||||
FeedDbContext.FeedUsers.Remove(fu);
|
||||
}
|
||||
}
|
||||
|
||||
FeedDbContext.AddOrUpdate(r => r.FeedAggregates, feedAggregate);
|
||||
@ -382,6 +386,8 @@ namespace ASC.Feed.Data
|
||||
{
|
||||
public static DIHelper AddFeedAggregateDataProvider(this DIHelper services)
|
||||
{
|
||||
services.TryAddScoped<FeedAggregateDataProvider>();
|
||||
|
||||
return services
|
||||
.AddAuthContextService()
|
||||
.AddTenantManagerService()
|
||||
|
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\web\ASC.Web.Core\ASC.Web.Core.csproj" />
|
||||
<ProjectReference Include="..\..\ASC.Common\ASC.Common.csproj" />
|
||||
<ProjectReference Include="..\..\ASC.Core.Common\ASC.Core.Common.csproj" />
|
||||
<ProjectReference Include="..\..\ASC.Feed\ASC.Feed.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Modules\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
using ASC.Common.Utils;
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace ASC.Feed.Configuration
|
||||
{
|
||||
public class FeedSettings
|
||||
{
|
||||
public string ServerRoot { get; set; }
|
||||
|
||||
public TimeSpan AggregatePeriod { get; set; }
|
||||
|
||||
public TimeSpan AggregateInterval { get; set; }
|
||||
|
||||
public TimeSpan RemovePeriod { get; set; }
|
||||
|
||||
public static FeedSettings GetInstance(IConfiguration configuration)
|
||||
{
|
||||
var result = configuration.GetSetting<FeedSettings>("feed");
|
||||
|
||||
if (string.IsNullOrEmpty(result.ServerRoot))
|
||||
{
|
||||
result.ServerRoot = "http://*/";
|
||||
}
|
||||
|
||||
if (result.AggregatePeriod == TimeSpan.Zero)
|
||||
{
|
||||
result.AggregatePeriod = TimeSpan.FromMinutes(5);
|
||||
}
|
||||
|
||||
if (result.AggregateInterval == TimeSpan.Zero)
|
||||
{
|
||||
result.AggregateInterval = TimeSpan.FromDays(14);
|
||||
}
|
||||
|
||||
if (result.RemovePeriod == TimeSpan.Zero)
|
||||
{
|
||||
result.RemovePeriod = TimeSpan.FromDays(1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
131
common/services/ASC.Feed.Aggregator/Modules/FeedModule.cs
Normal file
131
common/services/ASC.Feed.Aggregator/Modules/FeedModule.cs
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
*
|
||||
* (c) Copyright Ascensio System Limited 2010-2020
|
||||
*
|
||||
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
|
||||
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
|
||||
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
|
||||
*
|
||||
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
|
||||
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
|
||||
*
|
||||
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
|
||||
* relevant author attributions when distributing the software. If the display of the logo in its graphic
|
||||
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
|
||||
* in every copy of the program you distribute.
|
||||
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using ASC.Core;
|
||||
using ASC.Feed.Data;
|
||||
using ASC.Web.Core;
|
||||
|
||||
namespace ASC.Feed.Aggregator.Modules
|
||||
{
|
||||
public abstract class FeedModule : IFeedModule
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
public abstract string Product { get; }
|
||||
public abstract Guid ProductID { get; }
|
||||
|
||||
protected abstract string DbId { get; }
|
||||
|
||||
protected int Tenant
|
||||
{
|
||||
get { return TenantManager.GetCurrentTenant().TenantId; }
|
||||
}
|
||||
|
||||
public TenantManager TenantManager { get; }
|
||||
public WebItemSecurity WebItemSecurity { get; }
|
||||
|
||||
public FeedModule(TenantManager tenantManager, WebItemSecurity webItemSecurity)
|
||||
{
|
||||
TenantManager = tenantManager;
|
||||
WebItemSecurity = webItemSecurity;
|
||||
}
|
||||
|
||||
protected string GetGroupId(string item, Guid author, string rootId = null, int action = -1)
|
||||
{
|
||||
const int interval = 2;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var hours = now.Hour;
|
||||
var groupIdHours = hours - (hours % interval);
|
||||
|
||||
if (rootId == null)
|
||||
{
|
||||
// groupId = {item}_{author}_{date}
|
||||
return string.Format("{0}_{1}_{2}",
|
||||
item,
|
||||
author,
|
||||
now.ToString("yyyy.MM.dd.") + groupIdHours);
|
||||
}
|
||||
if (action == -1)
|
||||
{
|
||||
// groupId = {item}_{author}_{date}_{rootId}_{action}
|
||||
return string.Format("{0}_{1}_{2}_{3}",
|
||||
item,
|
||||
author,
|
||||
now.ToString("yyyy.MM.dd.") + groupIdHours,
|
||||
rootId);
|
||||
}
|
||||
|
||||
// groupId = {item}_{author}_{date}_{rootId}_{action}
|
||||
return string.Format("{0}_{1}_{2}_{3}_{4}",
|
||||
item,
|
||||
author,
|
||||
now.ToString("yyyy.MM.dd.") + groupIdHours,
|
||||
rootId,
|
||||
action);
|
||||
}
|
||||
|
||||
|
||||
public abstract IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
|
||||
|
||||
public abstract IEnumerable<Tuple<Feed, object>> GetFeeds(FeedFilter filter);
|
||||
|
||||
public virtual bool VisibleFor(Feed feed, object data, Guid userId)
|
||||
{
|
||||
return WebItemSecurity.IsAvailableForUser(ProductID, userId);
|
||||
}
|
||||
|
||||
public virtual void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId)
|
||||
{
|
||||
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return;
|
||||
|
||||
foreach (var tuple in feed)
|
||||
{
|
||||
if (VisibleFor(tuple.Item1.Feed, tuple.Item2, userId))
|
||||
{
|
||||
tuple.Item1.Users.Add(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static Guid ToGuid(object guid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var str = guid as string;
|
||||
return !string.IsNullOrEmpty(str) ? new Guid(str) : Guid.Empty;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
47
common/services/ASC.Feed.Aggregator/Modules/IFeedModule.cs
Normal file
47
common/services/ASC.Feed.Aggregator/Modules/IFeedModule.cs
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
*
|
||||
* (c) Copyright Ascensio System Limited 2010-2020
|
||||
*
|
||||
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
|
||||
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
|
||||
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
|
||||
*
|
||||
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
|
||||
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
|
||||
*
|
||||
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
|
||||
* relevant author attributions when distributing the software. If the display of the logo in its graphic
|
||||
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
|
||||
* in every copy of the program you distribute.
|
||||
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ASC.Feed.Data;
|
||||
|
||||
namespace ASC.Feed.Aggregator.Modules
|
||||
{
|
||||
public interface IFeedModule
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
string Product { get; }
|
||||
|
||||
IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
|
||||
|
||||
IEnumerable<Tuple<Feed, object>> GetFeeds(FeedFilter filter);
|
||||
|
||||
bool VisibleFor(Feed feed, object data, Guid userId);
|
||||
|
||||
void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId);
|
||||
}
|
||||
}
|
@ -0,0 +1,306 @@
|
||||
/*
|
||||
*
|
||||
* (c) Copyright Ascensio System Limited 2010-2020
|
||||
*
|
||||
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
|
||||
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
|
||||
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
|
||||
*
|
||||
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
|
||||
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
|
||||
*
|
||||
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
|
||||
* relevant author attributions when distributing the software. If the display of the logo in its graphic
|
||||
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
|
||||
* in every copy of the program you distribute.
|
||||
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ASC.Common;
|
||||
using ASC.Common.Caching;
|
||||
using ASC.Common.Logging;
|
||||
using ASC.Core;
|
||||
using ASC.Core.Common;
|
||||
using ASC.Feed.Aggregator.Modules;
|
||||
using ASC.Feed.Configuration;
|
||||
using ASC.Feed.Data;
|
||||
|
||||
using Autofac;
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ASC.Feed.Aggregator
|
||||
{
|
||||
public class FeedAggregatorService : IHostedService
|
||||
{
|
||||
private ILog Log { get; set; }
|
||||
//private static readonly SignalrServiceClient signalrServiceClient = new SignalrServiceClient("counters");//TODO
|
||||
|
||||
private Timer aggregateTimer;
|
||||
private Timer removeTimer;
|
||||
|
||||
private volatile bool isStopped;
|
||||
private readonly object aggregateLock = new object();
|
||||
private readonly object removeLock = new object();
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
public IContainer Container { get; }
|
||||
|
||||
public FeedAggregatorService(
|
||||
IConfiguration configuration,
|
||||
IServiceProvider serviceProvider,
|
||||
IContainer container,
|
||||
IOptionsMonitor<ILog> optionsMonitor)
|
||||
{
|
||||
Configuration = configuration;
|
||||
ServiceProvider = serviceProvider;
|
||||
Container = container;
|
||||
Log = optionsMonitor.Get("ASC.Feed.Agregator");
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var cfg = FeedSettings.GetInstance(Configuration);
|
||||
isStopped = false;
|
||||
|
||||
aggregateTimer = new Timer(AggregateFeeds, cfg.AggregateInterval, TimeSpan.Zero, cfg.AggregatePeriod);
|
||||
removeTimer = new Timer(RemoveFeeds, cfg.AggregateInterval, cfg.RemovePeriod, cfg.RemovePeriod);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
isStopped = true;
|
||||
|
||||
if (aggregateTimer != null)
|
||||
{
|
||||
aggregateTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
aggregateTimer.Dispose();
|
||||
aggregateTimer = null;
|
||||
}
|
||||
|
||||
if (removeTimer != null)
|
||||
{
|
||||
removeTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
removeTimer.Dispose();
|
||||
removeTimer = null;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void AggregateFeeds(object interval)
|
||||
{
|
||||
if (!Monitor.TryEnter(aggregateLock)) return;
|
||||
|
||||
try
|
||||
{
|
||||
var cfg = FeedSettings.GetInstance(Configuration);
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var commonLinkUtility = scope.ServiceProvider.GetService<BaseCommonLinkUtility>();
|
||||
commonLinkUtility.Initialize(cfg.ServerRoot);
|
||||
|
||||
var tenantManager = scope.ServiceProvider.GetService<TenantManager>();
|
||||
var feedAggregateDataProvider = scope.ServiceProvider.GetService<FeedAggregateDataProvider>();
|
||||
|
||||
var start = DateTime.UtcNow;
|
||||
Log.DebugFormat("Start of collecting feeds...");
|
||||
|
||||
var unreadUsers = new Dictionary<int, Dictionary<Guid, int>>();
|
||||
using var autofacScope = Container.BeginLifetimeScope();
|
||||
var modules = autofacScope.Resolve<IEnumerable<IFeedModule>>();
|
||||
|
||||
foreach (var module in modules)
|
||||
{
|
||||
var result = new List<FeedRow>();
|
||||
var fromTime = feedAggregateDataProvider.GetLastTimeAggregate(module.GetType().Name);
|
||||
if (fromTime == default) fromTime = DateTime.UtcNow.Subtract((TimeSpan)interval);
|
||||
var toTime = DateTime.UtcNow;
|
||||
|
||||
var tenants = Attempt(10, () => module.GetTenantsWithFeeds(fromTime)).ToList();
|
||||
Log.DebugFormat("Find {1} tenants for module {0}.", module.GetType().Name, tenants.Count());
|
||||
|
||||
foreach (var tenant in tenants)
|
||||
{
|
||||
// Warning! There is hack here!
|
||||
// clearing the cache to get the correct acl
|
||||
var cache = AscCache.Memory;
|
||||
cache.Remove("acl" + tenant);
|
||||
cache.Remove("/webitemsecurity/" + tenant);
|
||||
//cache.Remove(string.Format("sub/{0}/{1}/{2}", tenant, "6045b68c-2c2e-42db-9e53-c272e814c4ad", NotifyConstants.Event_NewCommentForMessage.ID));
|
||||
|
||||
try
|
||||
{
|
||||
if (tenantManager.GetTenant(tenant) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tenantManager.SetCurrentTenant(tenant);
|
||||
var userManager = scope.ServiceProvider.GetService<UserManager>();
|
||||
var securityContext = scope.ServiceProvider.GetService<SecurityContext>();
|
||||
var authManager = scope.ServiceProvider.GetService<AuthManager>();
|
||||
var users = userManager.GetUsers();
|
||||
|
||||
var feeds = Attempt(10, () => module.GetFeeds(new FeedFilter(fromTime, toTime) { Tenant = tenant }).Where(r => r.Item1 != null).ToList());
|
||||
Log.DebugFormat("{0} feeds in {1} tenant.", feeds.Count, tenant);
|
||||
|
||||
var tenant1 = tenant;
|
||||
var module1 = module;
|
||||
var feedsRow = feeds
|
||||
.Select(tuple => new Tuple<FeedRow, object>(new FeedRow(tuple.Item1)
|
||||
{
|
||||
Tenant = tenant1,
|
||||
ProductId = module1.Product
|
||||
}, tuple.Item2))
|
||||
.ToList();
|
||||
|
||||
foreach (var u in users)
|
||||
{
|
||||
if (isStopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!TryAuthenticate(securityContext, authManager, tenant1, u.ID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
module.VisibleFor(feedsRow, u.ID);
|
||||
}
|
||||
|
||||
result.AddRange(feedsRow.Select(r => r.Item1));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorFormat("Tenant: {0}, {1}", tenant, ex);
|
||||
}
|
||||
}
|
||||
|
||||
feedAggregateDataProvider.SaveFeeds(result, module.GetType().Name, toTime);
|
||||
|
||||
foreach (var res in result)
|
||||
{
|
||||
foreach (var userGuid in res.Users.Where(userGuid => !userGuid.Equals(res.ModifiedById)))
|
||||
{
|
||||
if (!unreadUsers.TryGetValue(res.Tenant, out var dictionary))
|
||||
{
|
||||
dictionary = new Dictionary<Guid, int>();
|
||||
}
|
||||
if (dictionary.ContainsKey(userGuid))
|
||||
{
|
||||
++dictionary[userGuid];
|
||||
}
|
||||
else
|
||||
{
|
||||
dictionary.Add(userGuid, 1);
|
||||
}
|
||||
|
||||
unreadUsers[res.Tenant] = dictionary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//signalrServiceClient.SendUnreadUsers(unreadUsers);
|
||||
|
||||
Log.DebugFormat("Time of collecting news: {0}", DateTime.UtcNow - start);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(aggregateLock);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveFeeds(object interval)
|
||||
{
|
||||
if (!Monitor.TryEnter(removeLock)) return;
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var feedAggregateDataProvider = scope.ServiceProvider.GetService<FeedAggregateDataProvider>();
|
||||
Log.DebugFormat("Start of removing old news");
|
||||
feedAggregateDataProvider.RemoveFeedAggregate(DateTime.UtcNow.Subtract((TimeSpan)interval));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(removeLock);
|
||||
}
|
||||
}
|
||||
|
||||
private static T Attempt<T>(int count, Func<T> action)
|
||||
{
|
||||
var counter = 0;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
return action();
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (count < ++counter)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryAuthenticate(SecurityContext securityContext, AuthManager authManager, int tenantId, Guid userid)
|
||||
{
|
||||
try
|
||||
{
|
||||
securityContext.AuthenticateMe(authManager.GetAccountByID(tenantId, userid));
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FeedAggregatorServiceExtension
|
||||
{
|
||||
public static DIHelper AddFeedAggregatorService(this DIHelper services)
|
||||
{
|
||||
services.TryAddSingleton<FeedAggregatorService>();
|
||||
|
||||
return services
|
||||
.AddBaseCommonLinkUtilityService()
|
||||
.AddTenantManagerService()
|
||||
.AddUserManagerService()
|
||||
.AddSecurityContextService()
|
||||
.AddAuthManager()
|
||||
.AddFeedAggregateDataProvider();
|
||||
}
|
||||
}
|
||||
}
|
@ -4,8 +4,11 @@ import { Router, Switch, Redirect } from "react-router-dom";
|
||||
import { Loader } from "asc-web-components";
|
||||
import Home from "./components/pages/Home";
|
||||
import DocEditor from "./components/pages/DocEditor";
|
||||
|
||||
import { history, PrivateRoute, PublicRoute, Login, Error404, StudioLayout, Offline } from "asc-web-common";
|
||||
|
||||
const VersionHistory = React.lazy(() => import('./components/pages/VersionHistory'));
|
||||
|
||||
const withStudioLayout = Component => props => <StudioLayout><Component {...props} /></StudioLayout>;
|
||||
|
||||
const App = ({ settings }) => {
|
||||
@ -21,6 +24,7 @@ const App = ({ settings }) => {
|
||||
<Redirect exact from="/" to={`${homepage}`} />
|
||||
<PrivateRoute exact path={[homepage, `${homepage}/filter`]} component={withStudioLayout(Home)} />
|
||||
<PrivateRoute exact path={`${homepage}/doceditor`} component={DocEditor} />
|
||||
<PrivateRoute exact path={`${homepage}/:fileId/history`} component={withStudioLayout(VersionHistory)} />
|
||||
<PublicRoute exact path={["/login","/login/error=:error", "/login/confirmed-email=:confirmedEmail"]} component={Login} />
|
||||
<PrivateRoute component={withStudioLayout(Error404)} />
|
||||
</Switch>
|
||||
|
@ -1,14 +1,15 @@
|
||||
import React from "react";
|
||||
import { TreeMenu, TreeNode, Icons, toastr, utils } from "asc-web-components";
|
||||
import { api } from "asc-web-common";
|
||||
import { api, constants } from "asc-web-common";
|
||||
const { files } = api;
|
||||
const { FolderType, ShareAccessRights } = constants;
|
||||
|
||||
class TreeFolders extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const treeData = props.data;
|
||||
this.state = { treeData, expandedKeys: props.expandedKeys, loaded: true };
|
||||
this.state = { treeData, expandedKeys: props.expandedKeys };
|
||||
}
|
||||
|
||||
getFolderIcon = key => {
|
||||
@ -32,8 +33,48 @@ class TreeFolders extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
showDragItems = (item) => {
|
||||
const { isAdmin, myId, commonId, isCommon, isMy, isShare, currentId } = this.props;
|
||||
if (item.id === currentId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
item.rootFolderType === FolderType.SHARE &&
|
||||
item.access === ShareAccessRights.FullAccess
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isAdmin) {
|
||||
if (isMy || isCommon || isShare) {
|
||||
if (
|
||||
(item.pathParts &&
|
||||
(item.pathParts[0] === myId || item.pathParts[0] === commonId)) ||
|
||||
item.rootFolderType === FolderType.USER ||
|
||||
item.rootFolderType === FolderType.COMMON
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isMy || isCommon || isShare) {
|
||||
if (
|
||||
(item.pathParts && item.pathParts[0] === myId) ||
|
||||
item.rootFolderType === FolderType.USER
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
getItems = data => {
|
||||
return data.map(item => {
|
||||
const dragging = this.showDragItems(item);
|
||||
|
||||
if (item.folders && item.folders.length > 0) {
|
||||
return (
|
||||
<TreeNode
|
||||
@ -41,6 +82,7 @@ class TreeFolders extends React.Component {
|
||||
key={item.id}
|
||||
title={item.title}
|
||||
icon={this.getFolderIcon(item.key)}
|
||||
dragging={this.props.dragging && dragging}
|
||||
>
|
||||
{this.getItems(item.folders)}
|
||||
</TreeNode>
|
||||
@ -51,6 +93,7 @@ class TreeFolders extends React.Component {
|
||||
id={item.id}
|
||||
key={item.id}
|
||||
title={item.title}
|
||||
dragging={this.props.dragging && dragging}
|
||||
isLeaf={item.foldersCount ? false : true}
|
||||
icon={this.getFolderIcon(item.key)}
|
||||
/>
|
||||
@ -177,7 +220,7 @@ class TreeFolders extends React.Component {
|
||||
this.props.setFilter(newFilter);
|
||||
}
|
||||
|
||||
this.setState({ expandedKeys: data, loaded: false });
|
||||
this.setState({ expandedKeys: data });
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@ -195,17 +238,30 @@ class TreeFolders extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
onMouseEnter = (data) => {
|
||||
if (this.props.dragging) {
|
||||
if(data.node.props.dragging) {
|
||||
this.props.setDragItem(data.node.props.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMouseLeave = data => {
|
||||
if (this.props.dragging) {
|
||||
this.props.setDragItem(null);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
selectedKeys,
|
||||
fakeNewDocuments,
|
||||
isLoading,
|
||||
onSelect,
|
||||
needUpdate
|
||||
needUpdate,
|
||||
onBadgeClick
|
||||
} = this.props;
|
||||
const { treeData, expandedKeys, loaded } = this.state;
|
||||
|
||||
const loadProp = loaded && needUpdate ? { loadData: this.onLoadData } : {};
|
||||
const { treeData, expandedKeys } = this.state;
|
||||
const loadProp = needUpdate ? { loadData: this.onLoadData } : {};
|
||||
|
||||
return (
|
||||
<TreeMenu
|
||||
@ -218,11 +274,13 @@ class TreeFolders extends React.Component {
|
||||
switcherIcon={this.switcherIcon}
|
||||
onSelect={onSelect}
|
||||
selectedKeys={selectedKeys}
|
||||
badgeLabel={fakeNewDocuments}
|
||||
onBadgeClick={() => console.log("onBadgeClick")}
|
||||
//badgeLabel={3}
|
||||
//onBadgeClick={onBadgeClick}
|
||||
{...loadProp}
|
||||
expandedKeys={expandedKeys}
|
||||
onExpand={this.onExpand}
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>
|
||||
{this.getItems(treeData)}
|
||||
</TreeMenu>
|
||||
|
@ -5,12 +5,18 @@ import TreeFolders from "./TreeFolders";
|
||||
import {
|
||||
setFilter,
|
||||
fetchFiles,
|
||||
setTreeFolders
|
||||
setTreeFolders,
|
||||
setDragItem
|
||||
} from "../../../store/files/actions";
|
||||
import store from "../../../store/store";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { NewFilesPanel } from "../../panels";
|
||||
|
||||
class ArticleBodyContent extends React.Component {
|
||||
state = { expandedKeys: this.props.filter.treeFolders };
|
||||
state = {
|
||||
expandedKeys: this.props.filter.treeFolders,
|
||||
showNewFilesPanel: false,
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
@ -30,55 +36,122 @@ class ArticleBodyContent extends React.Component {
|
||||
newFilter.page = 0;
|
||||
newFilter.startIndex = 0;
|
||||
|
||||
fetchFiles(data[0], newFilter, store.dispatch).catch(err =>
|
||||
toastr.error(err)
|
||||
).finally(() => onLoading(false));
|
||||
fetchFiles(data[0], newFilter, store.dispatch)
|
||||
.catch(err => toastr.error(err))
|
||||
.finally(() => onLoading(false));
|
||||
}
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (!isEqual(this.state, nextState) || !isEqual(this.props, nextProps)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
onShowNewFilesPanel = () => {
|
||||
this.setState({showNewFilesPanel: !this.state.showNewFilesPanel});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
data,
|
||||
selectedKeys,
|
||||
fakeNewDocuments,
|
||||
filter,
|
||||
setFilter,
|
||||
setTreeFolders,
|
||||
onLoading,
|
||||
isLoading
|
||||
isLoading,
|
||||
dragging,
|
||||
setDragItem,
|
||||
isMy,
|
||||
myId,
|
||||
isCommon,
|
||||
commonId,
|
||||
currentId,
|
||||
isAdmin,
|
||||
isShare
|
||||
} = this.props;
|
||||
|
||||
const { showNewFilesPanel, expandedKeys } = this.state;
|
||||
|
||||
const fakeFiles = this.props.selection;
|
||||
|
||||
//console.log("Article Body render", this.props, this.state.expandedKeys);
|
||||
return (
|
||||
<TreeFolders
|
||||
selectedKeys={selectedKeys}
|
||||
fakeNewDocuments={fakeNewDocuments}
|
||||
onSelect={this.onSelect}
|
||||
data={data}
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
setTreeFolders={setTreeFolders}
|
||||
expandedKeys={this.state.expandedKeys}
|
||||
onLoading={onLoading}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<>
|
||||
{showNewFilesPanel && (
|
||||
<NewFilesPanel
|
||||
visible={showNewFilesPanel}
|
||||
onClose={this.onShowNewFilesPanel}
|
||||
files={fakeFiles}
|
||||
/>
|
||||
)}
|
||||
<TreeFolders
|
||||
selectedKeys={selectedKeys}
|
||||
onSelect={this.onSelect}
|
||||
data={data}
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
setTreeFolders={setTreeFolders}
|
||||
expandedKeys={expandedKeys}
|
||||
onLoading={onLoading}
|
||||
isLoading={isLoading}
|
||||
dragging={dragging}
|
||||
setDragItem={setDragItem}
|
||||
isMy={isMy}
|
||||
myId={myId}
|
||||
isCommon={isCommon}
|
||||
commonId={commonId}
|
||||
currentId={currentId}
|
||||
isAdmin={isAdmin}
|
||||
isShare={isShare}
|
||||
onBadgeClick={this.onShowNewFilesPanel}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const { treeFolders, selectedFolder, filter } = state.files;
|
||||
const { treeFolders, selectedFolder, filter, selection } = state.files;
|
||||
const currentFolderId = selectedFolder.id.toString();
|
||||
const fakeNewDocuments = 8;
|
||||
const myFolderIndex = 0;
|
||||
const shareFolderIndex = 1;
|
||||
const commonFolderIndex = 2;
|
||||
|
||||
const myId = treeFolders[myFolderIndex].id;
|
||||
const shareId = treeFolders[shareFolderIndex].id;
|
||||
const commonId = treeFolders[commonFolderIndex].id;
|
||||
|
||||
const isMy = selectedFolder &&
|
||||
selectedFolder.pathParts &&
|
||||
selectedFolder.pathParts[0] === myId;
|
||||
|
||||
const isShare = selectedFolder &&
|
||||
selectedFolder.pathParts &&
|
||||
selectedFolder.pathParts[0] === shareId;
|
||||
|
||||
const isCommon = selectedFolder &&
|
||||
selectedFolder.pathParts &&
|
||||
selectedFolder.pathParts[0] === commonId;
|
||||
|
||||
return {
|
||||
data: treeFolders,
|
||||
selectedKeys: selectedFolder ? [currentFolderId] : [""],
|
||||
fakeNewDocuments,
|
||||
filter
|
||||
filter,
|
||||
isMy,
|
||||
isCommon,
|
||||
isShare,
|
||||
myId,
|
||||
commonId,
|
||||
currentId: selectedFolder.id,
|
||||
isAdmin: state.auth.user.isAdmin,
|
||||
selection
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, { setFilter, setTreeFolders })(
|
||||
export default connect(mapStateToProps, { setFilter, setTreeFolders, setDragItem })(
|
||||
ArticleBodyContent
|
||||
);
|
||||
|
@ -173,6 +173,13 @@ class FilesRowContent extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
onShowVersionHistory = (e) => {
|
||||
const {settings, history} = this.props;
|
||||
const fileId = e.currentTarget.dataset.id;
|
||||
|
||||
history.push(`${settings.homepage}/${fileId}/history`);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { t, item, fileAction, isLoading, isTrashFolder } = this.props;
|
||||
const { itemTitle, editingId/*, loading*/ } = this.state;
|
||||
@ -317,8 +324,9 @@ class FilesRowContent extends React.PureComponent {
|
||||
fontWeight={800}
|
||||
label={`Ver.${versionGroup}`}
|
||||
maxWidth="50px"
|
||||
onClick={() => { }}
|
||||
onClick={this.onShowVersionHistory}
|
||||
padding="0 5px"
|
||||
data-id={id}
|
||||
/>
|
||||
}
|
||||
{fileStatus === 2 &&
|
||||
@ -331,8 +339,9 @@ class FilesRowContent extends React.PureComponent {
|
||||
fontWeight={800}
|
||||
label={`New`}
|
||||
maxWidth="50px"
|
||||
onClick={() => { }}
|
||||
onClick={this.onShowVersionHistory}
|
||||
padding="0 5px"
|
||||
data-id={id}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
@ -384,13 +393,15 @@ class FilesRowContent extends React.PureComponent {
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const { filter, fileAction, selectedFolder, treeFolders } = state.files;
|
||||
const { settings } = state.auth;
|
||||
const indexOfTrash = 3;
|
||||
|
||||
return {
|
||||
filter,
|
||||
fileAction,
|
||||
parentFolder: selectedFolder.id,
|
||||
isTrashFolder: treeFolders[indexOfTrash].id === selectedFolder.id
|
||||
isTrashFolder: treeFolders[indexOfTrash].id === selectedFolder.id,
|
||||
settings
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,9 @@ import { connect } from "react-redux";
|
||||
import { ReactSVG } from 'react-svg'
|
||||
import { withTranslation } from "react-i18next";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import copy from "copy-to-clipboard";
|
||||
import styled from "styled-components";
|
||||
import queryString from 'query-string';
|
||||
import {
|
||||
IconButton,
|
||||
Row,
|
||||
@ -20,14 +22,15 @@ import {
|
||||
deleteFolder,
|
||||
deselectFile,
|
||||
fetchFiles,
|
||||
fetchFolder,
|
||||
selectFile,
|
||||
setAction,
|
||||
setTreeFolders,
|
||||
moveToFolder,
|
||||
getProgress
|
||||
copyToFolder,
|
||||
getProgress,
|
||||
setDragItem
|
||||
} from '../../../../../store/files/actions';
|
||||
import { isFileSelected, getFileIcon, getFolderIcon, getFolderType, loopTreeFolders, isImage, isSound, isVideo } from '../../../../../store/files/selectors';
|
||||
import { isFileSelected, getFileIcon, getFolderIcon, getFolderType, loopTreeFolders, isImage, isSound, isVideo } from '../../../../../store/files/selectors';
|
||||
import store from "../../../../../store/store";
|
||||
import { SharingPanel } from "../../../../panels";
|
||||
//import { getFilterByLocation } from "../../../../../helpers/converters";
|
||||
@ -40,6 +43,9 @@ const linkStyles = { isHovered: true, type: "action", fontSize: "14px", classNam
|
||||
const backgroundDragColor = "#EFEFB2";
|
||||
const backgroundDragEnterColor = "#F8F7BF";
|
||||
|
||||
const extsMediaPreviewed = [".aac", ".flac", ".m4a", ".mp3", ".oga", ".ogg", ".wav", ".f4v", ".m4v", ".mov", ".mp4", ".ogv", ".webm", ".avi", ".mpg", ".mpeg", ".wmv"];
|
||||
const extsImagePreviewed = [".bmp", ".gif", ".jpeg", ".jpg", ".png", ".ico", ".tif", ".tiff", ".webp"];
|
||||
|
||||
const CustomTooltip = styled.div`
|
||||
position: fixed;
|
||||
display: none;
|
||||
@ -90,8 +96,13 @@ class SectionBodyContent extends React.Component {
|
||||
// .catch(error => toastr.error(error));
|
||||
// }
|
||||
// }
|
||||
let previewId = queryString.parse(this.props.location.search).preview;
|
||||
|
||||
if(previewId){
|
||||
this.showMediaFile(previewId);
|
||||
}
|
||||
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
document.addEventListener("mousedown", this.onMouseDown);
|
||||
|
||||
document.addEventListener("dragover", this.onDragOver);
|
||||
document.addEventListener("dragleave", this.onDragLeaveDoc);
|
||||
@ -99,7 +110,6 @@ class SectionBodyContent extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
document.removeEventListener("mousedown", this.onMouseDown);
|
||||
|
||||
document.removeEventListener("dragover", this.onDragOver);
|
||||
document.removeEventListener("dragleave", this.onDragLeaveDoc);
|
||||
@ -117,13 +127,18 @@ class SectionBodyContent extends React.Component {
|
||||
} */
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if(this.state.showSharingPanel !== nextState.showSharingPanel) {
|
||||
if (this.state.showSharingPanel !== nextState.showSharingPanel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!isEqual(this.props, nextProps) || !isEqual(this.state.mediaViewerVisible, nextState.mediaViewerVisible)) {
|
||||
if (this.props.dragItem !== nextProps.dragItem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isEqual(this.props, nextProps) || !isEqual(this.state.mediaViewerVisible, nextState.mediaViewerVisible)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -187,7 +202,7 @@ class SectionBodyContent extends React.Component {
|
||||
const { filter, treeFolders, setTreeFolders, currentFolderType, getProgress, setProgressValue, finishFilesOperations } = this.props;
|
||||
getProgress().then(res => {
|
||||
const deleteProgress = res.find(x => x.id === id);
|
||||
if(deleteProgress && deleteProgress.progress !== 100) {
|
||||
if (deleteProgress && deleteProgress.progress !== 100) {
|
||||
setProgressValue(deleteProgress.progress);
|
||||
setTimeout(() => this.loopDeleteProgress(id, folderId, isFolder), 1000);
|
||||
} else {
|
||||
@ -233,7 +248,19 @@ class SectionBodyContent extends React.Component {
|
||||
}
|
||||
|
||||
onClickLinkForPortal = item => {
|
||||
return fetchFolder(item.folderId, store.dispatch);
|
||||
const {settings} = this.props;
|
||||
const isFile = !!item.fileExst;
|
||||
const { t } = this.props;
|
||||
|
||||
copy(isFile
|
||||
?
|
||||
this.isMediaOrImage(item.fileExst)
|
||||
? `${window.location.origin + settings.homepage}/filter?folder=${item.folderId}&preview=${item.id}`
|
||||
: item.webUrl
|
||||
:
|
||||
`${window.location.origin + settings.homepage}/filter?folder=${item.id}`);
|
||||
|
||||
toastr.success(t("LinkCopySuccess"));
|
||||
}
|
||||
|
||||
onClickDownload = item => {
|
||||
@ -266,7 +293,7 @@ class SectionBodyContent extends React.Component {
|
||||
key: "link-for-portal-users",
|
||||
label: "Link for portal users",
|
||||
onClick: this.onClickLinkForPortal.bind(this, item),
|
||||
disabled: true
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
key: "sep",
|
||||
@ -584,34 +611,34 @@ class SectionBodyContent extends React.Component {
|
||||
/>
|
||||
)
|
||||
}
|
||||
onMediaViewerClose = () =>{
|
||||
onMediaViewerClose = () => {
|
||||
this.setState({
|
||||
mediaViewerVisible: false
|
||||
});
|
||||
}
|
||||
onMediaFileClick = (id) => {
|
||||
this.setState({
|
||||
mediaViewerVisible: true,
|
||||
currentMediaFileId: id
|
||||
});
|
||||
this.setState({
|
||||
mediaViewerVisible: true,
|
||||
currentMediaFileId: id
|
||||
});
|
||||
}
|
||||
onDownloadMediaFile = (id) => {
|
||||
if(this.props.files.length > 0){
|
||||
if (this.props.files.length > 0) {
|
||||
let viewUrlFile = this.props.files.find(file => file.id === id).viewUrl;
|
||||
return window.open(viewUrlFile, "_blank");
|
||||
}
|
||||
}
|
||||
onDeleteMediaFile = (id) => {
|
||||
if(this.props.files.length > 0){
|
||||
if (this.props.files.length > 0) {
|
||||
let file = this.props.files.find(file => file.id === id);
|
||||
if(file)
|
||||
if (file)
|
||||
this.onDeleteFile(file.id, file.folderId)
|
||||
}
|
||||
}
|
||||
|
||||
onDragEnter = (item, e) => {
|
||||
const isCurrentItem = this.props.selection.find(x => x.id === item.id && x.fileExst === item.fileExst);
|
||||
if(!item.fileExst && (!isCurrentItem || e.dataTransfer.items.length)) {
|
||||
if (!item.fileExst && (!isCurrentItem || e.dataTransfer.items.length)) {
|
||||
e.currentTarget.style.background = backgroundDragColor;
|
||||
}
|
||||
}
|
||||
@ -619,16 +646,16 @@ class SectionBodyContent extends React.Component {
|
||||
onDragLeave = (item, e) => {
|
||||
const { selection, dragging, setDragging } = this.props;
|
||||
const isCurrentItem = selection.find(x => x.id === item.id && x.fileExst === item.fileExst);
|
||||
if(!e.dataTransfer.items.length) {
|
||||
if (!e.dataTransfer.items.length) {
|
||||
e.currentTarget.style.background = "none";
|
||||
} else if(!item.fileExst && !isCurrentItem) {
|
||||
} else if (!item.fileExst && !isCurrentItem) {
|
||||
e.currentTarget.style.background = backgroundDragEnterColor;
|
||||
}
|
||||
if(dragging && !e.relatedTarget) { setDragging(false); }
|
||||
if (dragging && !e.relatedTarget) { setDragging(false); }
|
||||
}
|
||||
|
||||
onDrop = (item, e) => {
|
||||
if(e.dataTransfer.items.length > 0 && !item.fileExst) {
|
||||
if (e.dataTransfer.items.length > 0 && !item.fileExst) {
|
||||
const { setDragging, onDropZoneUpload } = this.props;
|
||||
e.currentTarget.style.background = backgroundDragEnterColor;
|
||||
setDragging(false);
|
||||
@ -639,7 +666,7 @@ class SectionBodyContent extends React.Component {
|
||||
onDragOver = e => {
|
||||
e.preventDefault();
|
||||
const { dragging, setDragging } = this.props;
|
||||
if(e.dataTransfer.items.length > 0 && !dragging) {
|
||||
if (e.dataTransfer.items.length > 0 && !dragging) {
|
||||
setDragging(true);
|
||||
}
|
||||
}
|
||||
@ -647,60 +674,61 @@ class SectionBodyContent extends React.Component {
|
||||
onDragLeaveDoc = e => {
|
||||
e.preventDefault();
|
||||
const { dragging, setDragging } = this.props;
|
||||
if(dragging && !e.relatedTarget) {
|
||||
if (dragging && !e.relatedTarget) {
|
||||
setDragging(false);
|
||||
}
|
||||
}
|
||||
|
||||
onMouseDown = e => {
|
||||
const mouseButton = e.which ? e.which !== 1 : e.button ? e.button !== 0 : false;
|
||||
if(mouseButton || e.target.tagName !== "DIV") { return; }
|
||||
const label = e.target.getAttribute('label');
|
||||
if (mouseButton || e.target.tagName !== "DIV" || label) { return; }
|
||||
document.addEventListener("mousemove", this.onMouseMove);
|
||||
this.setTooltipPosition(e);
|
||||
const { selection, setDragging } = this.props;
|
||||
|
||||
const elem = e.target.closest('.draggable');
|
||||
if(!elem) {
|
||||
if (!elem) {
|
||||
return;
|
||||
}
|
||||
const value = elem.getAttribute('value');
|
||||
if(!value) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
const splitValue = value.split("_");
|
||||
let item = null;
|
||||
if(splitValue[0] === "folder") {
|
||||
if (splitValue[0] === "folder") {
|
||||
item = selection.find(x => x.id === Number(splitValue[1]) && !x.fileExst);
|
||||
} else {
|
||||
item = selection.find(x => x.id === Number(splitValue[1]) && x.fileExst);
|
||||
}
|
||||
if(item) {
|
||||
if (item) {
|
||||
setDragging(true);
|
||||
}
|
||||
}
|
||||
|
||||
onMouseUp = e => {
|
||||
const { selection, dragging, setDragging } = this.props;
|
||||
const { selection, dragging, setDragging, dragItem, setDragItem } = this.props;
|
||||
const mouseButton = e.which ? e.which !== 1 : e.button ? e.button !== 0 : false;
|
||||
if(mouseButton || !this.tooltipRef.current || !dragging) { return; }
|
||||
if (mouseButton || !this.tooltipRef.current || !dragging) { return; }
|
||||
document.removeEventListener("mousemove", this.onMouseMove);
|
||||
this.tooltipRef.current.style.display = "none";
|
||||
|
||||
|
||||
const elem = e.target.closest('.dropable');
|
||||
if(elem && selection.length && dragging) {
|
||||
if (elem && selection.length && dragging) {
|
||||
const value = elem.getAttribute('value');
|
||||
if(!value) {
|
||||
if (!value) {
|
||||
setDragging(false);
|
||||
return;
|
||||
}
|
||||
const splitValue = value.split("_");
|
||||
let item = null;
|
||||
if(splitValue[0] === "folder") {
|
||||
if (splitValue[0] === "folder") {
|
||||
item = selection.find(x => x.id === Number(splitValue[1]) && !x.fileExst);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if(item) {
|
||||
if (item) {
|
||||
setDragging(false);
|
||||
return;
|
||||
} else {
|
||||
@ -710,18 +738,23 @@ class SectionBodyContent extends React.Component {
|
||||
}
|
||||
} else {
|
||||
setDragging(false);
|
||||
if (dragItem) {
|
||||
this.onMoveTo(dragItem);
|
||||
setDragItem(null);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onMouseMove = e => {
|
||||
if(this.props.dragging) {
|
||||
if (this.props.dragging) {
|
||||
const tooltip = this.tooltipRef.current;
|
||||
tooltip.style.display = "block";
|
||||
this.setTooltipPosition(e);
|
||||
|
||||
const wrapperElement = document.elementFromPoint(e.clientX, e.clientY);
|
||||
if(!wrapperElement) { return; }
|
||||
if (!wrapperElement) { return; }
|
||||
const droppable = wrapperElement.closest('.dropable');
|
||||
|
||||
if (this.currentDroppable !== droppable) {
|
||||
@ -740,27 +773,70 @@ class SectionBodyContent extends React.Component {
|
||||
|
||||
setTooltipPosition = e => {
|
||||
const tooltip = this.tooltipRef.current;
|
||||
const margin = 8;
|
||||
tooltip.style.left = e.pageX + margin + "px";
|
||||
tooltip.style.top = e.pageY + margin + "px";
|
||||
if (tooltip) {
|
||||
const margin = 8;
|
||||
tooltip.style.left = e.pageX + margin + "px";
|
||||
tooltip.style.top = e.pageY + margin + "px";
|
||||
}
|
||||
};
|
||||
|
||||
showMediaFile = (id) => {
|
||||
this.onMediaFileClick(+id);
|
||||
}
|
||||
|
||||
isMediaOrImage = (fileExst) => {
|
||||
if(extsMediaPreviewed.includes(fileExst) || extsImagePreviewed.includes(fileExst)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
onMoveTo = (destFolderId) => {
|
||||
const { selection, moveToFolder, finishFilesOperations, startFilesOperations, t, loopFilesOperations } = this.props;
|
||||
const { selection, startFilesOperations, t, isShare, isCommon, isAdmin } = this.props;
|
||||
const folderIds = [];
|
||||
const fileIds = [];
|
||||
const conflictResolveType = 0; //Skip = 0, Overwrite = 1, Duplicate = 2
|
||||
const deleteAfter = true;
|
||||
|
||||
for(let item of selection) {
|
||||
if(item.fileExst) {
|
||||
startFilesOperations(t("MoveToOperation"));
|
||||
for (let item of selection) {
|
||||
if (item.fileExst) {
|
||||
fileIds.push(item.id)
|
||||
} else {
|
||||
folderIds.push(item.id)
|
||||
}
|
||||
}
|
||||
|
||||
startFilesOperations(t("MoveToOperation"));
|
||||
|
||||
if (isAdmin) {
|
||||
if (isShare) {
|
||||
this.copyTo(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter);
|
||||
} else {
|
||||
this.moveTo(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter);
|
||||
}
|
||||
} else {
|
||||
if (isShare || isCommon) {
|
||||
this.copyTo(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter);
|
||||
} else {
|
||||
this.moveTo(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copyTo = (destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter) => {
|
||||
const { copyToFolder, loopFilesOperations, finishFilesOperations } = this.props;
|
||||
|
||||
copyToFolder(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter)
|
||||
.then(res => {
|
||||
const id = res[0] && res[0].id ? res[0].id : null;
|
||||
loopFilesOperations(id, destFolderId, true);
|
||||
})
|
||||
.catch(err => finishFilesOperations(err))
|
||||
}
|
||||
|
||||
moveTo = (destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter) => {
|
||||
const { moveToFolder, loopFilesOperations, finishFilesOperations } = this.props;
|
||||
|
||||
moveToFolder(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter)
|
||||
.then(res => {
|
||||
const id = res[0] && res[0].id ? res[0].id : null;
|
||||
@ -769,9 +845,45 @@ class SectionBodyContent extends React.Component {
|
||||
.catch(err => finishFilesOperations(err))
|
||||
}
|
||||
|
||||
getTooltipLabel = () => {
|
||||
const { t, selection, isAdmin, isShare, isCommon } = this.props;
|
||||
const elementTitle = selection.length && selection[0].title;
|
||||
const elementCount = selection.length;
|
||||
if (selection.length) {
|
||||
if (selection.length > 1) {
|
||||
if (isAdmin) {
|
||||
if (isShare) {
|
||||
return t("TooltipElementsCopyMessage", { element: elementCount });
|
||||
} else {
|
||||
return t("TooltipElementsMoveMessage", { element: elementCount });
|
||||
}
|
||||
} else {
|
||||
if (isShare || isCommon) {
|
||||
return t("TooltipElementsCopyMessage", { element: elementCount });
|
||||
} else {
|
||||
return t("TooltipElementsMoveMessage", { element: elementCount });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isAdmin) {
|
||||
if (isShare) {
|
||||
return t("TooltipElementCopyMessage", { element: elementTitle });
|
||||
} else {
|
||||
return t("TooltipElementMoveMessage", { element: elementTitle });
|
||||
}
|
||||
} else {
|
||||
if (isShare || isCommon) {
|
||||
return t("TooltipElementCopyMessage", { element: elementTitle });
|
||||
} else {
|
||||
return t("TooltipElementMoveMessage", { element: elementTitle });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
t,
|
||||
files,
|
||||
folders,
|
||||
viewer,
|
||||
@ -790,12 +902,8 @@ class SectionBodyContent extends React.Component {
|
||||
const { editingId, showSharingPanel, currentItem } = this.state;
|
||||
|
||||
let items = [...folders, ...files];
|
||||
|
||||
const tooltipLabel = !selection.length
|
||||
? ""
|
||||
: selection.length > 1
|
||||
? t("TooltipElementsMessage", { element: selection.length })
|
||||
: t("TooltipElementMessage", { element: selection[0].title });
|
||||
|
||||
const tooltipLabel = this.getTooltipLabel();
|
||||
|
||||
const SimpleFilesRow = styled(Row)`
|
||||
${(props) =>
|
||||
@ -816,11 +924,11 @@ class SectionBodyContent extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
var playlist = [];
|
||||
let id = 0;
|
||||
files.forEach(function(file, i, files) {
|
||||
if(isImage(file.fileExst) || isSound(file.fileExst) || isVideo(file.fileExst)){
|
||||
files.forEach(function (file, i, files) {
|
||||
if (isImage(file.fileExst) || isSound(file.fileExst) || isVideo(file.fileExst)) {
|
||||
playlist.push(
|
||||
{
|
||||
id: id,
|
||||
@ -843,7 +951,7 @@ class SectionBodyContent extends React.Component {
|
||||
this.renderEmptyFilterContainer()
|
||||
) : (
|
||||
<>
|
||||
<CustomTooltip ref={this.tooltipRef}>{tooltipLabel}</CustomTooltip>
|
||||
<CustomTooltip ref={this.tooltipRef}>{tooltipLabel}</CustomTooltip>
|
||||
<RowContainer draggable useReactWindow={false}>
|
||||
{items.map((item) => {
|
||||
const isEdit =
|
||||
@ -859,13 +967,13 @@ class SectionBodyContent extends React.Component {
|
||||
const checked = isFileSelected(selection, item.id, item.parentId);
|
||||
const checkedProps = /* isAdmin(viewer) */ isEdit ? {} : { checked };
|
||||
const element = this.getItemIcon(item, isEdit);
|
||||
|
||||
|
||||
const selectedItem = selection.find(x => x.id === item.id && x.fileExst === item.fileExst);
|
||||
const isFolder = selectedItem ? false : item.fileExst ? false : true;
|
||||
const draggable = selectedItem && currentFolderType !== "Trash";
|
||||
let value = item.fileExst ? `file_${item.id}` : `folder_${item.id}`;
|
||||
value += draggable ? "_draggable" : "";
|
||||
const classNameProp = isFolder ? {className: " dropable"} : {};
|
||||
const classNameProp = isFolder && item.access < 2 ? { className: " dropable" } : {};
|
||||
|
||||
return (
|
||||
<DragAndDrop
|
||||
@ -873,7 +981,8 @@ class SectionBodyContent extends React.Component {
|
||||
onDrop={this.onDrop.bind(this, item)}
|
||||
onDragEnter={this.onDragEnter.bind(this, item)}
|
||||
onDragLeave={this.onDragLeave.bind(this, item)}
|
||||
dragging={dragging && isFolder}
|
||||
onMouseDown={this.onMouseDown}
|
||||
dragging={dragging && isFolder && item.access < 2}
|
||||
key={`dnd-key_${item.id}`}
|
||||
{...contextOptionsProps}
|
||||
value={value}
|
||||
@ -904,7 +1013,7 @@ class SectionBodyContent extends React.Component {
|
||||
</RowContainer>
|
||||
{playlist.length > 0 && this.state.mediaViewerVisible &&
|
||||
<MediaViewer
|
||||
currentFileId = {this.state.currentMediaFileId}
|
||||
currentFileId={this.state.currentMediaFileId}
|
||||
allowConvert={true} //TODO
|
||||
canDelete={(fileId) => { return true }} //TODO
|
||||
canDownload={(fileId) => { return true }} //TODO
|
||||
@ -914,8 +1023,8 @@ class SectionBodyContent extends React.Component {
|
||||
onDownload={this.onDownloadMediaFile}
|
||||
onClose={this.onMediaViewerClose}
|
||||
onEmptyPlaylistError={this.onMediaViewerClose}
|
||||
extsMediaPreviewed={[".aac", ".flac", ".m4a", ".mp3", ".oga", ".ogg", ".wav", ".f4v", ".m4v", ".mov", ".mp4", ".ogv", ".webm", ".avi", ".mpg", ".mpeg", ".wmv"]} //TODO
|
||||
extsImagePreviewed={[".bmp", ".gif", ".jpeg", ".jpg", ".png", ".ico", ".tif", ".tiff", ".webp"]} //TODO
|
||||
extsMediaPreviewed={extsMediaPreviewed} //TODO
|
||||
extsImagePreviewed={extsImagePreviewed} //TODO
|
||||
/>
|
||||
}
|
||||
{showSharingPanel && (
|
||||
@ -936,12 +1045,17 @@ SectionBodyContent.defaultProps = {
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { selectedFolder, treeFolders, selection } = state.files;
|
||||
const { id, title, foldersCount, filesCount } = selectedFolder;
|
||||
const { selectedFolder, treeFolders, selection, dragItem } = state.files;
|
||||
const { id, title, foldersCount, filesCount, pathParts } = selectedFolder;
|
||||
const currentFolderType = getFolderType(id, treeFolders);
|
||||
|
||||
const myFolderIndex = 0;
|
||||
const shareFolderIndex = 1;
|
||||
const commonFolderIndex = 2;
|
||||
const currentFolderCount = filesCount + foldersCount;
|
||||
const myDocumentsId = treeFolders[myFolderIndex].id;
|
||||
const shareFolderId = treeFolders[shareFolderIndex].id;
|
||||
const commonFolderId = treeFolders[commonFolderIndex].id;
|
||||
|
||||
return {
|
||||
fileAction: state.files.fileAction,
|
||||
@ -954,12 +1068,16 @@ const mapStateToProps = state => {
|
||||
selection,
|
||||
settings: state.auth.settings,
|
||||
viewer: state.auth.user,
|
||||
treeFolders: state.files.treeFolders,
|
||||
treeFolders,
|
||||
currentFolderType,
|
||||
title,
|
||||
myDocumentsId: treeFolders[myFolderIndex].id,
|
||||
myDocumentsId,
|
||||
currentFolderCount,
|
||||
selectedFolderId: id
|
||||
selectedFolderId: id,
|
||||
dragItem,
|
||||
isShare: pathParts[0] === shareFolderId,
|
||||
isCommon: pathParts[0] === commonFolderId,
|
||||
isAdmin: state.auth.user.isAdmin
|
||||
};
|
||||
};
|
||||
|
||||
@ -975,6 +1093,8 @@ export default connect(
|
||||
setAction,
|
||||
setTreeFolders,
|
||||
moveToFolder,
|
||||
getProgress
|
||||
copyToFolder,
|
||||
getProgress,
|
||||
setDragItem
|
||||
}
|
||||
)(withRouter(withTranslation()(SectionBodyContent)));
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import copy from "copy-to-clipboard";
|
||||
import styled, { css } from "styled-components";
|
||||
import { withRouter } from "react-router";
|
||||
import { constants, Headline, store, api } from "asc-web-common";
|
||||
@ -161,8 +162,14 @@ class SectionHeaderContent extends React.Component {
|
||||
];
|
||||
};
|
||||
|
||||
createLinkForPortalUsers = () =>
|
||||
toastr.info("createLinkForPortalUsers click");
|
||||
createLinkForPortalUsers = () => {
|
||||
const {currentFolderId} = this.props;
|
||||
const { t } = this.props;
|
||||
|
||||
copy(`${window.location.origin}/products/files/filter?folder=${currentFolderId}`);
|
||||
|
||||
toastr.success(t("LinkCopySuccess"));
|
||||
}
|
||||
|
||||
onMoveAction = () => this.setState({ showMoveToPanel: !this.state.showMoveToPanel });
|
||||
|
||||
@ -231,7 +238,7 @@ class SectionHeaderContent extends React.Component {
|
||||
key: "link-portal-users",
|
||||
label: t("LinkForPortalUsers"),
|
||||
onClick: this.createLinkForPortalUsers,
|
||||
disabled: true
|
||||
disabled: false
|
||||
},
|
||||
{ key: "separator-2", isSeparator: true },
|
||||
{
|
||||
|
@ -596,6 +596,7 @@ class PureHome extends React.Component {
|
||||
<ArticleBodyContent
|
||||
onLoading={this.onLoading}
|
||||
isLoading={isLoading}
|
||||
dragging={dragging}
|
||||
/>
|
||||
}
|
||||
sectionHeaderContent={
|
||||
|
@ -6,6 +6,7 @@
|
||||
"UploadToFolder": "Upload to folder",
|
||||
"SharingSettings": "Sharing settings",
|
||||
"LinkForPortalUsers": "Link for portal users",
|
||||
"LinkCopySuccess": "Link has been copied to the clipboard",
|
||||
"MoveTo": "Move to",
|
||||
"Copy": "Copy",
|
||||
"Download": "Download",
|
||||
@ -78,6 +79,8 @@
|
||||
"ErrorUploadMessage": "You cannot upload a folder or an empty file",
|
||||
"UploadingLabel": "Uploading files: {{file}} of {{totalFiles}}",
|
||||
"MoveToOperation": "Moving",
|
||||
"TooltipElementMessage": "Move {{element}}",
|
||||
"TooltipElementsMessage": "Move {{element}} elements"
|
||||
"TooltipElementMoveMessage": "Move {{element}}",
|
||||
"TooltipElementsMoveMessage": "Move {{element}} elements",
|
||||
"TooltipElementCopyMessage": "Copy {{element}}",
|
||||
"TooltipElementsCopyMessage": "Copy {{element}} elements"
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
"UploadToFolder": "Загрузить в папку",
|
||||
"SharingSettings": "Настройки доступа",
|
||||
"LinkForPortalUsers": "Ссылка для пользователей портала",
|
||||
"LinkCopySuccess": "Ссылка скопирована в буфер обмена",
|
||||
"MoveTo": "Переместить",
|
||||
"Copy": "Копировать",
|
||||
"Download": "Скачать",
|
||||
@ -78,6 +79,8 @@
|
||||
"ErrorUploadMessage": "Нельзя загрузить папку или пустой файл",
|
||||
"UploadingLabel": "Загружено файлов: {{file}} из {{totalFiles}}",
|
||||
"MoveToOperation": "Перемещение",
|
||||
"TooltipElementMessage": "Переместить {{element}}",
|
||||
"TooltipElementsMessage": "Переместить {{element}} элемента(ов)"
|
||||
"TooltipElementMoveMessage": "Переместить {{element}}",
|
||||
"TooltipElementsMoveMessage": "Переместить {{element}} элемента(ов)",
|
||||
"TooltipElementCopyMessage": "Скопировать {{element}}",
|
||||
"TooltipElementsCopyMessage": "Скопировать {{element}} элемента(ов)"
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Row, RowContainer, Link, Text, Box } from "asc-web-components";
|
||||
|
||||
//import i18n from '../../i18n';
|
||||
|
||||
const VersionBadge = (props) => (
|
||||
<svg width="55" height="18" viewBox="0 0 55 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 1C0 0.447716 0.447715 0 1 0L53.9994 0C54.6787 0 55.1603 0.662806 54.9505 1.3089L52.5529 8.6911C52.4877 8.89187 52.4877 9.10813 52.5529 9.3089L54.9505 16.6911C55.1603 17.3372 54.6787 18 53.9994 18H0.999999C0.447714 18 0 17.5523 0 17V1Z" fill="#A3A9AE"/>
|
||||
</svg>);
|
||||
|
||||
class SectionBodyContent extends React.PureComponent {
|
||||
renderRow = info => {
|
||||
const title = `${new Date(info.created).toLocaleString(this.props.culture)} ${info.createdBy.displayName}`;
|
||||
return (
|
||||
<Row key={info.id}>
|
||||
<Box marginProp="0 8px" displayProp="flex">
|
||||
<VersionBadge />
|
||||
<Text style={{position: "absolute", left: "16px"}} color="#FFFFFF" isBold fontSize="12px">Ver.{info.version}</Text>
|
||||
</Box>
|
||||
<Link fontWeight={600} fontSize="14px" title={title}>{title}</Link>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
render() {
|
||||
const { versions } = this.props;
|
||||
console.log("VersionHistory SectionBodyContent render()", versions);
|
||||
return <RowContainer useReactWindow={false}>{versions.map(this.renderRow)}</RowContainer>;
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(withTranslation()(SectionBodyContent));
|
@ -0,0 +1,99 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { withRouter } from "react-router";
|
||||
import { Headline } from 'asc-web-common';
|
||||
import { IconButton } from "asc-web-components";
|
||||
import { connect } from "react-redux";
|
||||
import { withTranslation } from "react-i18next";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.arrow-button {
|
||||
margin-right: 16px;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
padding: 8px 0 8px 8px;
|
||||
margin-left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
${props => props.isHeaderVisible && css`width: calc(100% + 76px);`}
|
||||
}
|
||||
|
||||
.group-button-menu-container {
|
||||
margin: 0 -16px;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
& > div:first-child {
|
||||
position: absolute;
|
||||
top: 56px;
|
||||
z-index: 180;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
margin: 0 -24px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-container {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: calc(100vw - 32px);
|
||||
|
||||
.action-button {
|
||||
margin-left: 16px;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
margin-left: auto;
|
||||
|
||||
& > div:first-child {
|
||||
padding: 8px 16px 8px 16px;
|
||||
margin-right: -16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const SectionHeaderContent = props => {
|
||||
|
||||
const { title } = props;
|
||||
|
||||
const onClickBack = () => {
|
||||
const { history, settings } = props;
|
||||
history.push(settings.homepage);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledContainer isHeaderVisible={true}>
|
||||
<IconButton
|
||||
iconName="ArrowPathIcon"
|
||||
size="17"
|
||||
color="#A3A9AE"
|
||||
hoverColor="#657077"
|
||||
isFill={true}
|
||||
onClick={onClickBack}
|
||||
className="arrow-button"
|
||||
/>
|
||||
<Headline className='headline-header' type="content" truncate={true}>{title}</Headline>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
settings: state.auth.settings,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps
|
||||
)(withTranslation()(withRouter(SectionHeaderContent)));
|
@ -0,0 +1,2 @@
|
||||
export { default as SectionHeaderContent } from './Header';
|
||||
export { default as SectionBodyContent } from './Body';
|
@ -0,0 +1,54 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import config from "../../../../package.json";
|
||||
import { constants } from 'asc-web-common';
|
||||
const { LANGUAGE } = constants;
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
newInstance
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: localStorage.getItem(LANGUAGE) || 'en',
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/Home/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
},
|
||||
ru: {
|
||||
translation: require("./locales/ru/translation.json")
|
||||
}
|
||||
};
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: localStorage.getItem(LANGUAGE) || 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default newInstance;
|
@ -0,0 +1,141 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
import { RequestLoader, Loader } from "asc-web-components";
|
||||
import { PageLayout, utils, api } from "asc-web-common";
|
||||
import { withTranslation, I18nextProvider } from "react-i18next";
|
||||
import i18n from "./i18n";
|
||||
|
||||
import {
|
||||
ArticleHeaderContent,
|
||||
ArticleBodyContent,
|
||||
ArticleMainButtonContent
|
||||
} from "../../Article";
|
||||
import { SectionHeaderContent, SectionBodyContent } from "./Section";
|
||||
|
||||
const { changeLanguage } = utils;
|
||||
|
||||
class PureVersionHistory extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { files, match } = props;
|
||||
const { fileId } = match.params;
|
||||
const found = files.filter(f => f.id == fileId);
|
||||
|
||||
this.state = {
|
||||
isLoading: false,
|
||||
fileId: props.match.params.fileId,
|
||||
file: found && found[0],
|
||||
versions: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { match, t } = this.props;
|
||||
const { fileId } = match.params;
|
||||
|
||||
//document.title = `${t("GroupAction")} – ${t("People")}`;
|
||||
|
||||
if (fileId) {
|
||||
//fetchGroup(fileId);
|
||||
api.files.getFileVersionInfo(fileId)
|
||||
.then((versions) => {
|
||||
console.log("getFileVersionInfo result", versions);
|
||||
this.setState({
|
||||
versions
|
||||
})
|
||||
})
|
||||
|
||||
console.log("Loading file versions", fileId);
|
||||
}
|
||||
}
|
||||
|
||||
onLoading = status => {
|
||||
this.setState({ isLoading: status });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { file, versions } = this.state;
|
||||
const { t, settings } = this.props;
|
||||
|
||||
console.log(`FileId ${file.id}`);
|
||||
|
||||
return (
|
||||
<>
|
||||
<RequestLoader
|
||||
visible={this.state.isLoading}
|
||||
zIndex={256}
|
||||
loaderSize="16px"
|
||||
loaderColor={"#999"}
|
||||
label={`${t("LoadingProcessing")} ${t("LoadingDescription")}`}
|
||||
fontSize="12px"
|
||||
fontColor={"#999"}
|
||||
/>
|
||||
{versions ? (
|
||||
<PageLayout
|
||||
withBodyScroll={true}
|
||||
withBodyAutoFocus={true}
|
||||
articleHeaderContent={<ArticleHeaderContent />}
|
||||
articleMainButtonContent={
|
||||
<ArticleMainButtonContent
|
||||
onLoading={this.onLoading}
|
||||
startUpload={this.startUpload}
|
||||
setProgressVisible={this.setProgressVisible}
|
||||
setProgressValue={this.setProgressValue}
|
||||
setProgressLabel={this.setProgressLabel}
|
||||
/>
|
||||
}
|
||||
articleBodyContent={
|
||||
<ArticleBodyContent
|
||||
onLoading={this.onLoading}
|
||||
isLoading={this.state.isLoading}
|
||||
/>
|
||||
}
|
||||
sectionHeaderContent={<SectionHeaderContent title={file.title} />}
|
||||
sectionBodyContent={
|
||||
<SectionBodyContent onLoading={this.onLoading} versions={versions} culture={settings.culture} />
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<PageLayout
|
||||
articleHeaderContent={<ArticleHeaderContent />}
|
||||
articleMainButtonContent={<ArticleMainButtonContent />}
|
||||
articleBodyContent={<ArticleBodyContent />}
|
||||
sectionBodyContent={
|
||||
<Loader className="pageLoader" type="rombs" size="40px" />
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const VersionHistoryContainer = withTranslation()(PureVersionHistory);
|
||||
|
||||
const VersionHistory = props => {
|
||||
changeLanguage(i18n);
|
||||
return (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<VersionHistoryContainer {...props} />
|
||||
</I18nextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
VersionHistory.propTypes = {
|
||||
files: PropTypes.array,
|
||||
history: PropTypes.object.isRequired,
|
||||
isLoaded: PropTypes.bool
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
files: state.files.files,
|
||||
settings: state.auth.settings,
|
||||
isLoaded: state.auth.isLoaded
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(withRouter(VersionHistory));
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import config from "../../../../package.json";
|
||||
import { constants } from 'asc-web-common';
|
||||
const { LANGUAGE } = constants;
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
newInstance
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: localStorage.getItem(LANGUAGE) || 'en',
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/NewFilesPanel/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
},
|
||||
ru: {
|
||||
translation: require("./locales/ru/translation.json")
|
||||
},
|
||||
};
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: localStorage.getItem(LANGUAGE) || 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default newInstance;
|
@ -0,0 +1,137 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router";
|
||||
import {
|
||||
Backdrop,
|
||||
Heading,
|
||||
Aside,
|
||||
Row,
|
||||
RowContent,
|
||||
RowContainer,
|
||||
Text,
|
||||
Link,
|
||||
Button
|
||||
} from "asc-web-components";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { utils as commonUtils } from "asc-web-common";
|
||||
import i18n from "./i18n";
|
||||
import { ReactSVG } from 'react-svg'
|
||||
import {
|
||||
StyledAsidePanel,
|
||||
StyledContent,
|
||||
StyledHeaderContent,
|
||||
StyledBody,
|
||||
StyledFooter
|
||||
} from "../StyledPanels";
|
||||
import { getFileIcon, getFolderIcon } from '../../../store/files/selectors';
|
||||
|
||||
const { changeLanguage } = commonUtils;
|
||||
|
||||
class NewFilesPanelComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
changeLanguage(i18n);
|
||||
}
|
||||
|
||||
getItemIcon = (item, isEdit) => {
|
||||
const extension = item.fileExst;
|
||||
const icon = extension
|
||||
? getFileIcon(extension, 24)
|
||||
: getFolderIcon(item.providerKey, 24);
|
||||
|
||||
return <ReactSVG
|
||||
beforeInjection={svg => {
|
||||
svg.setAttribute('style', 'margin-top: 4px');
|
||||
isEdit && svg.setAttribute('style', 'margin-left: 24px');
|
||||
}}
|
||||
src={icon}
|
||||
loading={this.svgLoader}
|
||||
/>;
|
||||
};
|
||||
|
||||
render() {
|
||||
//console.log("NewFiles panel render");
|
||||
const { t, visible, onClose, files } = this.props;
|
||||
const zIndex = 310;
|
||||
|
||||
return (
|
||||
<StyledAsidePanel visible={visible}>
|
||||
<Backdrop onClick={onClose} visible={visible} zIndex={zIndex} />
|
||||
<Aside className="header_aside-panel" visible={visible}>
|
||||
<StyledContent>
|
||||
<StyledHeaderContent className="files-operations-panel">
|
||||
<Heading size="medium" truncate>
|
||||
{t("NewFiles")}
|
||||
</Heading>
|
||||
</StyledHeaderContent>
|
||||
<StyledBody className="files-operations-body">
|
||||
<RowContainer useReactWindow manualHeight="83vh">
|
||||
{files.map((file) => {
|
||||
const element = this.getItemIcon(file);
|
||||
return (
|
||||
<Row key={file.id} element={element}>
|
||||
<RowContent>
|
||||
<Link
|
||||
containerWidth="100%"
|
||||
type="page"
|
||||
fontWeight="bold"
|
||||
color="#333"
|
||||
isTextOverflow
|
||||
truncate
|
||||
title={file.title}
|
||||
fontSize="14px"
|
||||
>
|
||||
{file.title}
|
||||
</Link>
|
||||
|
||||
<></>
|
||||
<Text fontSize="12px" containerWidth="auto">
|
||||
{file.checked && t("ConvertInto")}
|
||||
</Text>
|
||||
</RowContent>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</RowContainer>
|
||||
</StyledBody>
|
||||
<StyledFooter>
|
||||
<Button
|
||||
label={t("MarkAsRead")}
|
||||
size="big"
|
||||
primary
|
||||
//onClick={this.onSaveClick}
|
||||
/>
|
||||
<Button
|
||||
className="sharing_panel-button"
|
||||
label={t("CloseButton")}
|
||||
size="big"
|
||||
onClick={onClose}
|
||||
/>
|
||||
</StyledFooter>
|
||||
</StyledContent>
|
||||
</Aside>
|
||||
</StyledAsidePanel>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NewFilesPanelComponent.propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
visible: PropTypes.bool,
|
||||
};
|
||||
|
||||
const NewFilesPanelContainerTranslated = withTranslation()(
|
||||
NewFilesPanelComponent
|
||||
);
|
||||
|
||||
const NewFilesPanel = (props) => (
|
||||
<NewFilesPanelContainerTranslated i18n={i18n} {...props} />
|
||||
);
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, {})(withRouter(NewFilesPanel));
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"NewFiles": "New files",
|
||||
"MarkAsRead": "Mark all as read",
|
||||
"CloseButton": "Close"
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"NewFiles": "Новые файлы",
|
||||
"MarkAsRead": "Пометить всё прочтённым",
|
||||
"CloseButton": "Закрыть"
|
||||
}
|
@ -601,7 +601,7 @@ class SharingPanelComponent extends React.Component {
|
||||
/>*/}
|
||||
</div>
|
||||
</StyledSharingHeaderContent>
|
||||
<StyledSharingBody>
|
||||
<StyledSharingBody stype="mediumBlack" style={{height: '83vh'}}>
|
||||
{shareDataItems.map((item, index) => (
|
||||
<SharingRow
|
||||
key={index}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { Scrollbar } from "asc-web-components";
|
||||
|
||||
const PanelStyles = css`
|
||||
.panel_combo-box {
|
||||
@ -181,10 +182,21 @@ const StyledSharingHeaderContent = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSharingBody = styled.div`
|
||||
const StyledSharingBody = styled(Scrollbar)`
|
||||
position: relative;
|
||||
padding: 16px 0;
|
||||
|
||||
.nav-thumb-vertical {
|
||||
opacity: 0;
|
||||
transition: opacity 200ms ease;
|
||||
}
|
||||
|
||||
:hover {
|
||||
.nav-thumb-vertical {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.sharing_panel-text {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
@ -3,5 +3,6 @@ import AddUsersPanel from "./AddUsersPanel/AddUsersPanel";
|
||||
import AddGroupsPanel from "./AddGroupsPanel/AddGroupsPanel";
|
||||
import EmbeddingPanel from "./EmbeddingPanel/EmbeddingPanel";
|
||||
import OperationsPanel from "./OperationsPanel";
|
||||
import NewFilesPanel from "./NewFilesPanel";
|
||||
|
||||
export { SharingPanel, AddUsersPanel, AddGroupsPanel, EmbeddingPanel, OperationsPanel }
|
||||
export { SharingPanel, AddUsersPanel, AddGroupsPanel, EmbeddingPanel, OperationsPanel, NewFilesPanel }
|
@ -7,4 +7,5 @@ export const SORT_BY = "sortby";
|
||||
export const SORT_ORDER = "sortorder";
|
||||
export const PAGE = "page";
|
||||
export const PAGE_COUNT = "pagecount";
|
||||
export const FOLDER = "folder";
|
||||
export const FOLDER = "folder";
|
||||
export const PREVIEW = "preview"
|
@ -1,5 +1,6 @@
|
||||
import { api, history } from "asc-web-common";
|
||||
import axios from "axios";
|
||||
import queryString from 'query-string';
|
||||
import {
|
||||
AUTHOR_TYPE,
|
||||
FILTER_TYPE,
|
||||
@ -9,7 +10,8 @@ import {
|
||||
SEARCH,
|
||||
SORT_BY,
|
||||
SORT_ORDER,
|
||||
FOLDER
|
||||
FOLDER,
|
||||
PREVIEW
|
||||
} from "../../helpers/constants";
|
||||
import config from "../../../package.json";
|
||||
import { getTreeFolders } from "./selectors";
|
||||
@ -29,6 +31,7 @@ export const SET_FILTER = "SET_FILTER";
|
||||
export const SELECT_FILE = "SELECT_FILE";
|
||||
export const DESELECT_FILE = "DESELECT_FILE";
|
||||
export const SET_ACTION = "SET_ACTION";
|
||||
export const SET_DRAG_ITEM = "SET_DRAG_ITEM";
|
||||
|
||||
export function setFile(file) {
|
||||
return {
|
||||
@ -93,6 +96,13 @@ export function setTreeFolders(treeFolders) {
|
||||
};
|
||||
}
|
||||
|
||||
export function setDragItem(dragItem) {
|
||||
return {
|
||||
type: SET_DRAG_ITEM,
|
||||
dragItem
|
||||
}
|
||||
}
|
||||
|
||||
export function setFilesFilter(filter) {
|
||||
setFilterUrl(filter);
|
||||
return {
|
||||
@ -124,6 +134,7 @@ export function deselectFile(file) {
|
||||
export function setFilterUrl(filter) {
|
||||
const defaultFilter = FilesFilter.getDefault();
|
||||
const params = [];
|
||||
const URLParams = queryString.parse(window.location.href)
|
||||
|
||||
if (filter.filterType) {
|
||||
params.push(`${FILTER_TYPE}=${filter.filterType}`);
|
||||
@ -147,6 +158,10 @@ export function setFilterUrl(filter) {
|
||||
params.push(`${PAGE_COUNT}=${filter.pageCount}`);
|
||||
}
|
||||
|
||||
if(URLParams.preview) {
|
||||
params.push(`${PREVIEW}=${URLParams.preview}`);
|
||||
}
|
||||
|
||||
params.push(`${PAGE}=${filter.page + 1}`);
|
||||
params.push(`${SORT_BY}=${filter.sortBy}`);
|
||||
params.push(`${SORT_ORDER}=${filter.sortOrder}`);
|
||||
|
@ -11,7 +11,8 @@ import {
|
||||
SET_SELECTED,
|
||||
SET_SELECTION,
|
||||
SELECT_FILE,
|
||||
DESELECT_FILE
|
||||
DESELECT_FILE,
|
||||
SET_DRAG_ITEM
|
||||
} from "./actions";
|
||||
import { api } from "asc-web-common";
|
||||
import { isFileSelected, skipFile, getFilesBySelected } from "./selectors";
|
||||
@ -27,7 +28,8 @@ const initialState = {
|
||||
treeFolders: [],
|
||||
selected: "none",
|
||||
selectedFolder: null,
|
||||
selection: []
|
||||
selection: [],
|
||||
dragItem: null
|
||||
};
|
||||
|
||||
const filesReducer = (state = initialState, action) => {
|
||||
@ -93,6 +95,10 @@ const filesReducer = (state = initialState, action) => {
|
||||
return Object.assign({}, state, {
|
||||
fileAction: action.fileAction
|
||||
})
|
||||
case SET_DRAG_ITEM:
|
||||
return Object.assign({}, state, {
|
||||
dragItem: action.dragItem
|
||||
});
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Web.Files.Services.DocumentService;
|
||||
|
||||
namespace ASC.Files.Core
|
||||
@ -303,6 +304,10 @@ namespace ASC.Files.Core
|
||||
|
||||
bool ContainChanges(T fileId, int fileVersion);
|
||||
|
||||
IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to);
|
||||
|
||||
IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -28,6 +28,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
using ASC.Files.Core.Security;
|
||||
|
||||
namespace ASC.Files.Core
|
||||
{
|
||||
public interface IFolderDao<T>
|
||||
@ -292,6 +294,11 @@ namespace ASC.Files.Core
|
||||
/// <returns></returns>
|
||||
Dictionary<string, string> GetBunchObjectIDs(List<T> folderIDs);
|
||||
|
||||
|
||||
IEnumerable<(Folder<T>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to);
|
||||
|
||||
IEnumerable<T> GetTenantsWithFeeds(DateTime fromTime);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ using ASC.Core.Common.Settings;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.ElasticSearch;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Files.Thirdparty.ProviderDao;
|
||||
@ -1204,6 +1205,51 @@ namespace ASC.Files.Core.Data
|
||||
.Any();
|
||||
}
|
||||
|
||||
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
var q1 = FilesDbContext.Files
|
||||
.Where(r => r.TenantId == tenant)
|
||||
.Where(r => r.CurrentVersion)
|
||||
.Where(r => r.ModifiedOn >= from && r.ModifiedOn <= to);
|
||||
|
||||
var q2 = FromQuery(q1)
|
||||
.Select(r => new DbFileQueryWithSecurity() { DbFileQuery = r, Security = null });
|
||||
|
||||
var q3 = FilesDbContext.Files
|
||||
.Where(r => r.TenantId == tenant)
|
||||
.Where(r => r.CurrentVersion);
|
||||
|
||||
var q4 = FromQuery(q3)
|
||||
.Join(FilesDbContext.Security.DefaultIfEmpty(), r => r.file.Id.ToString(), s => s.EntryId, (f, s) => new DbFileQueryWithSecurity { DbFileQuery = f, Security = s })
|
||||
.Where(r => r.Security.TenantId == tenant)
|
||||
.Where(r => r.Security.EntryType == FileEntryType.File)
|
||||
.Where(r => r.Security.Security == Security.FileShare.Restrict)
|
||||
.Where(r => r.Security.TimeStamp >= from && r.Security.TimeStamp <= to);
|
||||
|
||||
return q2.Select(ToFileWithShare).ToList().Union(q4.Select(ToFileWithShare).ToList());
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
var q1 = FilesDbContext.Files
|
||||
.Where(r => r.ModifiedOn > fromTime)
|
||||
.Select(r => r.TenantId)
|
||||
.GroupBy(r => r)
|
||||
.Where(r => r.Count() > 0)
|
||||
.Select(r => r.Key)
|
||||
.ToList();
|
||||
|
||||
var q2 = FilesDbContext.Security
|
||||
.Where(r => r.TimeStamp > fromTime)
|
||||
.Select(r => r.TenantId)
|
||||
.GroupBy(r => r)
|
||||
.Where(r => r.Count() > 0)
|
||||
.Select(r => r.Key)
|
||||
.ToList();
|
||||
|
||||
return q1.Union(q2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Func<Selector<DbFile>, Selector<DbFile>> GetFuncForSearch(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false)
|
||||
@ -1341,6 +1387,21 @@ namespace ASC.Files.Core.Data
|
||||
return file;
|
||||
}
|
||||
|
||||
public (File<int>, SmallShareRecord) ToFileWithShare(DbFileQueryWithSecurity r)
|
||||
{
|
||||
var file = ToFile(r.DbFileQuery);
|
||||
var record = r.Security != null
|
||||
? new SmallShareRecord
|
||||
{
|
||||
ShareOn = r.Security.TimeStamp,
|
||||
ShareBy = r.Security.Owner,
|
||||
ShareTo = r.Security.Subject
|
||||
}
|
||||
: null;
|
||||
|
||||
return (file, record);
|
||||
}
|
||||
|
||||
internal protected DbFile InitDocument(DbFile dbFile)
|
||||
{
|
||||
if (!FactoryIndexer.CanSearchByContent())
|
||||
@ -1379,6 +1440,12 @@ namespace ASC.Files.Core.Data
|
||||
public bool shared { get; set; }
|
||||
}
|
||||
|
||||
public class DbFileQueryWithSecurity
|
||||
{
|
||||
public DbFileQuery DbFileQuery { get; set; }
|
||||
public DbFilesSecurity Security { get; set; }
|
||||
}
|
||||
|
||||
public static class FileDaoExtention
|
||||
{
|
||||
public static DIHelper AddFileDaoService(this DIHelper services)
|
||||
|
@ -37,6 +37,7 @@ using ASC.Core.Common.Settings;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.ElasticSearch;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Files.Thirdparty.ProviderDao;
|
||||
@ -1081,6 +1082,21 @@ namespace ASC.Files.Core.Data
|
||||
return result;
|
||||
}
|
||||
|
||||
public (Folder<int>, SmallShareRecord) ToFolderWithShare(DbFolderQueryWithSecurity r)
|
||||
{
|
||||
var file = ToFolder(r.DbFolderQuery);
|
||||
var record = r.Security != null
|
||||
? new SmallShareRecord
|
||||
{
|
||||
ShareOn = r.Security.TimeStamp,
|
||||
ShareBy = r.Security.Owner,
|
||||
ShareTo = r.Security.Subject
|
||||
}
|
||||
: null;
|
||||
|
||||
return (file, record);
|
||||
}
|
||||
|
||||
public string GetBunchObjectID(int folderID)
|
||||
{
|
||||
return Query(FilesDbContext.BunchObjects)
|
||||
@ -1096,6 +1112,51 @@ namespace ASC.Files.Core.Data
|
||||
.ToDictionary(r => r.LeftNode, r => r.RightNode);
|
||||
}
|
||||
|
||||
public IEnumerable<(Folder<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
var q1 = FilesDbContext.Folders
|
||||
.Where(r => r.TenantId == tenant)
|
||||
.Where(r => r.FolderType == FolderType.DEFAULT)
|
||||
.Where(r => r.CreateOn >= from && r.ModifiedOn <= to);
|
||||
|
||||
var q2 = FromQuery(q1)
|
||||
.Select(r => new DbFolderQueryWithSecurity() { DbFolderQuery = r, Security = null });
|
||||
|
||||
var q3 = FilesDbContext.Folders
|
||||
.Where(r => r.TenantId == tenant)
|
||||
.Where(r => r.FolderType == FolderType.DEFAULT);
|
||||
|
||||
var q4 = FromQuery(q3)
|
||||
.Join(FilesDbContext.Security.DefaultIfEmpty(), r => r.folder.Id.ToString(), s => s.EntryId, (f, s) => new DbFolderQueryWithSecurity { DbFolderQuery = f, Security = s })
|
||||
.Where(r => r.Security.TenantId == tenant)
|
||||
.Where(r => r.Security.EntryType == FileEntryType.Folder)
|
||||
.Where(r => r.Security.Security == FileShare.Restrict)
|
||||
.Where(r => r.Security.TimeStamp >= from && r.Security.TimeStamp <= to);
|
||||
|
||||
return q2.Select(ToFolderWithShare).ToList().Union(q4.Select(ToFolderWithShare).ToList());
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
var q1 = FilesDbContext.Files
|
||||
.Where(r => r.ModifiedOn > fromTime)
|
||||
.Select(r => r.TenantId)
|
||||
.GroupBy(r => r)
|
||||
.Where(r => r.Count() > 0)
|
||||
.Select(r => r.Key)
|
||||
.ToList();
|
||||
|
||||
var q2 = FilesDbContext.Security
|
||||
.Where(r => r.TimeStamp > fromTime)
|
||||
.Select(r => r.TenantId)
|
||||
.GroupBy(r => r)
|
||||
.Where(r => r.Count() > 0)
|
||||
.Select(r => r.Key)
|
||||
.ToList();
|
||||
|
||||
return q1.Union(q2);
|
||||
}
|
||||
|
||||
private string GetProjectTitle(object folderID)
|
||||
{
|
||||
return "";
|
||||
@ -1159,6 +1220,12 @@ namespace ASC.Files.Core.Data
|
||||
public bool shared { get; set; }
|
||||
}
|
||||
|
||||
public class DbFolderQueryWithSecurity
|
||||
{
|
||||
public DbFolderQuery DbFolderQuery { get; set; }
|
||||
public DbFilesSecurity Security { get; set; }
|
||||
}
|
||||
|
||||
public static class FolderDaoExtention
|
||||
{
|
||||
public static DIHelper AddFolderDaoService(this DIHelper services)
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Web.Core.Files;
|
||||
@ -538,7 +539,7 @@ namespace ASC.Files.Thirdparty.Box
|
||||
if (uploadSession.BytesUploaded == uploadSession.BytesTotal)
|
||||
{
|
||||
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"),
|
||||
FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
{
|
||||
uploadSession.File = SaveFile(uploadSession.File, fs);
|
||||
}
|
||||
@ -606,6 +607,16 @@ namespace ASC.Files.Thirdparty.Box
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Web.Core.Files;
|
||||
using ASC.Web.Studio.Core;
|
||||
@ -510,6 +511,16 @@ namespace ASC.Files.Thirdparty.Box
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Web.Core.Files;
|
||||
@ -590,7 +591,7 @@ namespace ASC.Files.Thirdparty.Dropbox
|
||||
}
|
||||
|
||||
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"),
|
||||
FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
{
|
||||
return SaveFile(uploadSession.File, fs);
|
||||
}
|
||||
@ -653,6 +654,16 @@ namespace ASC.Files.Thirdparty.Dropbox
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Web.Core.Files;
|
||||
using ASC.Web.Studio.Core;
|
||||
@ -507,6 +508,16 @@ namespace ASC.Files.Thirdparty.Dropbox
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Web.Core.Files;
|
||||
@ -582,7 +583,7 @@ namespace ASC.Files.Thirdparty.GoogleDrive
|
||||
return ToFile(GetDriveEntry(googleDriveSession.FileId));
|
||||
}
|
||||
|
||||
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
{
|
||||
return SaveFile(uploadSession.File, fs);
|
||||
}
|
||||
@ -654,6 +655,16 @@ namespace ASC.Files.Thirdparty.GoogleDrive
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Web.Core.Files;
|
||||
using ASC.Web.Studio.Core;
|
||||
@ -499,6 +500,16 @@ namespace ASC.Files.Thirdparty.GoogleDrive
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Web.Core.Files;
|
||||
@ -581,7 +582,7 @@ namespace ASC.Files.Thirdparty.OneDrive
|
||||
return ToFile(GetOneDriveItem(oneDriveSession.FileId));
|
||||
}
|
||||
|
||||
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
{
|
||||
return SaveFile(uploadSession.File, fs);
|
||||
}
|
||||
@ -655,6 +656,16 @@ namespace ASC.Files.Thirdparty.OneDrive
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Web.Core.Files;
|
||||
using ASC.Web.Studio.Core;
|
||||
@ -510,6 +511,16 @@ namespace ASC.Files.Thirdparty.OneDrive
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ using ASC.Common;
|
||||
using ASC.Core;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.Data;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Web.Files.Services.DocumentService;
|
||||
|
||||
@ -509,6 +510,16 @@ namespace ASC.Files.Thirdparty.ProviderDao
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ using ASC.Common;
|
||||
using ASC.Core;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.Data;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
|
||||
namespace ASC.Files.Thirdparty.ProviderDao
|
||||
@ -411,6 +412,16 @@ filterType, subjectGroup, subjectID, searchText, searchSubfolders, checkShare);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Web.Core.Files;
|
||||
@ -484,6 +485,16 @@ namespace ASC.Files.Thirdparty.SharePoint
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Web.Core.Files;
|
||||
using ASC.Web.Studio.Core;
|
||||
@ -466,6 +467,16 @@ namespace ASC.Files.Thirdparty.SharePoint
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Web.Core.Files;
|
||||
@ -261,7 +262,7 @@ namespace ASC.Files.Thirdparty.Sharpbox
|
||||
{
|
||||
if (!fileStream.CanSeek)
|
||||
{
|
||||
var tempBuffer = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, 8096, FileOptions.DeleteOnClose);
|
||||
var tempBuffer = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite, System.IO.FileShare.Read, 8096, FileOptions.DeleteOnClose);
|
||||
|
||||
fileStream.CopyTo(tempBuffer);
|
||||
tempBuffer.Flush();
|
||||
@ -625,7 +626,7 @@ namespace ASC.Files.Thirdparty.Sharpbox
|
||||
return ToFile(GetFileById(sharpboxSession.FileId));
|
||||
}
|
||||
|
||||
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
|
||||
{
|
||||
return SaveFile(uploadSession.File, fs);
|
||||
}
|
||||
@ -710,6 +711,16 @@ namespace ASC.Files.Thirdparty.Sharpbox
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Files.Core.Thirdparty;
|
||||
using ASC.Files.Resources;
|
||||
using ASC.Web.Core.Files;
|
||||
@ -530,6 +531,16 @@ namespace ASC.Files.Thirdparty.Sharpbox
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
<ProjectReference Include="..\..\..\common\ASC.Common\ASC.Common.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\ASC.Core.Common\ASC.Core.Common.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\services\ASC.ElasticSearch\ASC.ElasticSearch.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\services\ASC.Feed.Aggregator\ASC.Feed.Aggregator.csproj" />
|
||||
<ProjectReference Include="..\Server\ASC.Files.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
194
products/ASC.Files/Service/Core/FilesModule.cs
Normal file
194
products/ASC.Files/Service/Core/FilesModule.cs
Normal file
@ -0,0 +1,194 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using ASC.Core;
|
||||
using ASC.Feed;
|
||||
using ASC.Feed.Data;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Web.Core;
|
||||
using ASC.Web.Core.Files;
|
||||
|
||||
using FeedModule = ASC.Feed.Aggregator.Modules.FeedModule;
|
||||
|
||||
namespace ASC.Files.Service.Core
|
||||
{
|
||||
public class FilesModule : FeedModule
|
||||
{
|
||||
private const string fileItem = "file";
|
||||
private const string sharedFileItem = "sharedFile";
|
||||
|
||||
public override string Name => Constants.FilesModule;
|
||||
|
||||
public override string Product => "documents";
|
||||
|
||||
public override Guid ProductID => WebItemManager.DocumentsProductID;
|
||||
|
||||
protected override string DbId => "files";
|
||||
|
||||
public IFileDao<int> FileDao { get; }
|
||||
public IFolderDao<int> FolderDao { get; }
|
||||
public UserManager UserManager { get; }
|
||||
public FilesLinkUtility FilesLinkUtility { get; }
|
||||
public FileSecurity FileSecurity { get; }
|
||||
|
||||
public FilesModule(
|
||||
TenantManager tenantManager,
|
||||
UserManager userManager,
|
||||
WebItemSecurity webItemSecurity,
|
||||
FilesLinkUtility filesLinkUtility,
|
||||
FileSecurity fileSecurity,
|
||||
IDaoFactory daoFactory)
|
||||
: base(tenantManager, webItemSecurity)
|
||||
{
|
||||
FileDao = daoFactory.GetFileDao<int>();
|
||||
FolderDao = daoFactory.GetFolderDao<int>();
|
||||
UserManager = userManager;
|
||||
FilesLinkUtility = filesLinkUtility;
|
||||
FileSecurity = fileSecurity;
|
||||
}
|
||||
|
||||
public override bool VisibleFor(Feed.Aggregator.Feed feed, object data, Guid userId)
|
||||
{
|
||||
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return false;
|
||||
|
||||
var tuple = ((File<int>, SmallShareRecord))data;
|
||||
var file = tuple.Item1;
|
||||
var shareRecord = tuple.Item2;
|
||||
|
||||
bool targetCond;
|
||||
if (feed.Target != null)
|
||||
{
|
||||
if (shareRecord != null && shareRecord.ShareBy == userId) return false;
|
||||
|
||||
var owner = (Guid)feed.Target;
|
||||
var groupUsers = UserManager.GetUsersByGroup(owner).Select(x => x.ID).ToList();
|
||||
if (!groupUsers.Any())
|
||||
{
|
||||
groupUsers.Add(owner);
|
||||
}
|
||||
targetCond = groupUsers.Contains(userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetCond = true;
|
||||
}
|
||||
|
||||
return targetCond && FileSecurity.CanRead(file, userId);
|
||||
}
|
||||
|
||||
public override void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId)
|
||||
{
|
||||
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return;
|
||||
|
||||
var feed1 = feed.Select(r =>
|
||||
{
|
||||
var tuple = ((File<int>, SmallShareRecord))r.Item2;
|
||||
return new Tuple<FeedRow, File<int>, SmallShareRecord>(r.Item1, tuple.Item1, tuple.Item2);
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var files = feed1.Where(r => r.Item1.Feed.Target == null).Select(r => r.Item2).ToList();
|
||||
|
||||
foreach (var f in feed1.Where(r => r.Item1.Feed.Target != null && !(r.Item3 != null && r.Item3.ShareBy == userId)))
|
||||
{
|
||||
var file = f.Item2;
|
||||
if (IsTarget(f.Item1.Feed.Target, userId) && !files.Any(r => r.UniqID.Equals(file.UniqID)))
|
||||
{
|
||||
files.Add(file);
|
||||
}
|
||||
}
|
||||
|
||||
var canRead = FileSecurity.CanRead(files, userId).Where(r => r.Item2).ToList();
|
||||
|
||||
foreach (var f in feed1)
|
||||
{
|
||||
if (IsTarget(f.Item1.Feed.Target, userId) && canRead.Any(r => r.Item1.ID.Equals(f.Item2.ID)))
|
||||
{
|
||||
f.Item1.Users.Add(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Tuple<Feed.Aggregator.Feed, object>> GetFeeds(FeedFilter filter)
|
||||
{
|
||||
var files = FileDao.GetFeeds(filter.Tenant, filter.Time.From, filter.Time.To)
|
||||
.Where(f => f.Item1.RootFolderType != FolderType.TRASH && f.Item1.RootFolderType != FolderType.BUNCH)
|
||||
.ToList();
|
||||
|
||||
var folderIDs = files.Select(r => r.Item1.FolderID).ToArray();
|
||||
var folders = FolderDao.GetFolders(folderIDs, checkShare: false);
|
||||
|
||||
return files.Select(f => new Tuple<Feed.Aggregator.Feed, object>(ToFeed(f, folders.FirstOrDefault(r => r.ID.Equals(f.Item1.FolderID))), f));
|
||||
}
|
||||
|
||||
public override IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
return FileDao.GetTenantsWithFeeds(fromTime);
|
||||
}
|
||||
|
||||
private Feed.Aggregator.Feed ToFeed((File<int>, SmallShareRecord) tuple, Folder<int> rootFolder)
|
||||
{
|
||||
var file = tuple.Item1;
|
||||
var shareRecord = tuple.Item2;
|
||||
|
||||
if (shareRecord != null)
|
||||
{
|
||||
var feed = new Feed.Aggregator.Feed(shareRecord.ShareBy, shareRecord.ShareOn, true)
|
||||
{
|
||||
Item = sharedFileItem,
|
||||
ItemId = string.Format("{0}_{1}", file.ID, shareRecord.ShareTo),
|
||||
ItemUrl = FilesLinkUtility.GetFileRedirectPreviewUrl(file.ID, true),
|
||||
Product = Product,
|
||||
Module = Name,
|
||||
Title = file.Title,
|
||||
ExtraLocation = rootFolder.FolderType == FolderType.DEFAULT ? rootFolder.Title : string.Empty,
|
||||
ExtraLocationUrl = rootFolder.FolderType == FolderType.DEFAULT ? FilesLinkUtility.GetFileRedirectPreviewUrl(file.FolderID, false) : string.Empty,
|
||||
AdditionalInfo = file.ContentLengthString,
|
||||
Keywords = string.Format("{0}", file.Title),
|
||||
HasPreview = false,
|
||||
CanComment = false,
|
||||
Target = shareRecord.ShareTo,
|
||||
GroupId = GetGroupId(sharedFileItem, shareRecord.ShareBy, file.FolderID.ToString())
|
||||
};
|
||||
|
||||
return feed;
|
||||
}
|
||||
|
||||
var updated = file.Version != 1;
|
||||
return new Feed.Aggregator.Feed(file.ModifiedBy, file.ModifiedOn, true)
|
||||
{
|
||||
Item = fileItem,
|
||||
ItemId = string.Format("{0}_{1}", file.ID, file.Version > 1 ? 1 : 0),
|
||||
ItemUrl = FilesLinkUtility.GetFileRedirectPreviewUrl(file.ID, true),
|
||||
Product = Product,
|
||||
Module = Name,
|
||||
Action = updated ? FeedAction.Updated : FeedAction.Created,
|
||||
Title = file.Title,
|
||||
ExtraLocation = rootFolder.FolderType == FolderType.DEFAULT ? rootFolder.Title : string.Empty,
|
||||
ExtraLocationUrl = rootFolder.FolderType == FolderType.DEFAULT ? FilesLinkUtility.GetFileRedirectPreviewUrl(file.FolderID, false) : string.Empty,
|
||||
AdditionalInfo = file.ContentLengthString,
|
||||
Keywords = string.Format("{0}", file.Title),
|
||||
HasPreview = false,
|
||||
CanComment = false,
|
||||
Target = null,
|
||||
GroupId = GetGroupId(fileItem, file.ModifiedBy, file.FolderID.ToString(), updated ? 1 : 0)
|
||||
};
|
||||
}
|
||||
|
||||
private bool IsTarget(object target, Guid userId)
|
||||
{
|
||||
if (target == null) return true;
|
||||
var owner = (Guid)target;
|
||||
var groupUsers = UserManager.GetUsersByGroup(owner).Select(x => x.ID).ToList();
|
||||
if (!groupUsers.Any())
|
||||
{
|
||||
groupUsers.Add(owner);
|
||||
}
|
||||
|
||||
return groupUsers.Contains(userId);
|
||||
}
|
||||
}
|
||||
}
|
143
products/ASC.Files/Service/Core/FoldersModule.cs
Normal file
143
products/ASC.Files/Service/Core/FoldersModule.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using ASC.Core;
|
||||
using ASC.Feed;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.Security;
|
||||
using ASC.Web.Core;
|
||||
using ASC.Web.Core.Files;
|
||||
|
||||
using FeedModule = ASC.Feed.Aggregator.Modules.FeedModule;
|
||||
|
||||
namespace ASC.Files.Service.Core
|
||||
{
|
||||
public class FoldersModule : FeedModule
|
||||
{
|
||||
private const string folderItem = "folder";
|
||||
private const string sharedFolderItem = "sharedFolder";
|
||||
|
||||
protected override string DbId => Constants.FilesDbId;
|
||||
|
||||
public override string Name => Constants.FoldersModule;
|
||||
|
||||
public override string Product => "documents";
|
||||
|
||||
public override Guid ProductID => WebItemManager.DocumentsProductID;
|
||||
|
||||
public UserManager UserManager { get; }
|
||||
public FilesLinkUtility FilesLinkUtility { get; }
|
||||
public FileSecurity FileSecurity { get; }
|
||||
|
||||
public IFileDao<int> FileDao { get; }
|
||||
public IFolderDao<int> FolderDao { get; }
|
||||
|
||||
public FoldersModule(
|
||||
TenantManager tenantManager,
|
||||
UserManager userManager,
|
||||
WebItemSecurity webItemSecurity,
|
||||
FilesLinkUtility filesLinkUtility,
|
||||
FileSecurity fileSecurity,
|
||||
IDaoFactory daoFactory)
|
||||
: base(tenantManager, webItemSecurity)
|
||||
{
|
||||
UserManager = userManager;
|
||||
FilesLinkUtility = filesLinkUtility;
|
||||
FileSecurity = fileSecurity;
|
||||
FileDao = daoFactory.GetFileDao<int>();
|
||||
FolderDao = daoFactory.GetFolderDao<int>();
|
||||
}
|
||||
|
||||
public override bool VisibleFor(Feed.Aggregator.Feed feed, object data, Guid userId)
|
||||
{
|
||||
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return false;
|
||||
|
||||
var tuple = (Tuple<Folder<int>, SmallShareRecord>)data;
|
||||
var folder = tuple.Item1;
|
||||
var shareRecord = tuple.Item2;
|
||||
|
||||
bool targetCond;
|
||||
if (feed.Target != null)
|
||||
{
|
||||
if (shareRecord != null && shareRecord.ShareBy == userId) return false;
|
||||
|
||||
var owner = (Guid)feed.Target;
|
||||
var groupUsers = UserManager.GetUsersByGroup(owner).Select(x => x.ID).ToList();
|
||||
if (!groupUsers.Any())
|
||||
{
|
||||
groupUsers.Add(owner);
|
||||
}
|
||||
targetCond = groupUsers.Contains(userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetCond = true;
|
||||
}
|
||||
|
||||
return targetCond && FileSecurity.CanRead(folder, userId);
|
||||
}
|
||||
|
||||
public override IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
|
||||
{
|
||||
return FolderDao.GetTenantsWithFeeds(fromTime);
|
||||
}
|
||||
|
||||
public override IEnumerable<Tuple<Feed.Aggregator.Feed, object>> GetFeeds(FeedFilter filter)
|
||||
{
|
||||
var folders = FolderDao.GetFeeds(filter.Tenant, filter.Time.From, filter.Time.To)
|
||||
.Where(f => f.Item1.RootFolderType != FolderType.TRASH && f.Item1.RootFolderType != FolderType.BUNCH)
|
||||
.ToList();
|
||||
|
||||
var parentFolderIDs = folders.Select(r => r.Item1.ParentFolderID).ToArray();
|
||||
var parentFolders = FolderDao.GetFolders(parentFolderIDs, checkShare: false);
|
||||
|
||||
return folders.Select(f => new Tuple<Feed.Aggregator.Feed, object>(ToFeed(f, parentFolders.FirstOrDefault(r => r.ID.Equals(f.Item1.ParentFolderID))), f));
|
||||
}
|
||||
|
||||
private Feed.Aggregator.Feed ToFeed((Folder<int>, SmallShareRecord) tuple, Folder<int> rootFolder)
|
||||
{
|
||||
var folder = tuple.Item1;
|
||||
var shareRecord = tuple.Item2;
|
||||
|
||||
if (shareRecord != null)
|
||||
{
|
||||
var feed = new Feed.Aggregator.Feed(shareRecord.ShareBy, shareRecord.ShareOn, true)
|
||||
{
|
||||
Item = sharedFolderItem,
|
||||
ItemId = string.Format("{0}_{1}", folder.ID, shareRecord.ShareTo),
|
||||
ItemUrl = FilesLinkUtility.GetFileRedirectPreviewUrl(folder.ID, false),
|
||||
Product = Product,
|
||||
Module = Name,
|
||||
Title = folder.Title,
|
||||
ExtraLocation = rootFolder.FolderType == FolderType.DEFAULT ? rootFolder.Title : string.Empty,
|
||||
ExtraLocationUrl = rootFolder.FolderType == FolderType.DEFAULT ? FilesLinkUtility.GetFileRedirectPreviewUrl(folder.ParentFolderID, false) : string.Empty,
|
||||
Keywords = string.Format("{0}", folder.Title),
|
||||
HasPreview = false,
|
||||
CanComment = false,
|
||||
Target = shareRecord.ShareTo,
|
||||
GroupId = GetGroupId(sharedFolderItem, shareRecord.ShareBy, folder.ParentFolderID.ToString())
|
||||
};
|
||||
|
||||
return feed;
|
||||
}
|
||||
|
||||
return new Feed.Aggregator.Feed(folder.CreateBy, folder.CreateOn)
|
||||
{
|
||||
Item = folderItem,
|
||||
ItemId = folder.ID.ToString(),
|
||||
ItemUrl = FilesLinkUtility.GetFileRedirectPreviewUrl(folder.ID, false),
|
||||
Product = Product,
|
||||
Module = Name,
|
||||
Title = folder.Title,
|
||||
ExtraLocation = rootFolder.FolderType == FolderType.DEFAULT ? rootFolder.Title : string.Empty,
|
||||
ExtraLocationUrl = rootFolder.FolderType == FolderType.DEFAULT ? FilesLinkUtility.GetFileRedirectPreviewUrl(folder.ParentFolderID, false) : string.Empty,
|
||||
Keywords = string.Format("{0}", folder.Title),
|
||||
HasPreview = false,
|
||||
CanComment = false,
|
||||
Target = null,
|
||||
GroupId = GetGroupId(folderItem, folder.CreateBy, folder.ParentFolderID.ToString())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using ASC.Common.Caching;
|
||||
using ASC.Common.DependencyInjection;
|
||||
using ASC.Common.Logging;
|
||||
using ASC.ElasticSearch;
|
||||
using ASC.Feed.Aggregator;
|
||||
using ASC.Web.Files.Core.Search;
|
||||
using ASC.Web.Files.Utils;
|
||||
|
||||
@ -39,6 +40,7 @@ namespace ASC.Files.Service
|
||||
)
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddJsonFile($"appsettings.{env}.json", true)
|
||||
.AddJsonFile($"appsettings.services.json", true)
|
||||
.AddJsonFile("storage.json")
|
||||
.AddJsonFile("notify.json")
|
||||
.AddJsonFile("kafka.json")
|
||||
@ -58,8 +60,12 @@ namespace ASC.Files.Service
|
||||
.AddFactoryIndexerFileService()
|
||||
.AddFactoryIndexerFolderService();
|
||||
|
||||
var a = typeof(FactoryIndexer<ISearchItem>).ToString();
|
||||
services.AddAutofac(hostContext.Configuration, hostContext.HostingEnvironment.ContentRootPath, false, false, "search.json");
|
||||
diHelper.AddNLogManager("ASC.Feed.Agregator");
|
||||
services.AddHostedService<FeedAggregatorService>();
|
||||
diHelper
|
||||
.AddFeedAggregatorService();
|
||||
|
||||
services.AddAutofac(hostContext.Configuration, hostContext.HostingEnvironment.ContentRootPath, true, false, "search.json", "feed.json");
|
||||
})
|
||||
.UseConsoleLifetime()
|
||||
.Build();
|
||||
|
22
products/ASC.Files/Service/feed.json
Normal file
22
products/ASC.Files/Service/feed.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"type": "ASC.Files.Service.Core.FilesModule, ASC.Files.Service",
|
||||
"services": [
|
||||
{
|
||||
"type": "ASC.Feed.Aggregator.Modules.IFeedModule, ASC.Feed.Aggregator"
|
||||
}
|
||||
],
|
||||
"instanceScope": "perlifetimescope"
|
||||
},
|
||||
{
|
||||
"type": "ASC.Files.Service.Core.FoldersModule, ASC.Files.Service",
|
||||
"services": [
|
||||
{
|
||||
"type": "ASC.Feed.Aggregator.Modules.IFeedModule, ASC.Feed.Aggregator"
|
||||
}
|
||||
],
|
||||
"instanceScope": "perlifetimescope"
|
||||
}
|
||||
]
|
||||
}
|
@ -59,7 +59,9 @@ export function getFoldersTree() {
|
||||
return {
|
||||
id: folder.id,
|
||||
title: folder.title,
|
||||
foldersCount: folder.foldersCount
|
||||
access: folder.access,
|
||||
foldersCount: folder.foldersCount,
|
||||
rootFolderType: folder.rootFolderType
|
||||
}
|
||||
}) : null,
|
||||
pathParts: data.pathParts,
|
||||
@ -320,3 +322,10 @@ export function moveToFolder(destFolderId, folderIds, fileIds, conflictResolveTy
|
||||
const data = { destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter };
|
||||
return request({ method: "put", url: "/files/fileops/move", data });
|
||||
}
|
||||
|
||||
export function getFileVersionInfo(fileId) {
|
||||
return request({
|
||||
method: "get",
|
||||
url: `/files/file/${fileId}/history`
|
||||
});
|
||||
}
|
||||
|
@ -44,11 +44,16 @@ class DragAndDrop extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
onMouseDown = e => {
|
||||
this.props.onMouseDown && this.props.onMouseDown(e);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let div = this.dropRef.current;
|
||||
div.addEventListener("drop", this.onDrop);
|
||||
div.addEventListener("dragenter", this.onDragEnter);
|
||||
div.addEventListener("dragleave", this.onDragLeave);
|
||||
div.addEventListener("mousedown", this.onMouseDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -56,6 +61,7 @@ class DragAndDrop extends Component {
|
||||
div.removeEventListener("drop", this.onDrop);
|
||||
div.removeEventListener("dragenter", this.onDragEnter);
|
||||
div.removeEventListener("dragleave", this.onDragLeave);
|
||||
div.removeEventListener("mousedown", this.onMouseDown);
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -83,7 +89,8 @@ DragAndDrop.propTypes = {
|
||||
dragging: PropTypes.bool,
|
||||
onDragEnter: PropTypes.func,
|
||||
onDragLeave: PropTypes.func,
|
||||
onDrop: PropTypes.func
|
||||
onDrop: PropTypes.func,
|
||||
onMouseDown: PropTypes.func
|
||||
};
|
||||
|
||||
DragAndDrop.defaultProps = {
|
||||
|
@ -59,7 +59,8 @@ class SelectedFrame extends React.Component {
|
||||
const offsetScroll = this.props.scrollRef.current.viewScrollTop || 0;
|
||||
|
||||
if (item.offsetTop - offsetScroll >= topStart && item.offsetTop - offsetScroll <= topEnd) {
|
||||
if (currentItem.getAttribute("value").split("_")[2]) {
|
||||
const value = currentItem.getAttribute("value");
|
||||
if (value && value.split("_")[2]) {
|
||||
needUpdate = false;
|
||||
break;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-components",
|
||||
"version": "1.0.385",
|
||||
"version": "1.0.388",
|
||||
"description": "Ascensio System SIA component library",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "dist/asc-web-components.js",
|
||||
|
@ -19,7 +19,7 @@ const StyledTreeContainer = styled.div`
|
||||
const StyledTreeMenu = styled(Tree)`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
width: 93%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
&:not(.rc-tree-show-line) .rc-tree-switcher-noop {
|
||||
@ -80,9 +80,9 @@ const StyledTreeMenu = styled(Tree)`
|
||||
`
|
||||
: ''
|
||||
}
|
||||
.rc-tree-title {
|
||||
/* .rc-tree-title {
|
||||
width: ${props => props.badgeLabel && 'calc(100% - 60px) !important'};
|
||||
}
|
||||
} */
|
||||
|
||||
`;
|
||||
|
||||
|
@ -48,13 +48,14 @@ const TreeNodeMenu = styled(TreeNode)`
|
||||
|
||||
position: relative;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
.draggable {
|
||||
color: #333;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* Required to make elements draggable in old WebKit */
|
||||
-khtml-user-drag: element;
|
||||
-webkit-user-drag: element;
|
||||
@ -91,7 +92,11 @@ const TreeNodeMenu = styled(TreeNode)`
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
background: ${props => props.dragging ? "#F8F7BF" : "none"};
|
||||
|
||||
:hover {
|
||||
background: ${props => props.dragging ? "#EFEFB2" : "none"};
|
||||
}
|
||||
}
|
||||
span.rc-tree-switcher,
|
||||
span.rc-tree-iconEle {
|
||||
@ -221,6 +226,10 @@ const TreeNodeMenu = styled(TreeNode)`
|
||||
border-radius: 3px;
|
||||
z-index: 0;
|
||||
width: 108%;
|
||||
|
||||
:hover {
|
||||
background: #DFE2E3;
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
@ -3,6 +3,7 @@ export { default as AvatarEditor } from './components/avatar-editor'
|
||||
export { default as Aside } from './components/aside'
|
||||
export { default as Backdrop } from './components/backdrop'
|
||||
export { default as Badge } from './components/badge'
|
||||
export { default as Box } from './components/box'
|
||||
export { default as Button } from './components/button'
|
||||
export { default as Calendar } from './components/calendar'
|
||||
export { default as Checkbox } from './components/checkbox'
|
||||
|
@ -42,7 +42,7 @@ namespace ASC.Web.Core.Files
|
||||
public class FilesLinkUtility
|
||||
{
|
||||
public const string FilesBaseVirtualPath = "~/products/files/";
|
||||
public const string EditorPage = "doceditor.aspx";
|
||||
public const string EditorPage = "doceditor";
|
||||
private readonly string FilesUploaderURL;
|
||||
public CommonLinkUtility CommonLinkUtility { get; set; }
|
||||
public BaseCommonLinkUtility BaseCommonLinkUtility { get; }
|
||||
@ -73,7 +73,7 @@ namespace ASC.Web.Core.Files
|
||||
get { return BaseCommonLinkUtility.ToAbsolute(FilesBaseVirtualPath); }
|
||||
}
|
||||
|
||||
public const string FileId = "fileid";
|
||||
public const string FileId = "fileId";
|
||||
public const string FolderId = "folderid";
|
||||
public const string Version = "version";
|
||||
public const string FileUri = "fileuri";
|
||||
|
Loading…
Reference in New Issue
Block a user