Merge branch 'develop' into feature/info-panel-update and solved merge conflict
This commit is contained in:
commit
bb16de4ed9
@ -38,9 +38,14 @@ echo "SERVICE_DOCEDITOR: $doceditor"
|
||||
echo "SERVICE_LOGIN: $login"
|
||||
echo "SERVICE_CLIENT: $client"
|
||||
|
||||
echo "Stop all backend services"
|
||||
# Stop all backend services"
|
||||
$dir/build/start/stop.backend.docker.sh
|
||||
|
||||
echo "Remove all backend containers"
|
||||
docker rm -f $(docker ps -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $1}')
|
||||
echo "Remove all backend images"
|
||||
docker rmi -f $(docker images -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $3}')
|
||||
|
||||
echo "Remove all docker images except 'mysql, rabbitmq, redis, elasticsearch, documentserver'"
|
||||
docker image rm -f $(docker images -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $3}')
|
||||
|
||||
@ -68,7 +73,9 @@ if [ "$1" = "--no_ds" ]; then
|
||||
echo "SKIP Document server"
|
||||
else
|
||||
echo "Run Document server"
|
||||
docker compose -f ds.yml up -d
|
||||
DOCUMENT_SERVER_IMAGE_NAME=onlyoffice/documentserver-de:latest \
|
||||
ROOT_DIR=$dir \
|
||||
docker compose -f ds.dev.yml up -d
|
||||
fi
|
||||
|
||||
echo "Build all backend services"
|
||||
@ -86,5 +93,5 @@ echo "Run DB migration"
|
||||
DOCKERFILE=$docker_file \
|
||||
docker compose -f migration-runner.yml up -d
|
||||
|
||||
echo "Start all backend services"
|
||||
# Start all backend services"
|
||||
$dir/build/start/start.backend.docker.sh
|
12
build/clear.backend.docker.sh
Executable file
12
build/clear.backend.docker.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Stop all onlyoffice containers."
|
||||
docker stop $(docker ps -a | egrep "onlyoffice" | awk 'NR>0 {print $1}')
|
||||
echo "Remove all onlyoffice containers."
|
||||
docker rm -f $(docker ps -a | egrep "onlyoffice" | awk 'NR>0 {print $1}')
|
||||
echo "Remove all onlyoffice images."
|
||||
docker rmi -f $(docker images -a | egrep "onlyoffice" | awk 'NR>0 {print $3}')
|
||||
echo "Remove unused volumes."
|
||||
docker volume prune -f
|
||||
echo "Remove unused networks."
|
||||
docker network prune -f
|
@ -34,6 +34,7 @@ RUN apt-get -y update && \
|
||||
apt-get install -y nodejs && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ADD https://api.github.com/repos/ONLYOFFICE/DocSpace/git/refs/heads/${GIT_BRANCH} version.json
|
||||
RUN echo ${GIT_BRANCH} && \
|
||||
git clone --recurse-submodules -b ${GIT_BRANCH} https://github.com/ONLYOFFICE/DocSpace.git ${SRC_PATH}
|
||||
|
||||
@ -196,7 +197,7 @@ WORKDIR ${BUILD_PATH}/services/ASC.ApiSystem/
|
||||
COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py
|
||||
COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.ApiSystem/service/ .
|
||||
|
||||
CMD [" ASC.ApiSystem.dll", " ASC.ApiSystem"]
|
||||
CMD ["ASC.ApiSystem.dll", "ASC.ApiSystem"]
|
||||
|
||||
## ASC.ClearEvents ##
|
||||
FROM dotnetrun AS clear-events
|
||||
|
23
build/install/docker/ds.dev.yml
Normal file
23
build/install/docker/ds.dev.yml
Normal file
@ -0,0 +1,23 @@
|
||||
version: "3.6"
|
||||
services:
|
||||
onlyoffice-document-server:
|
||||
image: "${DOCUMENT_SERVER_IMAGE_NAME}"
|
||||
container_name: ${DOCUMENT_SERVER_HOST}
|
||||
# Strings below enable the JSON Web Token validation.
|
||||
environment:
|
||||
- JWT_ENABLED=true
|
||||
- JWT_SECRET=${DOCUMENT_SERVER_JWT_SECRET}
|
||||
- JWT_HEADER=${DOCUMENT_SERVER_JWT_HEADER}
|
||||
- JWT_IN_BODY=true
|
||||
expose:
|
||||
- "80"
|
||||
volumes:
|
||||
- ${ROOT_DIR}/Data:/var/www/onlyoffice/Data
|
||||
stdin_open: true
|
||||
restart: always
|
||||
stop_grace_period: 60s
|
||||
|
||||
networks:
|
||||
default:
|
||||
external:
|
||||
name: ${NETWORK_NAME}
|
@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
|
||||
PUSHD %~dp0..\..
|
||||
set servicepath=%cd%\web\ASC.Web.HealthChecks.UI\bin\Debug\ASC.Web.HealthChecks.UI.exe urls=http://0.0.0.0:5027
|
||||
set servicepath=%cd%\web\ASC.Web.HealthChecks.UI\bin\Debug\ASC.Web.HealthChecks.UI.exe urls=http://0.0.0.0:5033
|
@ -39,7 +39,7 @@ docker_file=Dockerfile.dev
|
||||
env_extension="dev"
|
||||
core_base_domain="localhost"
|
||||
|
||||
echo "Start all backend services"
|
||||
echo "Start all backend services (containers)"
|
||||
DOCKERFILE=$docker_file \
|
||||
ROOT_DIR=$dir \
|
||||
RELEASE_DATE=$build_date \
|
||||
|
@ -1,23 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
#rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
#echo "Run script directory:" $dir
|
||||
|
||||
#dir=$(builtin cd $rd/../../; pwd)
|
||||
|
||||
#echo "Root directory:" $dir
|
||||
|
||||
#cd $dir/build/install/docker/
|
||||
|
||||
#docker_dir="$( pwd )"
|
||||
|
||||
#echo "Docker directory:" $docker_dir
|
||||
|
||||
echo "Stop all backend containers"
|
||||
# DOCKERFILE=Dockerfile.dev \
|
||||
# docker compose -f docspace.dev.yml down
|
||||
echo "Stop all backend services (containers)"
|
||||
docker stop $(docker ps -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $1}')
|
||||
echo "Remove all backend containers"
|
||||
docker rm -f $(docker ps -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $1}')
|
||||
echo "Remove all backend images"
|
||||
docker rmi -f $(docker images -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $3}')
|
@ -16,6 +16,8 @@
|
||||
<PackageReference Include="AspNetCore.HealthChecks.Kafka" Version="6.0.2" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.MySql" Version="6.0.2" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="6.0.2" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="6.0.2" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="6.0.4" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.5" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.7" />
|
||||
|
@ -24,6 +24,8 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using CsvHelper;
|
||||
|
||||
namespace ASC.Api.Core.Core;
|
||||
|
||||
public static class CustomHealthCheck
|
||||
@ -64,6 +66,7 @@ public static class CustomHealthCheck
|
||||
}
|
||||
|
||||
var elasticSettings = configuration.GetSection("elastic");
|
||||
|
||||
if (elasticSettings != null && elasticSettings.GetChildren().Any())
|
||||
{
|
||||
var host = elasticSettings.GetSection("Host").Value ?? "localhost";
|
||||
@ -79,6 +82,24 @@ public static class CustomHealthCheck
|
||||
}
|
||||
}
|
||||
|
||||
var redisConfiguration = configuration.GetSection("Redis").Get<RedisConfiguration>();
|
||||
|
||||
if (redisConfiguration != null)
|
||||
{
|
||||
hcBuilder.AddRedis(redisConfiguration.ConfigurationOptions.ToString(),
|
||||
name: "redis",
|
||||
tags: new string[] { "redis" });
|
||||
}
|
||||
|
||||
var rabbitMQConfiguration = configuration.GetSection("RabbitMQ").Get<RabbitMQSettings>();
|
||||
|
||||
if (rabbitMQConfiguration != null)
|
||||
{
|
||||
hcBuilder.AddRabbitMQ(x => rabbitMQConfiguration.GetConnectionFactory(),
|
||||
name: "rabbitMQ",
|
||||
tags: new string[] { "rabbitMQ" });
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
@ -85,38 +85,7 @@ public static class ServiceCollectionExtension
|
||||
|
||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
|
||||
|
||||
var factory = new ConnectionFactory()
|
||||
{
|
||||
DispatchConsumersAsync = true
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(rabbitMQConfiguration.Uri))
|
||||
{
|
||||
factory.Uri = new Uri(rabbitMQConfiguration.Uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
factory.HostName = rabbitMQConfiguration.HostName;
|
||||
factory.UserName = rabbitMQConfiguration.UserName;
|
||||
factory.Password = rabbitMQConfiguration.Password;
|
||||
factory.Port = rabbitMQConfiguration.Port;
|
||||
factory.VirtualHost = rabbitMQConfiguration.VirtualHost;
|
||||
}
|
||||
|
||||
if (rabbitMQConfiguration.EnableSsl)
|
||||
{
|
||||
factory.Ssl = new SslOption
|
||||
{
|
||||
Enabled = rabbitMQConfiguration.EnableSsl,
|
||||
Version = SslProtocols.Tls12
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(rabbitMQConfiguration.SslCertPath))
|
||||
{
|
||||
factory.Ssl.CertPath = rabbitMQConfiguration.SslCertPath;
|
||||
factory.Ssl.ServerName = rabbitMQConfiguration.SslServerName;
|
||||
}
|
||||
}
|
||||
var connectionFactory = rabbitMQConfiguration.GetConnectionFactory();
|
||||
|
||||
var retryCount = 5;
|
||||
|
||||
@ -125,7 +94,7 @@ public static class ServiceCollectionExtension
|
||||
retryCount = int.Parse(cfg["core:eventBus:connectRetryCount"]);
|
||||
}
|
||||
|
||||
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
|
||||
return new DefaultRabbitMQPersistentConnection(connectionFactory, logger, retryCount);
|
||||
});
|
||||
|
||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
|
||||
|
43
common/ASC.Api.Core/Model/TaskProgressDto.cs
Normal file
43
common/ASC.Api.Core/Model/TaskProgressDto.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Api.Core.Model;
|
||||
public class TaskProgressDto
|
||||
{
|
||||
public bool IsCompleted { get; set; }
|
||||
public int Progress { get; set; }
|
||||
|
||||
public static TaskProgressDto GetSample()
|
||||
{
|
||||
return new TaskProgressDto
|
||||
{
|
||||
IsCompleted = true,
|
||||
Progress = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,35 +55,7 @@ public class RabbitMQCache<T> : IDisposable, ICacheNotify<T> where T : IMessage<
|
||||
|
||||
var rabbitMQConfiguration = configuration.GetSection("rabbitmq").Get<RabbitMQSettings>();
|
||||
|
||||
_factory = new ConnectionFactory();
|
||||
|
||||
if (!string.IsNullOrEmpty(rabbitMQConfiguration.Uri))
|
||||
{
|
||||
_factory.Uri = new Uri(rabbitMQConfiguration.Uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
_factory.HostName = rabbitMQConfiguration.HostName;
|
||||
_factory.UserName = rabbitMQConfiguration.UserName;
|
||||
_factory.Password = rabbitMQConfiguration.Password;
|
||||
_factory.Port = rabbitMQConfiguration.Port;
|
||||
_factory.VirtualHost = rabbitMQConfiguration.VirtualHost;
|
||||
}
|
||||
|
||||
if (rabbitMQConfiguration.EnableSsl)
|
||||
{
|
||||
_factory.Ssl = new SslOption
|
||||
{
|
||||
Enabled = rabbitMQConfiguration.EnableSsl,
|
||||
Version = SslProtocols.Tls12
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(rabbitMQConfiguration.SslCertPath))
|
||||
{
|
||||
_factory.Ssl.CertPath = rabbitMQConfiguration.SslCertPath;
|
||||
_factory.Ssl.ServerName = rabbitMQConfiguration.SslServerName;
|
||||
}
|
||||
}
|
||||
_factory = rabbitMQConfiguration.GetConnectionFactory();
|
||||
|
||||
_connection = _factory.CreateConnection();
|
||||
_consumerChannel = CreateConsumerChannel();
|
||||
@ -140,7 +112,6 @@ public class RabbitMQCache<T> : IDisposable, ICacheNotify<T> where T : IMessage<
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void TryConnect()
|
||||
{
|
||||
lock (_lock)
|
||||
|
@ -24,6 +24,8 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using System.Security.Authentication;
|
||||
|
||||
namespace ASC.Common.Caching;
|
||||
public class RabbitMQSettings
|
||||
{
|
||||
@ -36,4 +38,42 @@ public class RabbitMQSettings
|
||||
public bool EnableSsl { get; set; }
|
||||
public string SslServerName { get; set; }
|
||||
public string SslCertPath { get; set; }
|
||||
|
||||
public ConnectionFactory GetConnectionFactory()
|
||||
{
|
||||
var factory = new ConnectionFactory()
|
||||
{
|
||||
DispatchConsumersAsync = true
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(Uri))
|
||||
{
|
||||
factory.Uri = new Uri(Uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
factory.HostName = HostName;
|
||||
factory.UserName = UserName;
|
||||
factory.Password = Password;
|
||||
factory.Port = Port;
|
||||
factory.VirtualHost = VirtualHost;
|
||||
}
|
||||
|
||||
if (EnableSsl)
|
||||
{
|
||||
factory.Ssl = new SslOption
|
||||
{
|
||||
Enabled = EnableSsl,
|
||||
Version = SslProtocols.Tls12
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(SslCertPath))
|
||||
{
|
||||
factory.Ssl.CertPath = SslCertPath;
|
||||
factory.Ssl.ServerName = SslServerName;
|
||||
}
|
||||
}
|
||||
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
@ -600,7 +600,7 @@ public class UserManager
|
||||
return GetUsers(employeeStatus).Where(u => IsUserInGroupInternal(u.Id, groupId, refs)).ToArray();
|
||||
}
|
||||
|
||||
public void AddUserIntoGroup(Guid userId, Guid groupId)
|
||||
public void AddUserIntoGroup(Guid userId, Guid groupId, bool dontClearAddressBook = false)
|
||||
{
|
||||
if (Constants.LostUser.Id == userId || Constants.LostGroupInfo.ID == groupId)
|
||||
{
|
||||
@ -618,9 +618,13 @@ public class UserManager
|
||||
var myUri = (_accessor?.HttpContext != null) ? _accessor.HttpContext.Request.GetUrlRewriter().ToString() :
|
||||
(_cache.Get<string>("REWRITE_URL" + tenant.Id) != null) ?
|
||||
new Uri(_cache.Get<string>("REWRITE_URL" + tenant.Id)).ToString() : tenant.GetTenantDomain(_coreSettings);
|
||||
|
||||
if (!dontClearAddressBook)
|
||||
{
|
||||
_cardDavAddressbook.Delete(myUri, user.Id, user.Email, tenant.Id).Wait(); //todo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveUserFromGroup(Guid userId, Guid groupId)
|
||||
{
|
||||
|
@ -101,12 +101,8 @@ class DbQuotaService : IQuotaService
|
||||
using var coreDbContext = _dbContextFactory.CreateDbContext();
|
||||
using var tx = coreDbContext.Database.BeginTransaction();
|
||||
|
||||
AddQuota(coreDbContext, Guid.Empty);
|
||||
|
||||
if (row.UserId != Guid.Empty)
|
||||
{
|
||||
AddQuota(coreDbContext, row.UserId);
|
||||
}
|
||||
|
||||
tx.Commit();
|
||||
});
|
||||
|
@ -76,6 +76,7 @@ public class TenantQuotaController : IQuotaController
|
||||
CurrentSize += size;
|
||||
}
|
||||
|
||||
SetTenantQuotaRow(module, domain, size, dataTag, true, Guid.Empty);
|
||||
SetTenantQuotaRow(module, domain, size, dataTag, true, _authContext.CurrentAccount.ID);
|
||||
}
|
||||
|
||||
@ -87,6 +88,7 @@ public class TenantQuotaController : IQuotaController
|
||||
CurrentSize += size;
|
||||
}
|
||||
|
||||
SetTenantQuotaRow(module, domain, size, dataTag, true, Guid.Empty);
|
||||
SetTenantQuotaRow(module, domain, size, dataTag, true, _authContext.CurrentAccount.ID);
|
||||
}
|
||||
|
||||
|
@ -113,14 +113,14 @@ namespace ASC.Notify.Textile.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This email is generated automatically and you do not need to answer it.
|
||||
///<br />You receive this email because you are a registered user of <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
///<br />Click here to unsubscribe from informational emails: <a href="{1}" style="color: #7b7b7b;" target="_blank">Unsubscribe</a>
|
||||
/// Looks up a localized string similar to For any purchase questions, email us at <a href="mailto:sales@onlyoffice.com" style="color: #7b7b7b;" target="_blank">sales@onlyoffice.com</a>.
|
||||
///<br />In case of technical problems contact our <a href="https://www.onlyoffice.com/support-contact-form.aspx" style="color: #7b7b7b;" target="_blank">support team</a>.
|
||||
///<br /> <a href="{0}" style="color: #7b7b7b;" target="_blank">Click here to unsubscribe</a>
|
||||
///<br />.
|
||||
/// </summary>
|
||||
public static string TextForFooterWithUnsubscribeLink {
|
||||
public static string TextForFooterUnsubsribeDocSpace {
|
||||
get {
|
||||
return ResourceManager.GetString("TextForFooterWithUnsubscribeLink", resourceCulture);
|
||||
return ResourceManager.GetString("TextForFooterUnsubsribeDocSpace", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,10 +192,4 @@
|
||||
</td>
|
||||
</tr></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Bu e-poçt avtomatik olaraq yaradılır və ona cavab vermək lazım deyil.
|
||||
<br />Siz <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>-nin qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün bu e-poçtu alırsınız.
|
||||
<br />Bu e-poçtların artıq sizə göndərilməsini istəmirsinizsə, aşağıdakı linkə klikləyin: <a href="{1}" style="color: #7b7b7b;" target="_blank">Abunəlikdən çıxın</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -158,10 +158,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Този имейл се генерира автоматично и не е необходимо да отговаряте.
|
||||
<br/>Получавате този имейл, защото сте регистриран потребител на <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br/>Ако вече не искате да получавате тези имейли, кликнете върху следната връзка: <a href="{1}" style="color: #7b7b7b;" target="_blank">Отписване</a>
|
||||
<br/></value>
|
||||
</data>
|
||||
</root>
|
@ -128,10 +128,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Tento e-mail je generován automaticky a nemusíte na něj odpovídat.
|
||||
<br />Tento e-mail obdržíte, protože jste registrovaným uživatelem <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Pokud již nechcete přijímat tyto e-maily, klikněte na následující odkaz: <a href="{1}" style="color: #7b7b7b;" target="_blank">Unsubscribe</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -192,10 +192,4 @@
|
||||
</td>
|
||||
</tr></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Diese E-Mail ist automatisch generiert und erfordert keine Antwort.
|
||||
<br />Sie erhalten diese E-Mail, weil Sie ein registrierter Benutzer bzw. eine registrierte Benutzerin von <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a> sind.
|
||||
<br />Um keine E-Mails mehr zu erhalten, klicken Sie bitte hier: <a href="{1}" style="color: #7b7b7b;" target="_blank">Abmelden</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -192,10 +192,4 @@
|
||||
</td>
|
||||
</tr></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Este correo electrónico se genera automáticamente y no es necesario responderlo.
|
||||
<br />Ha recibido este correo electrónico porque es un usuario registrado de <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Haga clic aquí para cancelar la suscripción a los correos electrónicos informativos: <a href="{1}" style="color: #7b7b7b;" target="_blank">Cancelar la suscripción</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -192,10 +192,4 @@
|
||||
</td>
|
||||
</tr></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Cet e-mail est généré automatiquement et vous n'avez pas besoin de répondre.
|
||||
<br />Vous recevez ce courriel parce que vous êtes un utilisateur enregistré de <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Cliquez ici pour vous désabonner des e-mails d'information: <a href="{1}" style="color: #7b7b7b;" target="_blank">Se Désabonner</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -192,10 +192,4 @@
|
||||
</td>
|
||||
</tr></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Questo messaggio è stato generato automaticamente e non necessità di risposta.
|
||||
<br />Hai ricevuto questo messaggio in quanto sei un untente registrato su <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Сlicca qui per annullare l'iscrizione alle email informative:<a href="{1}" style="color: #7b7b7b;" target="_blank">Rimuovi sottoscrizione</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -158,10 +158,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Šis e-pasts ir izveidots automātiski, un jums nav uz to jāatbild.
|
||||
<br />Jūs saņēmāt šo e-pastu, jo esat reģistrēts lietotājs portālā <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Ja nevēlaties saņemt šos e-pastus, spiediet uz šīs saites: <a href="{1}" style="color: #7b7b7b;" target="_blank">Atsaukt abonēšanu</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -128,10 +128,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Deze e-mail is automatisch gegenereerd en u hoeft deze niet te beantwoorden.
|
||||
<br />U ontvangt deze e-mail omdat u een geregistreerde gebruiker bent van <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Als u deze e-mails niet langer wilt ontvangen, klik dan op de volgende link: <a href="{1}" style="color: #7b7b7b;" target="_blank">Afmelden</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -128,10 +128,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Wiadomość utworzona automatycznie i odpowiadać na nią nie trzeba.
|
||||
<br />otrzymujesz tę wiadomość, ponieważ jesteś zarejestrowanym użytkownikiem <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Jeśli nie chcesz otrzymywać tych wiadomości, kliknij na poniższy link: <a href="{1}" style="color: #7b7b7b;" target="_blank">Rezygnacja</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -192,10 +192,4 @@
|
||||
</td>
|
||||
</tr></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Este e-mail é gerado automaticamente e você não precisa respondê-lo.
|
||||
<br />Você está recebendo este e-mail por que é um usuário registrado do <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Se você não quiser mais receber estes e-mails, clique no seguinte link: <a href="{1}" style="color: #7b7b7b;" target="_blank">Cancelar subscrição</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -192,10 +192,10 @@
|
||||
</td>
|
||||
</tr></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>This email is generated automatically and you do not need to answer it.
|
||||
<br />You receive this email because you are a registered user of <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Click here to unsubscribe from informational emails: <a href="{1}" style="color: #7b7b7b;" target="_blank">Unsubscribe</a>
|
||||
<data name="TextForFooterUnsubsribeDocSpace" xml:space="preserve">
|
||||
<value>For any purchase questions, email us at <a href="mailto:sales@onlyoffice.com" style="color: #7b7b7b;" target="_blank">sales@onlyoffice.com</a>.
|
||||
<br />In case of technical problems contact our <a href="https://www.onlyoffice.com/support-contact-form.aspx" style="color: #7b7b7b;" target="_blank">support team</a>.
|
||||
<br /> <a href="{0}" style="color: #7b7b7b;" target="_blank">Click here to unsubscribe</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -192,10 +192,4 @@
|
||||
</td>
|
||||
</tr></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Это сообщение создано автоматически, и отвечать на него не нужно.
|
||||
<br />Вы получили это сообщение, так как являетесь зарегистрированным пользователем <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Если вы хотите отписаться от информационных электронных писем, нажмите на следующую ссылку: <a href="{1}" style="color: #7b7b7b;" target="_blank">Отписаться</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -128,10 +128,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Tento e-mail je generovaný automaticky a nemusíte na to odpovedať.
|
||||
<br />Tento e-mail dostanete pretože ste registrovaným používateľom <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Ak si viac neprajete dostávať tieto e-maily, kliknite na nasledujúci link: <a href="{1}" style="color: #7b7b7b;" target="_blank">Unsubscribe</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -128,10 +128,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Bu e-posta otomatik olarak oluşturulmuştur, lütfen yanıtlamayın.
|
||||
<br />Bu e-postayı almanızın sebebi <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a> kayıtlı kullanıcısı olmanızdır.
|
||||
<br />Bu e-postaları almak istemiyorsanız lütfen linke tıklayın: <a href="{1}" style="color: #7b7b7b;" target="_blank">Abonelikten Ayrıl</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -128,10 +128,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Цей електронний лист сформовано автоматично, і вам не потрібно відповідати на нього.
|
||||
<br />Ви отримуєте цей електронний лист, оскільки ви є зареєстрованим користувачем <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Якщо ви більше не бажаєте отримувати ці електронні листи, натисніть на це посилання: <a href="{1}" style="color: #7b7b7b;" target="_blank">Скасувати підписку</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -128,10 +128,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>Email này được tạo tự động và bạn không cần trả lời nó.
|
||||
<br />Bạn nhận được email này vì bạn là người dùng đã đăng ký <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>
|
||||
<br />Nếu bạn không còn muốn nhận những email này, hãy nhấp vào liên kết sau: <a href="{1}" style="color: #7b7b7b;" target="_blank">Hủy đăng ký</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -128,10 +128,4 @@
|
||||
</div>
|
||||
</body></value>
|
||||
</data>
|
||||
<data name="TextForFooterWithUnsubscribeLink" xml:space="preserve">
|
||||
<value>此电子邮件是自动生成的,您无需回复。
|
||||
<br/>您收到此邮件,因为您是<a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>的用户
|
||||
<br/>点击这里,取消邮件订阅。<a href="{1}" style="color: #7b7b7b;" target="_blank">取消订阅</a>
|
||||
<br /></value>
|
||||
</data>
|
||||
</root>
|
@ -263,14 +263,6 @@ public class TextileStyler : IPatternStyler
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var rootPathArgument = message.GetArgument("__VirtualRootPath");
|
||||
var rootPath = rootPathArgument == null ? string.Empty : (string)rootPathArgument.Value;
|
||||
|
||||
if (string.IsNullOrEmpty(rootPath))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var unsubscribeLink = _coreBaseSettings.CustomMode && _coreBaseSettings.Personal
|
||||
? GetSiteUnsubscribeLink(message, settings)
|
||||
: GetPortalUnsubscribeLink(message, settings);
|
||||
@ -280,7 +272,7 @@ public class TextileStyler : IPatternStyler
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return string.Format(NotifyTemplateResource.TextForFooterWithUnsubscribeLink, rootPath, unsubscribeLink);
|
||||
return string.Format(NotifyTemplateResource.TextForFooterUnsubsribeDocSpace, unsubscribeLink);
|
||||
}
|
||||
|
||||
private string GetPortalUnsubscribeLink(NoticeMessage message, MailWhiteLabelSettings settings)
|
||||
|
@ -1,9 +1,14 @@
|
||||
const winston = require("winston");
|
||||
const winston = require("winston"),
|
||||
WinstonCloudWatch = require('winston-cloudwatch');
|
||||
|
||||
require("winston-daily-rotate-file");
|
||||
|
||||
const path = require("path");
|
||||
const config = require("../config");
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const { randomUUID } = require('crypto');
|
||||
const date = require('date-and-time');
|
||||
|
||||
let logpath = config.get("logPath");
|
||||
if(logpath != null)
|
||||
@ -17,6 +22,14 @@ if(logpath != null)
|
||||
const fileName = logpath ? path.join(logpath, "socket-io.%DATE%.log") : path.join(__dirname, "..", "..", "..", "Logs", "socket-io.%DATE%.log");
|
||||
const dirName = path.dirname(fileName);
|
||||
|
||||
const aws = config.get("aws");
|
||||
|
||||
const accessKeyId = aws.accessKeyId;
|
||||
const secretAccessKey = aws.secretAccessKey;
|
||||
const awsRegion = aws.region;
|
||||
const logGroupName = aws.logGroupName;
|
||||
const logStreamName = aws.logStreamName;
|
||||
|
||||
if (!fs.existsSync(dirName)) {
|
||||
fs.mkdirSync(dirName);
|
||||
}
|
||||
@ -38,23 +51,65 @@ var options = {
|
||||
json: false,
|
||||
colorize: true,
|
||||
},
|
||||
cloudWatch: {
|
||||
name: 'aws',
|
||||
level: "debug",
|
||||
logGroupName: () => {
|
||||
const hostname = os.hostname();
|
||||
|
||||
return logGroupName.replace("${instance-id}", hostname);
|
||||
},
|
||||
logStreamName: () => {
|
||||
const now = new Date();
|
||||
const guid = randomUUID();
|
||||
const dateAsString = date.format(now, 'YYYY/MM/DDTHH.mm.ss');
|
||||
|
||||
return logStreamName.replace("${guid}", guid)
|
||||
.replace("${date}", dateAsString);
|
||||
},
|
||||
awsRegion: awsRegion,
|
||||
jsonMessage: true,
|
||||
awsOptions: {
|
||||
credentials: {
|
||||
accessKeyId: accessKeyId,
|
||||
secretAccessKey: secretAccessKey
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//const fileTransport = new winston.transports.DailyRotateFile(options.file);
|
||||
|
||||
const transports = [
|
||||
var transports = [
|
||||
new winston.transports.Console(options.console),
|
||||
new winston.transports.DailyRotateFile(options.file),
|
||||
new winston.transports.DailyRotateFile(options.file)
|
||||
];
|
||||
|
||||
if (aws != null && aws.accessKeyId !== '')
|
||||
{
|
||||
transports.push(new WinstonCloudWatch(options.cloudWatch));
|
||||
}
|
||||
|
||||
//winston.exceptions.handle(fileTransport);
|
||||
|
||||
const customFormat = winston.format(info => {
|
||||
const now = new Date();
|
||||
|
||||
info.date = date.format(now, 'YYYY-MM-DD HH:mm:ss');
|
||||
info.applicationContext = "SocketIO";
|
||||
info.level = info.level.toUpperCase();
|
||||
|
||||
const hostname = os.hostname();
|
||||
|
||||
info["instance-id"] = hostname;
|
||||
|
||||
return info;
|
||||
})();
|
||||
|
||||
module.exports = new winston.createLogger({
|
||||
//defaultMeta: { component: "socket.io-server" },
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp({
|
||||
format: "YYYY-MM-DD HH:mm:ss",
|
||||
}),
|
||||
customFormat,
|
||||
winston.format.json()
|
||||
),
|
||||
transports: transports,
|
||||
|
@ -3,5 +3,12 @@
|
||||
"port": 9899,
|
||||
"appsettings": "../../../config",
|
||||
"environment": "Development"
|
||||
},
|
||||
"aws":{
|
||||
"accessKeyId": "",
|
||||
"secretAccessKey": "",
|
||||
"region": "",
|
||||
"logGroupName": "/docspace/ASC.SocketIO/instance/${instance-id}/general",
|
||||
"logStreamName": "${guid} - ${date}"
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,11 @@
|
||||
"start:dev": "nodemon server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-cloudwatch-logs": "^3.199.0",
|
||||
"axios": "0.24.0",
|
||||
"connect-redis": "~6.0.0",
|
||||
"cookie-parser": "~1.4.6",
|
||||
"date-and-time": "^2.4.1",
|
||||
"express": "~4.17.2",
|
||||
"express-session": "~1.17.2",
|
||||
"express-socket.io-session": "~1.3.5",
|
||||
@ -20,7 +22,8 @@
|
||||
"nodemon": "^2.0.15",
|
||||
"redis": "^3.1.2",
|
||||
"socket.io": "^4.4.0",
|
||||
"winston": "^3.3.3",
|
||||
"winston": "^3.8.2",
|
||||
"winston-cloudwatch": "^6.1.1",
|
||||
"winston-daily-rotate-file": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -103,3 +103,5 @@ global using Microsoft.Extensions.Options;
|
||||
global using Newtonsoft.Json.Linq;
|
||||
|
||||
global using NLog;
|
||||
global using HealthChecks.UI.Client;
|
||||
global using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||
|
@ -25,6 +25,8 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
|
||||
using StackExchange.Redis.Extensions.Core.Configuration;
|
||||
|
||||
var options = new WebApplicationOptions
|
||||
{
|
||||
Args = args,
|
||||
@ -45,6 +47,16 @@ var logger = LogManager.Setup()
|
||||
})
|
||||
.LoadConfiguration(builder.Configuration, builder.Environment)
|
||||
.GetLogger(typeof(Startup).Namespace);
|
||||
var path = builder.Configuration["pathToConf"];
|
||||
logger.Debug("path: " + path);
|
||||
logger.Debug("EnvironmentName: " + builder.Environment.EnvironmentName);
|
||||
|
||||
var redisConfiguration = builder.Configuration.GetSection("Redis").Get<RedisConfiguration>();
|
||||
logger.Error($"redisConfiguration is null: {redisConfiguration == null}");
|
||||
var kafkaConfiguration = builder.Configuration.GetSection("kafka").Get<KafkaSettings>();
|
||||
logger.Debug($"kafkaConfiguration is null: {kafkaConfiguration == null}");
|
||||
var rabbitMQConfiguration = builder.Configuration.GetSection("RabbitMQ").Get<RabbitMQSettings>();
|
||||
logger.Debug($"rabbitMQConfiguration is null: {rabbitMQConfiguration == null}");
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -80,7 +80,6 @@ public class Startup
|
||||
services.AddSingleton(jsonOptions);
|
||||
|
||||
_diHelper.AddControllers();
|
||||
_diHelper.TryAdd<CultureMiddleware>();
|
||||
_diHelper.TryAdd<IpSecurityFilter>();
|
||||
_diHelper.TryAdd<PaymentFilter>();
|
||||
_diHelper.TryAdd<ProductSecurityFilter>();
|
||||
@ -122,11 +121,19 @@ public class Startup
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseCultureMiddleware();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapCustom();
|
||||
|
||||
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
|
||||
{
|
||||
Predicate = _ => true,
|
||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||
});
|
||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Name.Contains("self")
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -24,17 +24,24 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using ASC.Api.Core.Core;
|
||||
using ASC.Common.Logging;
|
||||
|
||||
namespace ASC.ClearEvents.Extensions;
|
||||
public static class ServiceCollectionExtension
|
||||
{
|
||||
public static IServiceCollection AddClearEventsServices(this IServiceCollection services)
|
||||
public static IServiceCollection AddClearEventsServices(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
var diHelper = new DIHelper(services);
|
||||
|
||||
services.AddScoped<EFLoggerFactory>();
|
||||
|
||||
services.AddHostedService<ClearEventsService>();
|
||||
diHelper.TryAdd<ClearEventsService>();
|
||||
services.AddBaseDbContextPool<MessagesContext>();
|
||||
|
||||
services.AddCustomHealthCheck(configuration);
|
||||
|
||||
return services;
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,10 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using ASC.Api.Core.Core;
|
||||
using HealthChecks.UI.Client;
|
||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||
|
||||
using NLog;
|
||||
|
||||
var options = new WebApplicationOptions
|
||||
@ -50,7 +54,7 @@ try
|
||||
{
|
||||
logger.Info("Configuring web host ({applicationContext})...", Program.AppName);
|
||||
builder.Host.ConfigureDefault();
|
||||
builder.Services.AddClearEventsServices();
|
||||
builder.Services.AddClearEventsServices(builder.Configuration);
|
||||
|
||||
builder.Host.ConfigureContainer<ContainerBuilder>((context, builder) =>
|
||||
{
|
||||
@ -59,6 +63,22 @@ try
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
|
||||
{
|
||||
Predicate = _ => true,
|
||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||
});
|
||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Name.Contains("self")
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
logger.Info("Starting web host ({applicationContext})...", Program.AppName);
|
||||
await app.RunWithTasksAsync();
|
||||
}
|
||||
|
3
packages/client/public/images/remove.session.svg
Normal file
3
packages/client/public/images/remove.session.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.6 8.00029C1.6 4.46548 4.46508 1.6 7.99857 1.6C11.5348 1.6 14.4 4.46592 14.4 8.00029C14.4 11.5335 11.5348 14.4 7.99857 14.4C6.16874 14.4 4.52033 13.6337 3.35202 12.3999C2.26458 11.2515 1.6 9.70508 1.6 8.00029ZM7.99857 0C3.58105 0 0 3.58219 0 8.00029C0 10.1304 0.83265 12.0663 2.19023 13.5C3.64805 15.0395 5.71117 16 7.99857 16C12.4184 16 16 12.4172 16 8.00029C16 3.58219 12.4184 0 7.99857 0ZM9.88726 11L10.9985 9.88994L9.10981 8.00274L10.9985 6.11255L9.88726 5L7.99804 6.8877L6.11031 5L4.99854 6.11255L6.88676 8.00274L4.99854 9.88994L6.11031 11L7.99804 9.1133L9.88726 11Z" fill="#A3A9AE"/>
|
||||
</svg>
|
After Width: | Height: | Size: 744 B |
3
packages/client/public/images/tick.svg
Normal file
3
packages/client/public/images/tick.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.70719 10.7072L11.7072 3.70718L10.293 2.29297L4.00008 8.58585L1.70718 6.29297L0.292969 7.70718L3.29298 10.7072C3.6835 11.0977 4.31666 11.0977 4.70719 10.7072Z" fill="#20D21F"/>
|
||||
</svg>
|
After Width: | Height: | Size: 331 B |
@ -28,6 +28,7 @@
|
||||
"EmptyFilterDescriptionTextRooms": "No rooms match this filter. Try a different one or clear filter to view all rooms.",
|
||||
"EmptyFilterSubheadingText": "No files to be displayed for this filter here",
|
||||
"EmptyFolderDecription": "Drop files here or create new ones.",
|
||||
"EmptyFolderDescriptionUser": "Files and folders uploaded by admins will appeared here.",
|
||||
"EmptyFolderHeader": "No files in this folder",
|
||||
"EmptyRecycleBin": "Empty Trash",
|
||||
"EmptyScreenFolder": "No docs here yet",
|
||||
|
@ -1,10 +1,13 @@
|
||||
{
|
||||
"ActiveSessions": "Active Sessions",
|
||||
"ChangeEmailSuccess": "Email has been changed successfully",
|
||||
"ChangesApplied": "Changes are applied",
|
||||
"ConnectSocialNetworks": "Сonnect your social networks",
|
||||
"ContactInformation": "Contact Information",
|
||||
"CountCodesRemaining": "codes remaining",
|
||||
"ChangePasswordAfterLoggingOut": "Change password after logging out",
|
||||
"DarkTheme": "Dark theme",
|
||||
"DescriptionForSecurity": "For more security, you need to change your password.",
|
||||
"EditPhoto": "Edit photo",
|
||||
"EditSubscriptionsBtn": "Edit subscriptions",
|
||||
"EditUser": "Edit profile",
|
||||
@ -13,6 +16,14 @@
|
||||
"InviteAgainLbl": "Invite again",
|
||||
"LightTheme": "Light theme",
|
||||
"LoginSettings": "Login settings",
|
||||
"LogoutAllActiveSessions": "Log out from all active sessions",
|
||||
"LogoutActiveConnection": "Log out from active connection",
|
||||
"LogoutAllActiveConnections": "Log out from all active connections",
|
||||
"LogoutDescription": "Note. All active connections except this connection will be logged out, as it is currently in use.",
|
||||
"LogoutBtn": "Log out",
|
||||
"LogoutFrom": "Log out from {{platform}} {{browser}} ?",
|
||||
"SuccessLogout": "The active connection was logged out: {{platform}}, {{browser}}",
|
||||
"LogoutAllActiveSessionsDescription": "All active connections except this connection will be logged out, as it is currently in use.",
|
||||
"MessageEmailActivationInstuctionsSentOnEmail": "The email activation instructions have been sent to the <strong>{{ email }}</strong> email address",
|
||||
"MyProfile": "My profile",
|
||||
"PhoneLbl": "Phone",
|
||||
|
@ -77,8 +77,7 @@ export default function withContent(WrappedContent) {
|
||||
access === ShareAccessRights.FullAccess || // only badges?
|
||||
access === ShareAccessRights.None; // TODO: fix access type for owner (now - None)
|
||||
|
||||
const linkStyles =
|
||||
isTrashFolder || isArchiveFolder //|| window.innerWidth <= 1024
|
||||
const linkStyles = isTrashFolder //|| window.innerWidth <= 1024
|
||||
? { noHover: true }
|
||||
: { onClick: onFilesClick };
|
||||
|
||||
|
@ -158,8 +158,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
!!e.target.closest(".lock-file") ||
|
||||
!!e.target.closest(".additional-badges") ||
|
||||
e.target.closest(".tag") ||
|
||||
isTrashFolder ||
|
||||
isArchiveFolder
|
||||
isTrashFolder
|
||||
)
|
||||
return;
|
||||
|
||||
|
@ -353,7 +353,7 @@ const Items = ({
|
||||
);
|
||||
|
||||
if (isVisitor) {
|
||||
items.length > 1 && items.splice(2, 0, filesHeader);
|
||||
items.length > 2 && items.splice(2, 0, filesHeader);
|
||||
} else {
|
||||
items.splice(3, 0, filesHeader);
|
||||
}
|
||||
|
@ -90,7 +90,8 @@ const ArticleMainButtonContent = (props) => {
|
||||
|
||||
isOwner,
|
||||
isAdmin,
|
||||
isVisitor,
|
||||
|
||||
canCreateFiles,
|
||||
|
||||
setInvitePanelOptions,
|
||||
} = props;
|
||||
@ -123,6 +124,7 @@ const ArticleMainButtonContent = (props) => {
|
||||
);
|
||||
|
||||
const onCreateRoom = React.useCallback(() => {
|
||||
console.log("click");
|
||||
const event = new Event(Events.ROOM_CREATE);
|
||||
window.dispatchEvent(event);
|
||||
}, []);
|
||||
@ -405,7 +407,9 @@ const ArticleMainButtonContent = (props) => {
|
||||
? t("Common:Invite")
|
||||
: t("Common:Actions");
|
||||
|
||||
const isDisabled = (!canCreate && !canInvite) || isArchiveFolder;
|
||||
const isDisabled =
|
||||
((!canCreate || (!canCreateFiles && !isRoomsFolder)) && !canInvite) ||
|
||||
isArchiveFolder;
|
||||
const isProfile = history.location.pathname === "/accounts/view/@self";
|
||||
|
||||
return (
|
||||
@ -420,7 +424,7 @@ const ArticleMainButtonContent = (props) => {
|
||||
!isArchiveFolder &&
|
||||
!isArticleLoading &&
|
||||
!isProfile &&
|
||||
(canCreate || canInvite) && (
|
||||
((canCreate && (canCreateFiles || isRoomsFolder)) || canInvite) && (
|
||||
<MobileView
|
||||
t={t}
|
||||
titleProp={t("Upload")}
|
||||
@ -485,6 +489,7 @@ export default inject(
|
||||
uploadDataStore,
|
||||
treeFoldersStore,
|
||||
selectedFolderStore,
|
||||
accessRightsStore,
|
||||
}) => {
|
||||
const { isLoaded, firstLoad, isLoading, canCreate } = filesStore;
|
||||
const {
|
||||
@ -509,6 +514,8 @@ export default inject(
|
||||
|
||||
const { isAdmin, isOwner, isVisitor } = auth.userStore.user;
|
||||
|
||||
const { canCreateFiles } = accessRightsStore;
|
||||
|
||||
return {
|
||||
showText: auth.settingsStore.showText,
|
||||
isMobileArticle: auth.settingsStore.isMobileArticle,
|
||||
@ -525,6 +532,7 @@ export default inject(
|
||||
selectedTreeNode,
|
||||
|
||||
canCreate,
|
||||
canCreateFiles,
|
||||
|
||||
startUpload,
|
||||
|
||||
|
@ -15,6 +15,7 @@ const EmptyFolderContainer = ({
|
||||
linkStyles,
|
||||
isRooms,
|
||||
sectionWidth,
|
||||
canCreateFiles,
|
||||
}) => {
|
||||
const onBackToParentFolder = () => {
|
||||
setIsLoading(true);
|
||||
@ -24,7 +25,7 @@ const EmptyFolderContainer = ({
|
||||
: fetchFiles(parentId).finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
const buttons = (
|
||||
const buttons = canCreateFiles ? (
|
||||
<>
|
||||
<div className="empty-folder_container-links">
|
||||
<img
|
||||
@ -75,13 +76,19 @@ const EmptyFolderContainer = ({
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
return (
|
||||
<EmptyContainer
|
||||
headerText={t("EmptyScreenFolder")}
|
||||
style={{ gridColumnGap: "39px" }}
|
||||
descriptionText={t("EmptyFolderDecription")}
|
||||
descriptionText={
|
||||
canCreateFiles
|
||||
? t("EmptyFolderDecription")
|
||||
: t("EmptyFolderDescriptionUser")
|
||||
}
|
||||
imageSrc="/static/images/empty_screen_alt.svg"
|
||||
buttons={buttons}
|
||||
sectionWidth={sectionWidth}
|
||||
@ -90,7 +97,8 @@ const EmptyFolderContainer = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore, selectedFolderStore }) => {
|
||||
export default inject(
|
||||
({ accessRightsStore, filesStore, selectedFolderStore }) => {
|
||||
const { fetchFiles, fetchRooms } = filesStore;
|
||||
const { navigationPath, parentId } = selectedFolderStore;
|
||||
|
||||
@ -103,11 +111,15 @@ export default inject(({ filesStore, selectedFolderStore }) => {
|
||||
id = elem.id;
|
||||
}
|
||||
|
||||
const { canCreateFiles } = accessRightsStore;
|
||||
|
||||
return {
|
||||
fetchFiles,
|
||||
fetchRooms,
|
||||
setIsLoading: filesStore.setIsLoading,
|
||||
parentId: id ?? parentId,
|
||||
isRooms: isRoom || isRootRoom,
|
||||
canCreateFiles,
|
||||
};
|
||||
})(withTranslation(["Files", "Translations"])(observer(EmptyFolderContainer)));
|
||||
}
|
||||
)(withTranslation(["Files", "Translations"])(observer(EmptyFolderContainer)));
|
||||
|
@ -42,7 +42,6 @@ const RenameEvent = ({
|
||||
const onUpdate = React.useCallback((e, value) => {
|
||||
const originalTitle = getTitleWithoutExst(item);
|
||||
|
||||
setIsLoading(true);
|
||||
let timerId;
|
||||
|
||||
const isSameTitle =
|
||||
@ -52,7 +51,7 @@ const RenameEvent = ({
|
||||
|
||||
if (isSameTitle) {
|
||||
setStartValue(originalTitle);
|
||||
setIsLoading(false);
|
||||
|
||||
onClose();
|
||||
|
||||
return editCompleteAction(item, type);
|
||||
@ -82,7 +81,6 @@ const RenameEvent = ({
|
||||
timerId = null;
|
||||
clearActiveOperations([item.id]);
|
||||
|
||||
setIsLoading(false);
|
||||
onClose();
|
||||
})
|
||||
: renameFolder(item.id, value)
|
||||
@ -106,7 +104,6 @@ const RenameEvent = ({
|
||||
clearTimeout(timerId);
|
||||
timerId = null;
|
||||
clearActiveOperations(null, [item.id]);
|
||||
setIsLoading(false);
|
||||
onClose();
|
||||
});
|
||||
}, []);
|
||||
|
@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { isMobile, isIOS } from "react-device-detect";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import SmartBanner from "react-smartbanner";
|
||||
import "./main.css";
|
||||
|
||||
@ -10,14 +11,9 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const ReactSmartBanner = (props) => {
|
||||
const {
|
||||
t,
|
||||
ready,
|
||||
isBannerVisible,
|
||||
setIsBannerVisible,
|
||||
currentProductId,
|
||||
} = props;
|
||||
const { t, ready, isBannerVisible, setIsBannerVisible } = props;
|
||||
const force = isIOS ? "ios" : "android";
|
||||
const location = useLocation();
|
||||
|
||||
const [isDocuments, setIsDocuments] = useState(false);
|
||||
|
||||
@ -43,12 +39,12 @@ const ReactSmartBanner = (props) => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (window.location.pathname.toLowerCase().includes("files")) {
|
||||
if (window.location.pathname.toLowerCase().includes("rooms")) {
|
||||
setIsDocuments(true);
|
||||
} else {
|
||||
setIsDocuments(false);
|
||||
}
|
||||
}, [currentProductId]);
|
||||
}, [location]);
|
||||
|
||||
const storeText = {
|
||||
ios: t("SmartBanner:AppStore"),
|
||||
@ -99,10 +95,9 @@ const ReactSmartBanner = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, bannerStore }) => {
|
||||
export default inject(({ bannerStore }) => {
|
||||
return {
|
||||
isBannerVisible: bannerStore.isBannerVisible,
|
||||
setIsBannerVisible: bannerStore.setIsBannerVisible,
|
||||
currentProductId: auth.settingsStore.currentProductId,
|
||||
};
|
||||
})(observer(ReactSmartBanner));
|
||||
|
@ -114,7 +114,6 @@
|
||||
}
|
||||
.smartbanner-ios {
|
||||
background: #f2f2f2;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
line-height: 80px;
|
||||
}
|
||||
.smartbanner-ios .smartbanner-close {
|
||||
|
@ -0,0 +1,68 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import Checkbox from "@docspace/components/checkbox";
|
||||
import Button from "@docspace/components/button";
|
||||
import Box from "@docspace/components/box";
|
||||
import Text from "@docspace/components/text";
|
||||
import ModalDialogContainer from "../ModalDialogContainer";
|
||||
|
||||
const LogoutAllConnectionDialog = ({
|
||||
visible,
|
||||
onClose,
|
||||
onRemoveAllSessions,
|
||||
loading,
|
||||
onRemoveAllExceptThis,
|
||||
}) => {
|
||||
const { t } = useTranslation(["Profile", "Common"]);
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
|
||||
const onChangeCheckbox = () => {
|
||||
setIsChecked((prev) => !prev);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalDialogContainer
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
displayType="modal"
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
{t("Profile:LogoutAllActiveConnections")}
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<Text as="p">{t("Profile:LogoutDescription")}</Text>
|
||||
<Text as="p" style={{ margin: "15px 0" }}>
|
||||
{t("Profile:DescriptionForSecurity")}
|
||||
</Text>
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<Checkbox isChecked={isChecked} onChange={onChangeCheckbox} />
|
||||
{t("Profile:ChangePasswordAfterLoggingOut")}
|
||||
</Box>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="LogoutBtn"
|
||||
label={t("Profile:LogoutBtn")}
|
||||
size="normal"
|
||||
scale
|
||||
primary={true}
|
||||
onClick={() =>
|
||||
isChecked ? onRemoveAllSessions() : onRemoveAllExceptThis()
|
||||
}
|
||||
isLoading={loading}
|
||||
/>
|
||||
<Button
|
||||
key="CloseBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
scale
|
||||
onClick={onClose}
|
||||
isDisabled={loading}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogoutAllConnectionDialog;
|
@ -0,0 +1,54 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import Button from "@docspace/components/button";
|
||||
import ModalDialogContainer from "../ModalDialogContainer";
|
||||
|
||||
const LogoutConnectionDialog = ({
|
||||
visible,
|
||||
onClose,
|
||||
onRemoveSession,
|
||||
data,
|
||||
loading,
|
||||
}) => {
|
||||
const { t } = useTranslation(["Profile", "Common"]);
|
||||
|
||||
return (
|
||||
<ModalDialogContainer
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
displayType="modal"
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
{t("Profile:LogoutActiveConnection")}
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
{t("Profile:LogoutFrom", {
|
||||
platform: data.platform,
|
||||
browser: data.browser,
|
||||
})}
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="LogoutBtn"
|
||||
label={t("Profile:LogoutBtn")}
|
||||
size="normal"
|
||||
scale
|
||||
primary={true}
|
||||
onClick={() => onRemoveSession(data.id)}
|
||||
isLoading={loading}
|
||||
/>
|
||||
<Button
|
||||
key="CloseBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
scale
|
||||
onClick={onClose}
|
||||
isDisabled={loading}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogoutConnectionDialog;
|
@ -27,6 +27,8 @@ import ChangeNameDialog from "./ChangeNameDialog";
|
||||
import AvatarEditorDialog from "./AvatarEditorDialog";
|
||||
import DeletePortalDialog from "./DeletePortalDialog";
|
||||
import InviteUsersWarningDialog from "./InviteUsersWarningDialog";
|
||||
import LogoutConnectionDialog from "./LogoutConnectionDialog";
|
||||
import LogoutAllConnectionDialog from "./LogoutAllConnectionDialog";
|
||||
|
||||
export {
|
||||
EmptyTrashDialog,
|
||||
@ -57,5 +59,7 @@ export {
|
||||
ChangeNameDialog,
|
||||
AvatarEditorDialog,
|
||||
DeletePortalDialog,
|
||||
LogoutConnectionDialog,
|
||||
InviteUsersWarningDialog,
|
||||
LogoutAllConnectionDialog,
|
||||
};
|
||||
|
@ -107,6 +107,7 @@ const Table = ({
|
||||
hasMoreAccounts,
|
||||
filterTotal,
|
||||
withPaging,
|
||||
canChangeUserType,
|
||||
}) => {
|
||||
const ref = useRef(null);
|
||||
|
||||
@ -156,6 +157,7 @@ const Table = ({
|
||||
isOwner={isOwner}
|
||||
changeUserType={changeType}
|
||||
userId={userId}
|
||||
canChangeUserType={canChangeUserType}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
@ -165,7 +167,7 @@ const Table = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ peopleStore, auth }) => {
|
||||
export default inject(({ peopleStore, auth, accessRightsStore }) => {
|
||||
const {
|
||||
usersStore,
|
||||
filterStore,
|
||||
@ -180,6 +182,8 @@ export default inject(({ peopleStore, auth }) => {
|
||||
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
|
||||
const { isAdmin, isOwner, id: userId } = auth.userStore.user;
|
||||
|
||||
const { canChangeUserType } = accessRightsStore;
|
||||
|
||||
return {
|
||||
peopleList,
|
||||
viewAs,
|
||||
@ -195,5 +199,6 @@ export default inject(({ peopleStore, auth }) => {
|
||||
fetchMoreAccounts,
|
||||
hasMoreAccounts,
|
||||
filterTotal,
|
||||
canChangeUserType,
|
||||
};
|
||||
})(observer(Table));
|
||||
|
@ -129,14 +129,15 @@ const PeopleTableRow = (props) => {
|
||||
checkedProps,
|
||||
onContentRowSelect,
|
||||
onEmailClick,
|
||||
isAdmin,
|
||||
|
||||
isOwner,
|
||||
theme,
|
||||
changeUserType,
|
||||
userId,
|
||||
|
||||
setBufferSelection,
|
||||
isActive,
|
||||
isSeveralSelection,
|
||||
canChangeUserType,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
@ -147,10 +148,7 @@ const PeopleTableRow = (props) => {
|
||||
position,
|
||||
rooms,
|
||||
|
||||
id,
|
||||
|
||||
role,
|
||||
|
||||
isVisitor,
|
||||
} = item;
|
||||
|
||||
@ -278,26 +276,9 @@ const PeopleTableRow = (props) => {
|
||||
</Text>
|
||||
);
|
||||
|
||||
if (userId === id || statusType === "disabled") return text;
|
||||
const canChange = canChangeUserType(item);
|
||||
|
||||
switch (role) {
|
||||
case "owner":
|
||||
return text;
|
||||
|
||||
case "admin":
|
||||
case "manager":
|
||||
if (isOwner) {
|
||||
return combobox;
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
|
||||
case "user":
|
||||
return combobox;
|
||||
|
||||
default:
|
||||
return text;
|
||||
}
|
||||
return canChange ? combobox : text;
|
||||
};
|
||||
|
||||
const typeCell = renderTypeCell();
|
||||
|
@ -78,6 +78,7 @@ const SectionFilterContent = ({
|
||||
newFilter.page = 0;
|
||||
|
||||
newFilter.role = role;
|
||||
|
||||
newFilter.group = group;
|
||||
|
||||
setIsLoading(true);
|
||||
@ -144,11 +145,11 @@ const SectionFilterContent = ({
|
||||
isHeader: true,
|
||||
isLast: true,
|
||||
},
|
||||
{ key: "admin", group: "filter-type", label: t("Administrator") },
|
||||
{ key: "admin", group: "filter-type", label: t("Common:DocSpaceAdmin") },
|
||||
{
|
||||
key: "manager",
|
||||
group: "filter-type",
|
||||
label: "Manager",
|
||||
label: t("Common:RoomAdmin"),
|
||||
},
|
||||
{
|
||||
key: "user",
|
||||
@ -266,14 +267,14 @@ const SectionFilterContent = ({
|
||||
|
||||
switch (filter.role) {
|
||||
case "admin":
|
||||
label = t("Administrator");
|
||||
label = t("Common:DocSpaceAdmin");
|
||||
break;
|
||||
case "manager":
|
||||
label = t("Common:RoomAdmin");
|
||||
break;
|
||||
case "user":
|
||||
label = userCaption;
|
||||
break;
|
||||
case "guest":
|
||||
label = guestCaption;
|
||||
break;
|
||||
default:
|
||||
label = "";
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ const SectionHeaderContent = (props) => {
|
||||
setInfoPanelIsVisible,
|
||||
isInfoPanelVisible,
|
||||
isOwner,
|
||||
isAdmin,
|
||||
|
||||
setInvitePanelOptions,
|
||||
} = props;
|
||||
|
||||
|
@ -54,7 +54,7 @@ const PureHome = ({
|
||||
setIsRefresh(true);
|
||||
const newFilter = Filter.getFilter(location);
|
||||
//console.log("PEOPLE URL changed", pathname, newFilter);
|
||||
getUsersList(newFilter).finally(() => {
|
||||
getUsersList(newFilter, true).finally(() => {
|
||||
setFirstLoad(false);
|
||||
setIsLoading(false);
|
||||
setIsRefresh(false);
|
||||
|
@ -179,7 +179,6 @@ export default inject(({ auth, selectedFolderStore }) => {
|
||||
calculateSelection,
|
||||
normalizeSelection,
|
||||
isItemChanged,
|
||||
selectionParentRoom,
|
||||
setSelectionParentRoom,
|
||||
roomsView,
|
||||
fileView,
|
||||
|
@ -50,6 +50,8 @@ const ItemContextOptions = ({
|
||||
contextMenuRef.current.hide();
|
||||
}, [selection]);
|
||||
|
||||
const options = contextHelper.getItemContextOptions();
|
||||
|
||||
return (
|
||||
<StyledItemContextOptions onClick={setItemAsBufferSelection}>
|
||||
<ContextMenu
|
||||
@ -57,6 +59,7 @@ const ItemContextOptions = ({
|
||||
getContextModel={contextHelper.getItemContextOptions}
|
||||
withBackdrop={false}
|
||||
/>
|
||||
{options.length > 0 && (
|
||||
<ContextMenuButton
|
||||
className="expandButton"
|
||||
title={"Show item actions"}
|
||||
@ -65,6 +68,7 @@ const ItemContextOptions = ({
|
||||
directionX="right"
|
||||
isNew={true}
|
||||
/>
|
||||
)}
|
||||
</StyledItemContextOptions>
|
||||
);
|
||||
};
|
||||
|
@ -17,6 +17,7 @@ const Accounts = ({
|
||||
isOwner,
|
||||
isAdmin,
|
||||
changeUserType,
|
||||
canChangeUserType,
|
||||
selfId,
|
||||
}) => {
|
||||
const [statusLabel, setStatusLabel] = React.useState("");
|
||||
@ -127,26 +128,9 @@ const Accounts = ({
|
||||
|
||||
const status = getUserStatus(selection);
|
||||
|
||||
if (selfId === id || status === "disabled") return text;
|
||||
const canChange = canChangeUserType({ ...selection, statusType: status });
|
||||
|
||||
switch (role) {
|
||||
case "owner":
|
||||
return text;
|
||||
|
||||
case "admin":
|
||||
case "manager":
|
||||
if (isOwner) {
|
||||
return combobox;
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
|
||||
case "user":
|
||||
return combobox;
|
||||
|
||||
default:
|
||||
return text;
|
||||
}
|
||||
return canChange ? combobox : text;
|
||||
};
|
||||
|
||||
const typeData = renderTypeData();
|
||||
|
@ -31,12 +31,14 @@ const Members = ({
|
||||
|
||||
setInvitePanelOptions,
|
||||
changeUserType,
|
||||
canInviteUserInRoom,
|
||||
}) => {
|
||||
const membersHelper = new MembersHelper({ t });
|
||||
|
||||
const [members, setMembers] = useState(null);
|
||||
const [showLoader, setShowLoader] = useState(false);
|
||||
|
||||
const isDisabledInvite = !canInviteUserInRoom({ access: selection.access });
|
||||
const fetchMembers = async (roomId) => {
|
||||
let timerId;
|
||||
if (members) timerId = setTimeout(() => setShowLoader(true), 1000);
|
||||
@ -123,6 +125,7 @@ const Members = ({
|
||||
isFill={true}
|
||||
onClick={onClickInviteUsers}
|
||||
size={16}
|
||||
isDisabled={isDisabledInvite}
|
||||
/>
|
||||
</StyledUserTypeHeader>
|
||||
|
||||
@ -174,12 +177,14 @@ const Members = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, filesStore, peopleStore, dialogsStore }) => {
|
||||
export default inject(
|
||||
({ auth, filesStore, peopleStore, dialogsStore, accessRightsStore }) => {
|
||||
const { selectionParentRoom, setSelectionParentRoom } = auth.infoPanelStore;
|
||||
const { getRoomMembers, updateRoomMemberRole } = filesStore;
|
||||
const { isOwner, isAdmin, id: selfId } = auth.userStore.user;
|
||||
const { setInvitePanelOptions } = dialogsStore;
|
||||
const { changeType: changeUserType } = peopleStore;
|
||||
const { canInviteUserInRoom } = accessRightsStore;
|
||||
|
||||
return {
|
||||
selectionParentRoom,
|
||||
@ -194,8 +199,10 @@ export default inject(({ auth, filesStore, peopleStore, dialogsStore }) => {
|
||||
|
||||
setInvitePanelOptions,
|
||||
changeUserType,
|
||||
canInviteUserInRoom,
|
||||
};
|
||||
})(
|
||||
}
|
||||
)(
|
||||
withTranslation([
|
||||
"InfoPanel",
|
||||
"Common",
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
import { ColorTheme, ThemeType } from "@docspace/common/components/ColorTheme";
|
||||
|
||||
import { StyledInfoPanelHeader } from "./styles/common";
|
||||
import { FolderType } from "@docspace/common/constants";
|
||||
|
||||
const InfoPanelHeaderContent = (props) => {
|
||||
const {
|
||||
@ -30,6 +31,8 @@ const InfoPanelHeaderContent = (props) => {
|
||||
getIsGallery,
|
||||
getIsAccounts,
|
||||
isRootFolder,
|
||||
canRemoveUserFromRoom,
|
||||
rootFolderType,
|
||||
} = props;
|
||||
|
||||
const isRooms = getIsRooms();
|
||||
@ -47,6 +50,9 @@ const InfoPanelHeaderContent = (props) => {
|
||||
const setHistory = () => setView("history");
|
||||
const setDetails = () => setView("details");
|
||||
|
||||
const isArchiveRoot = rootFolderType === FolderType.Archive;
|
||||
const canRemove = canRemoveUserFromRoom({ access: selection?.access });
|
||||
|
||||
const submenuData = [
|
||||
{
|
||||
id: "members",
|
||||
@ -68,7 +74,11 @@ const InfoPanelHeaderContent = (props) => {
|
||||
},
|
||||
];
|
||||
|
||||
const roomsSubmenu = [...submenuData];
|
||||
const roomsSubmenu = isArchiveRoot
|
||||
? !canRemove
|
||||
? [{ ...submenuData[2] }]
|
||||
: [{ ...submenuData[0] }, { ...submenuData[2] }]
|
||||
: [...submenuData];
|
||||
const personalSubmenu = [submenuData[1], submenuData[2]];
|
||||
|
||||
const isTablet =
|
||||
@ -122,7 +132,7 @@ const InfoPanelHeaderContent = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, selectedFolderStore }) => {
|
||||
export default inject(({ auth, selectedFolderStore, accessRightsStore }) => {
|
||||
const {
|
||||
selection,
|
||||
setIsVisible,
|
||||
@ -134,7 +144,8 @@ export default inject(({ auth, selectedFolderStore }) => {
|
||||
getIsGallery,
|
||||
getIsAccounts,
|
||||
} = auth.infoPanelStore;
|
||||
const { isRootFolder } = selectedFolderStore;
|
||||
const { isRootFolder, rootFolderType } = selectedFolderStore;
|
||||
const { canRemoveUserFromRoom } = accessRightsStore;
|
||||
|
||||
return {
|
||||
selection,
|
||||
@ -148,6 +159,8 @@ export default inject(({ auth, selectedFolderStore }) => {
|
||||
getIsAccounts,
|
||||
|
||||
isRootFolder,
|
||||
rootFolderType,
|
||||
canRemoveUserFromRoom,
|
||||
};
|
||||
})(
|
||||
withTranslation(["Common", "InfoPanel"])(
|
||||
|
@ -183,6 +183,8 @@ const StyledTableRow = styled(TableRow)`
|
||||
.row_update-text,
|
||||
.expandButton,
|
||||
.badges,
|
||||
.tag,
|
||||
.author-cell,
|
||||
.table-container_cell > p {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import Avatar from "@docspace/components/avatar";
|
||||
|
||||
const AuthorCell = ({ fileOwner, sideColor, item }) => {
|
||||
return (
|
||||
<StyledAuthorCell>
|
||||
<StyledAuthorCell className="author-cell">
|
||||
<Avatar
|
||||
source={item.createdBy.avatarSmall}
|
||||
className="author-avatar-cell"
|
||||
|
@ -395,7 +395,11 @@ const SectionFilterContent = ({
|
||||
}
|
||||
|
||||
filterValues.push({
|
||||
key: isMe ? FilterKeys.me : roomsFilter.subjectId,
|
||||
key: isMe
|
||||
? roomsFilter.excludeSubject
|
||||
? FilterKeys.other
|
||||
: FilterKeys.me
|
||||
: roomsFilter.subjectId,
|
||||
group: FilterGroups.roomFilterOwner,
|
||||
label: label,
|
||||
});
|
||||
|
@ -4,7 +4,11 @@ import styled, { css } from "styled-components";
|
||||
import { withRouter } from "react-router";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import { AppServerConfig } from "@docspace/common/constants";
|
||||
import {
|
||||
AppServerConfig,
|
||||
FolderType,
|
||||
RoomSearchArea,
|
||||
} from "@docspace/common/constants";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { isMobile, isTablet, isMobileOnly } from "react-device-detect";
|
||||
import DropDownItem from "@docspace/components/drop-down-item";
|
||||
@ -546,16 +550,22 @@ class SectionHeaderContent extends React.Component {
|
||||
setAlreadyFetchingRooms,
|
||||
|
||||
categoryType,
|
||||
|
||||
rootFolderType,
|
||||
} = this.props;
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
setAlreadyFetchingRooms(true);
|
||||
|
||||
fetchRooms(null, null)
|
||||
.then(() => {
|
||||
const filter = RoomsFilter.getDefault();
|
||||
|
||||
if (rootFolderType === FolderType.Archive) {
|
||||
filter.searchArea = RoomSearchArea.Archive;
|
||||
}
|
||||
|
||||
fetchRooms(null, filter)
|
||||
.then(() => {
|
||||
const filterParamsStr = filter.toUrlParams();
|
||||
|
||||
const url = getCategoryUrl(categoryType, filter.folder);
|
||||
@ -595,7 +605,7 @@ class SectionHeaderContent extends React.Component {
|
||||
showText,
|
||||
isRoomsFolder,
|
||||
isEmptyPage,
|
||||
isVisitor,
|
||||
canCreateFiles,
|
||||
} = this.props;
|
||||
const menuItems = this.getMenuItems();
|
||||
const isLoading = !title || !tReady;
|
||||
@ -624,7 +634,7 @@ class SectionHeaderContent extends React.Component {
|
||||
sectionWidth={context.sectionWidth}
|
||||
showText={showText}
|
||||
isRootFolder={isRootFolder}
|
||||
canCreate={canCreate && !isVisitor}
|
||||
canCreate={canCreate && canCreateFiles}
|
||||
title={title}
|
||||
isDesktop={isDesktop}
|
||||
isTabletView={isTabletView}
|
||||
@ -669,7 +679,7 @@ export default inject(
|
||||
treeFoldersStore,
|
||||
filesActionsStore,
|
||||
settingsStore,
|
||||
|
||||
accessRightsStore,
|
||||
contextOptionsStore,
|
||||
}) => {
|
||||
const {
|
||||
@ -732,6 +742,7 @@ export default inject(
|
||||
roomType,
|
||||
pathParts,
|
||||
navigationPath,
|
||||
rootFolderType,
|
||||
} = selectedFolderStore;
|
||||
|
||||
const selectedFolder = { ...selectedFolderStore };
|
||||
@ -748,6 +759,8 @@ export default inject(
|
||||
onClickReconnectStorage,
|
||||
} = contextOptionsStore;
|
||||
|
||||
const { canCreateFiles } = accessRightsStore;
|
||||
|
||||
return {
|
||||
showText: auth.settingsStore.showText,
|
||||
isDesktop: auth.settingsStore.isDesktopClient,
|
||||
@ -759,6 +772,7 @@ export default inject(
|
||||
pathParts: pathParts,
|
||||
navigationPath: navigationPath,
|
||||
canCreate,
|
||||
canCreateFiles,
|
||||
setIsInfoPanelVisible: setIsVisible,
|
||||
isInfoPanelVisible: isVisible,
|
||||
isHeaderVisible,
|
||||
@ -818,6 +832,8 @@ export default inject(
|
||||
onClickInviteUsers,
|
||||
onShowInfoPanel,
|
||||
onClickArchive,
|
||||
|
||||
rootFolderType,
|
||||
};
|
||||
}
|
||||
)(
|
||||
|
@ -135,6 +135,16 @@ class ArticleBodyContent extends React.Component {
|
||||
|
||||
if (tReady) setIsLoadedArticleBody(true);
|
||||
|
||||
if (prevProps.location.pathname !== this.props.location.pathname) {
|
||||
if (this.props.location.pathname.includes("payments")) {
|
||||
this.setState({ selectedKeys: ["4-0"] });
|
||||
}
|
||||
|
||||
if (this.props.location.pathname.includes("common")) {
|
||||
this.setState({ selectedKeys: ["0-0"] });
|
||||
}
|
||||
}
|
||||
|
||||
if (!isArrayEqual(prevState.selectedKeys, this.state.selectedKeys)) {
|
||||
const { selectedKeys } = this.state;
|
||||
|
||||
|
261
packages/client/src/pages/Profile/Section/Footer/index.js
Normal file
261
packages/client/src/pages/Profile/Section/Footer/index.js
Normal file
@ -0,0 +1,261 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import Text from "@docspace/components/text";
|
||||
import Link from "@docspace/components/link";
|
||||
import Box from "@docspace/components/box";
|
||||
import HelpButton from "@docspace/components/help-button";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
|
||||
import {
|
||||
LogoutConnectionDialog,
|
||||
LogoutAllConnectionDialog,
|
||||
} from "SRC_DIR/components/dialogs";
|
||||
|
||||
import {
|
||||
StyledFooter,
|
||||
Table,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableHeaderCell,
|
||||
TableBody,
|
||||
TableDataCell,
|
||||
} from "./styled-active-sessions";
|
||||
|
||||
const removeIcon = (
|
||||
<ReactSVG className="remove-icon" src="images/remove.session.svg" />
|
||||
);
|
||||
const tickIcon = (
|
||||
<ReactSVG className="tick-icon" wrapper="span" src="images/tick.svg" />
|
||||
);
|
||||
|
||||
const ActiveSessions = ({
|
||||
t,
|
||||
culture,
|
||||
getAllSessions,
|
||||
removeAllSessions,
|
||||
removeSession,
|
||||
logoutVisible,
|
||||
setLogoutVisible,
|
||||
logoutAllVisible,
|
||||
setLogoutAllVisible,
|
||||
removeAllExecptThis,
|
||||
}) => {
|
||||
const [sessions, setSessions] = useState([]);
|
||||
const [currentSession, setCurrentSession] = useState(0);
|
||||
const [modalData, setModalData] = useState({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
getAllSessions().then((res) => {
|
||||
setSessions(res.items);
|
||||
setCurrentSession(res.loginEvent);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onClickRemoveAllSessions = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
await removeAllSessions().then(() =>
|
||||
getAllSessions().then((res) => setSessions(res.items))
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setLogoutAllVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onClickRemoveAllExceptThis = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
await removeAllExecptThis().then(() =>
|
||||
getAllSessions().then((res) => setSessions(res.items))
|
||||
);
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setLogoutAllVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onClickRemoveSession = async (id) => {
|
||||
const foundSession = sessions.find((s) => s.id === id);
|
||||
try {
|
||||
setLoading(true);
|
||||
await removeSession(foundSession.id).then(() =>
|
||||
getAllSessions().then((res) => setSessions(res.items))
|
||||
);
|
||||
toastr.success(
|
||||
t("Profile:SuccessLogout", {
|
||||
platform: foundSession.platform,
|
||||
browser: foundSession.browser,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setLogoutVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
const convertTime = (date) => {
|
||||
return new Date(date).toLocaleString(culture);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledFooter>
|
||||
<Text fontSize="16px" fontWeight={700}>
|
||||
{t("Profile:ActiveSessions")}
|
||||
</Text>
|
||||
<Box
|
||||
displayProp="flex"
|
||||
alignItems="center"
|
||||
justifyContent="flex-start"
|
||||
marginProp="10px 0 0"
|
||||
>
|
||||
<Link
|
||||
className="session-logout"
|
||||
type="action"
|
||||
isHovered
|
||||
onClick={() => setLogoutAllVisible(true)}
|
||||
>
|
||||
{t("Profile:LogoutAllActiveSessions")}
|
||||
</Link>
|
||||
<HelpButton
|
||||
iconName="/static/images/info.react.svg"
|
||||
tooltipContent={
|
||||
<Text fontSize="13px">
|
||||
{t("Profile:LogoutAllActiveSessionsDescription")}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
{isMobile ? (
|
||||
<Table>
|
||||
<TableBody>
|
||||
{sessions.map((session) => (
|
||||
<TableRow key={session.id}>
|
||||
<TableDataCell style={{ borderTop: "0" }}>
|
||||
{session.platform}
|
||||
<span className="session-browser">
|
||||
<span>{session.browser}</span>
|
||||
</span>
|
||||
{currentSession === session.id ? tickIcon : null}
|
||||
|
||||
<Box flexDirection="column" alignItems="center">
|
||||
<span className="session-date">
|
||||
{convertTime(session.date)}
|
||||
</span>
|
||||
<span className="session-ip">{session.ip}</span>
|
||||
</Box>
|
||||
</TableDataCell>
|
||||
|
||||
<TableDataCell
|
||||
style={{ borderTop: "0" }}
|
||||
onClick={() => {
|
||||
setLogoutVisible(true);
|
||||
setModalData({
|
||||
id: session.id,
|
||||
platform: session.platform,
|
||||
browser: session.browser,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{currentSession !== session.id ? removeIcon : null}
|
||||
</TableDataCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>{t("Common:Sessions")}</TableHeaderCell>
|
||||
<TableHeaderCell>{t("Common:Date")}</TableHeaderCell>
|
||||
<TableHeaderCell>{t("Common:IpAddress")}</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{sessions.map((session) => (
|
||||
<TableRow key={session.id}>
|
||||
<TableDataCell>
|
||||
{session.platform}
|
||||
<span className="session-browser">
|
||||
<span>{session.browser}</span>
|
||||
</span>
|
||||
{currentSession === session.id ? tickIcon : null}
|
||||
</TableDataCell>
|
||||
<TableDataCell>{convertTime(session.date)}</TableDataCell>
|
||||
<TableDataCell>{session.ip}</TableDataCell>
|
||||
<TableDataCell
|
||||
onClick={() => {
|
||||
setLogoutVisible(true);
|
||||
setModalData({
|
||||
id: session.id,
|
||||
platform: session.platform,
|
||||
browser: session.browser,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{currentSession !== session.id ? removeIcon : null}
|
||||
</TableDataCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
|
||||
{logoutVisible && (
|
||||
<LogoutConnectionDialog
|
||||
visible={logoutVisible}
|
||||
data={modalData}
|
||||
loading={loading}
|
||||
onClose={() => setLogoutVisible(false)}
|
||||
onRemoveSession={onClickRemoveSession}
|
||||
/>
|
||||
)}
|
||||
|
||||
{logoutAllVisible && (
|
||||
<LogoutAllConnectionDialog
|
||||
visible={logoutAllVisible}
|
||||
loading={loading}
|
||||
onClose={() => setLogoutAllVisible(false)}
|
||||
onRemoveAllSessions={onClickRemoveAllSessions}
|
||||
onRemoveAllExceptThis={onClickRemoveAllExceptThis}
|
||||
/>
|
||||
)}
|
||||
</StyledFooter>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, setup }) => {
|
||||
const { culture } = auth.settingsStore;
|
||||
const {
|
||||
getAllSessions,
|
||||
removeAllSessions,
|
||||
removeSession,
|
||||
logoutVisible,
|
||||
setLogoutVisible,
|
||||
logoutAllVisible,
|
||||
setLogoutAllVisible,
|
||||
removeAllExecptThis,
|
||||
} = setup;
|
||||
return {
|
||||
culture,
|
||||
getAllSessions,
|
||||
removeAllSessions,
|
||||
removeSession,
|
||||
logoutVisible,
|
||||
setLogoutVisible,
|
||||
logoutAllVisible,
|
||||
setLogoutAllVisible,
|
||||
removeAllExecptThis,
|
||||
};
|
||||
})(observer(withTranslation(["Profile", "Common"])(ActiveSessions)));
|
@ -0,0 +1,114 @@
|
||||
import styled from "styled-components";
|
||||
import { hugeMobile } from "@docspace/components/utils/device";
|
||||
|
||||
export const StyledFooter = styled.div`
|
||||
.session-logout {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.icon-button {
|
||||
margin-left: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Table = styled.table`
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 4px;
|
||||
`;
|
||||
|
||||
export const TableHead = styled.thead`
|
||||
font-size: 12px;
|
||||
`;
|
||||
|
||||
export const TableRow = styled.tr`
|
||||
display: table-row;
|
||||
`;
|
||||
|
||||
export const TableHeaderCell = styled.th`
|
||||
border-top: 1px solid ${(props) => props.theme.activeSessions.borderColor};
|
||||
border-bottom: 1px solid ${(props) => props.theme.activeSessions.borderColor};
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
padding: 14px 0;
|
||||
color: #a3a9ae;
|
||||
position: relative;
|
||||
border-top: 0;
|
||||
|
||||
:not(:first-child):before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 17px;
|
||||
left: -8px;
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
background: #d0d5da;
|
||||
}
|
||||
`;
|
||||
|
||||
export const TableBody = styled.tbody`
|
||||
font-size: 11px;
|
||||
`;
|
||||
|
||||
export const TableDataCell = styled.td`
|
||||
border-top: 1px solid ${(props) => props.theme.activeSessions.borderColor};
|
||||
border-bottom: 1px solid ${(props) => props.theme.activeSessions.borderColor};
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
padding: 14px 0;
|
||||
color: #a3a9ae;
|
||||
|
||||
@media ${hugeMobile} {
|
||||
.session-browser {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
max-width: 150px;
|
||||
display: inline-block;
|
||||
margin-left: 0 !important;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
span {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:first-child {
|
||||
font-size: 13px;
|
||||
color: ${(props) => props.theme.activeSessions.color};
|
||||
span {
|
||||
color: #a3a9ae;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.session-date {
|
||||
position: relative;
|
||||
margin-left: 0 !important;
|
||||
margin-right: 8px;
|
||||
:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: -8px;
|
||||
width: 1px;
|
||||
height: 12px;
|
||||
background: #d0d5da;
|
||||
}
|
||||
}
|
||||
|
||||
:last-child {
|
||||
text-align: center;
|
||||
}
|
||||
.remove-icon {
|
||||
svg {
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
path {
|
||||
fill: #a3a9ae;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
@ -1,2 +1,3 @@
|
||||
export { default as SectionHeaderContent } from "./Header";
|
||||
export { default as SectionBodyContent } from "./Body";
|
||||
export { default as SectionFooterContent } from "./Footer";
|
||||
|
@ -3,7 +3,12 @@ import PropTypes from "prop-types";
|
||||
import Section from "@docspace/common/components/Section";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
|
||||
import { SectionHeaderContent, SectionBodyContent } from "./Section";
|
||||
import {
|
||||
SectionHeaderContent,
|
||||
SectionBodyContent,
|
||||
SectionFooterContent,
|
||||
} from "./Section";
|
||||
|
||||
import { withRouter } from "react-router";
|
||||
import withCultureNames from "@docspace/common/hoc/withCultureNames";
|
||||
import { inject, observer } from "mobx-react";
|
||||
@ -80,7 +85,7 @@ class Profile extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
//console.log("Profile render");
|
||||
// console.log("Profile render");
|
||||
|
||||
const { profile, showCatalog } = this.props;
|
||||
|
||||
@ -93,6 +98,10 @@ class Profile extends React.Component {
|
||||
<Section.SectionBody>
|
||||
<SectionBodyContent profile={profile} />
|
||||
</Section.SectionBody>
|
||||
|
||||
<Section.SectionFooter>
|
||||
<SectionFooterContent profile={profile} />
|
||||
</Section.SectionFooter>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
471
packages/client/src/store/AccessRightsStore.js
Normal file
471
packages/client/src/store/AccessRightsStore.js
Normal file
@ -0,0 +1,471 @@
|
||||
import { makeAutoObservable } from "mobx";
|
||||
|
||||
import {
|
||||
EmployeeActivationStatus,
|
||||
EmployeeStatus,
|
||||
FolderType,
|
||||
ShareAccessRights,
|
||||
} from "@docspace/common/constants";
|
||||
|
||||
class AccessRightsStore {
|
||||
authStore = null;
|
||||
selectedFolderStore = null;
|
||||
|
||||
constructor(authStore, selectedFolderStore) {
|
||||
this.authStore = authStore;
|
||||
this.selectedFolderStore = selectedFolderStore;
|
||||
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
canEditRoom = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = this.selectedFolderStore;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canInviteUserInRoom = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = this.selectedFolderStore;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
get canChangeUserRoleInRoom() {}
|
||||
|
||||
canRemoveUserFromRoom = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canArchiveRoom = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canRemoveRoom = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
get canCreateFiles() {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { access, rootFolderType } = this.selectedFolderStore;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
access === ShareAccessRights.None ||
|
||||
access === ShareAccessRights.FullAccess ||
|
||||
access === ShareAccessRights.RoomManager ||
|
||||
isAdmin ||
|
||||
isOwner
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
canDownloadFiles = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canEditFile = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access ||
|
||||
ShareAccessRights.Editing === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canFillForm = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access ||
|
||||
ShareAccessRights.Editing === room.access ||
|
||||
ShareAccessRights.FormFilling === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canPeerReview = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access ||
|
||||
ShareAccessRights.Editing === room.access ||
|
||||
ShareAccessRights.FormFilling === room.access ||
|
||||
ShareAccessRights.Review === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canCommentFile = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (isAdmin || isOwner || ShareAccessRights.ReadOnly !== room.access)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canBlockFile = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canShowVersionHistory = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access ||
|
||||
ShareAccessRights.Editing === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canManageVersionHistory = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canMoveFile = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canDeleteFile = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canRenameFile = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canCopyFile = (room) => {
|
||||
const { isAdmin, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { rootFolderType } = room;
|
||||
|
||||
if (rootFolderType === FolderType.Archive) return false;
|
||||
|
||||
if (
|
||||
isAdmin ||
|
||||
isOwner ||
|
||||
ShareAccessRights.None === room.access ||
|
||||
ShareAccessRights.FullAccess === room.access ||
|
||||
ShareAccessRights.RoomManager === room.access
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canChangeUserType = (user) => {
|
||||
const { id, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const { id: userId, statusType, role } = user;
|
||||
|
||||
if (userId === id || statusType === EmployeeStatus.Disabled) return false;
|
||||
|
||||
switch (role) {
|
||||
case "owner":
|
||||
return false;
|
||||
|
||||
case "admin":
|
||||
case "manager":
|
||||
if (isOwner) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
case "user":
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
canMakeEmployeeUser = (user) => {
|
||||
const { id, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const {
|
||||
status,
|
||||
id: userId,
|
||||
isAdmin: userIsAdmin,
|
||||
isOwner: userIsOwner,
|
||||
isVisitor: userIsVisitor,
|
||||
} = user;
|
||||
|
||||
const needMakeEmployee =
|
||||
status !== EmployeeStatus.Disabled && userId !== id;
|
||||
|
||||
if (isOwner) return needMakeEmployee;
|
||||
|
||||
return needMakeEmployee && !userIsAdmin && !userIsOwner && userIsVisitor;
|
||||
};
|
||||
|
||||
canActivateUser = (user) => {
|
||||
const { id, isOwner, isAdmin } = this.authStore.userStore.user;
|
||||
|
||||
const {
|
||||
status,
|
||||
id: userId,
|
||||
isAdmin: userIsAdmin,
|
||||
isOwner: userIsOwner,
|
||||
} = user;
|
||||
|
||||
const needActivate = status !== EmployeeStatus.Active && userId !== id;
|
||||
|
||||
if (isOwner) return needActivate;
|
||||
|
||||
if (isAdmin) return needActivate && !userIsAdmin && !userIsOwner;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canDisableUser = (user) => {
|
||||
const { id, isOwner, isAdmin } = this.authStore.userStore.user;
|
||||
|
||||
const {
|
||||
status,
|
||||
id: userId,
|
||||
isAdmin: userIsAdmin,
|
||||
isOwner: userIsOwner,
|
||||
} = user;
|
||||
|
||||
const needDisable = status !== EmployeeStatus.Disabled && userId !== id;
|
||||
|
||||
if (isOwner) return needDisable;
|
||||
|
||||
if (isAdmin) return needDisable && !userIsAdmin && !userIsOwner;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canInviteUser = (user) => {
|
||||
const { id, isOwner } = this.authStore.userStore.user;
|
||||
|
||||
const {
|
||||
activationStatus,
|
||||
status,
|
||||
id: userId,
|
||||
isAdmin: userIsAdmin,
|
||||
isOwner: userIsOwner,
|
||||
} = user;
|
||||
|
||||
const needInvite =
|
||||
activationStatus === EmployeeActivationStatus.Pending &&
|
||||
status === EmployeeStatus.Active &&
|
||||
userId !== id;
|
||||
|
||||
if (isOwner) return needInvite;
|
||||
|
||||
return needInvite && !userIsAdmin && !userIsOwner;
|
||||
};
|
||||
|
||||
canRemoveUser = (user) => {
|
||||
const { id, isOwner, isAdmin } = this.authStore.userStore.user;
|
||||
|
||||
const {
|
||||
status,
|
||||
id: userId,
|
||||
isAdmin: userIsAdmin,
|
||||
isOwner: userIsOwner,
|
||||
} = user;
|
||||
|
||||
const needRemove = status === EmployeeStatus.Disabled && userId !== id;
|
||||
|
||||
if (isOwner) return needRemove;
|
||||
|
||||
if (isAdmin) return needRemove && !userIsAdmin && !userIsOwner;
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export default AccessRightsStore;
|
@ -1,7 +1,7 @@
|
||||
import { makeAutoObservable } from "mobx";
|
||||
|
||||
class BannerStore {
|
||||
isBannerVisible = false; //TODO: set to true after fix
|
||||
isBannerVisible = true;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
FileStatus,
|
||||
} from "@docspace/common/constants";
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import { TIMEOUT } from "@docspace/client/src/helpers/filesConstants";
|
||||
import { checkProtocol } from "../helpers/files-helpers";
|
||||
@ -26,6 +27,7 @@ import { AppServerConfig } from "@docspace/common/constants";
|
||||
import config from "PACKAGE_FILE";
|
||||
import FilesFilter from "@docspace/common/api/files/filter";
|
||||
import api from "@docspace/common/api";
|
||||
import { isTablet } from "@docspace/components/utils/device";
|
||||
|
||||
class FilesActionStore {
|
||||
authStore;
|
||||
@ -36,6 +38,7 @@ class FilesActionStore {
|
||||
settingsStore;
|
||||
dialogsStore;
|
||||
mediaViewerDataStore;
|
||||
accessRightsStore;
|
||||
|
||||
isBulkDownload = false;
|
||||
|
||||
@ -47,7 +50,8 @@ class FilesActionStore {
|
||||
selectedFolderStore,
|
||||
settingsStore,
|
||||
dialogsStore,
|
||||
mediaViewerDataStore
|
||||
mediaViewerDataStore,
|
||||
accessRightsStore
|
||||
) {
|
||||
makeAutoObservable(this);
|
||||
this.authStore = authStore;
|
||||
@ -58,6 +62,7 @@ class FilesActionStore {
|
||||
this.settingsStore = settingsStore;
|
||||
this.dialogsStore = dialogsStore;
|
||||
this.mediaViewerDataStore = mediaViewerDataStore;
|
||||
this.accessRightsStore = accessRightsStore;
|
||||
}
|
||||
|
||||
setIsBulkDownload = (isBulkDownload) => {
|
||||
@ -404,7 +409,7 @@ class FilesActionStore {
|
||||
});
|
||||
|
||||
try {
|
||||
await removeFiles(folderIds, null, true, true).then(async (res) => {
|
||||
await removeFiles(folderIds, [], true, true).then(async (res) => {
|
||||
if (res[0]?.error) return Promise.reject(res[0].error);
|
||||
const data = res[0] ? res[0] : null;
|
||||
const pbData = {
|
||||
@ -1252,46 +1257,70 @@ class FilesActionStore {
|
||||
};
|
||||
|
||||
isAvailableOption = (option) => {
|
||||
const {
|
||||
isFavoritesFolder,
|
||||
isRecentFolder,
|
||||
isCommonFolder,
|
||||
} = this.treeFoldersStore;
|
||||
const { isFavoritesFolder, isRecentFolder } = this.treeFoldersStore;
|
||||
const {
|
||||
isAccessedSelected,
|
||||
canConvertSelected,
|
||||
isThirdPartyRootSelection,
|
||||
hasSelection,
|
||||
allFilesIsEditing,
|
||||
selection,
|
||||
} = this.filesStore;
|
||||
const { personal } = this.authStore.settingsStore;
|
||||
const { userAccess } = this.filesStore;
|
||||
|
||||
const {
|
||||
canCopyFile,
|
||||
canDeleteFile,
|
||||
canMoveFile,
|
||||
canArchiveRoom,
|
||||
canRemoveRoom,
|
||||
} = this.accessRightsStore;
|
||||
const { access, rootFolderType } = this.selectedFolderStore;
|
||||
|
||||
switch (option) {
|
||||
case "showInfo":
|
||||
case "copy":
|
||||
const canCopy = canCopyFile({ access, rootFolderType });
|
||||
|
||||
return hasSelection && canCopy;
|
||||
case "showInfo":
|
||||
case "download":
|
||||
return hasSelection;
|
||||
case "downloadAs":
|
||||
return canConvertSelected;
|
||||
case "moveTo":
|
||||
const canMove = canMoveFile({ access, rootFolderType });
|
||||
return (
|
||||
!isThirdPartyRootSelection &&
|
||||
hasSelection &&
|
||||
isAccessedSelected &&
|
||||
!isRecentFolder &&
|
||||
!isFavoritesFolder &&
|
||||
!allFilesIsEditing
|
||||
!allFilesIsEditing &&
|
||||
canMove
|
||||
);
|
||||
|
||||
case "archive":
|
||||
case "unarchive":
|
||||
const canArchive = selection
|
||||
.map((s) => canArchiveRoom(s))
|
||||
.filter((s) => s);
|
||||
|
||||
return canArchive.length > 0;
|
||||
case "delete-room":
|
||||
const canRemove = selection
|
||||
.map((s) => canRemoveRoom(s))
|
||||
.filter((r) => r);
|
||||
|
||||
return canRemove.length > 0;
|
||||
|
||||
case "delete":
|
||||
const canDelete = canDeleteFile({ access, rootFolderType });
|
||||
const deleteCondition =
|
||||
!isThirdPartyRootSelection &&
|
||||
hasSelection &&
|
||||
isAccessedSelected &&
|
||||
!allFilesIsEditing;
|
||||
|
||||
return isCommonFolder ? userAccess && deleteCondition : deleteCondition;
|
||||
return canDelete && deleteCondition;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1373,6 +1402,14 @@ class FilesActionStore {
|
||||
this.deleteItemAction(items, translations, null, null, true);
|
||||
};
|
||||
|
||||
onShowInfoPanel = () => {
|
||||
const { selection } = this.filesStore;
|
||||
const { setSelection, setIsVisible } = this.authStore.infoPanelStore;
|
||||
|
||||
setSelection([selection]);
|
||||
setIsVisible(true);
|
||||
};
|
||||
|
||||
getOption = (option, t) => {
|
||||
const {
|
||||
setSharingPanelVisible,
|
||||
@ -1383,6 +1420,15 @@ class FilesActionStore {
|
||||
} = this.dialogsStore;
|
||||
|
||||
switch (option) {
|
||||
case "show-info":
|
||||
if (!isTablet() && !isMobile) return null;
|
||||
else
|
||||
return {
|
||||
key: "show-info",
|
||||
label: t("Common:Info"),
|
||||
iconUrl: "/static/images/info.outline.react.svg",
|
||||
onClick: this.onShowInfoPanel,
|
||||
};
|
||||
case "copy":
|
||||
if (!this.isAvailableOption("copy")) return null;
|
||||
else
|
||||
@ -1438,6 +1484,8 @@ class FilesActionStore {
|
||||
disabled: false,
|
||||
};
|
||||
case "archive":
|
||||
if (!this.isAvailableOption("archive")) return null;
|
||||
else
|
||||
return {
|
||||
key: "archive",
|
||||
label: t("Archived"),
|
||||
@ -1446,6 +1494,8 @@ class FilesActionStore {
|
||||
disabled: false,
|
||||
};
|
||||
case "unarchive":
|
||||
if (!this.isAvailableOption("unarchive")) return null;
|
||||
else
|
||||
return {
|
||||
key: "unarchive",
|
||||
label: t("Common:Restore"),
|
||||
@ -1454,7 +1504,7 @@ class FilesActionStore {
|
||||
disabled: false,
|
||||
};
|
||||
case "delete-room":
|
||||
if (!this.isAvailableOption("delete")) return null;
|
||||
if (!this.isAvailableOption("delete-room")) return null;
|
||||
else
|
||||
return {
|
||||
label: t("Common:Delete"),
|
||||
@ -1507,8 +1557,13 @@ class FilesActionStore {
|
||||
getArchiveRoomsFolderOptions = (itemsCollection, t) => {
|
||||
const archive = this.getOption("unarchive", t);
|
||||
const deleteOption = this.getOption("delete-room", t);
|
||||
const showOption = this.getOption("show-info", t);
|
||||
|
||||
itemsCollection
|
||||
.set("unarchive", archive)
|
||||
.set("show-info", showOption)
|
||||
.set("delete", deleteOption);
|
||||
|
||||
itemsCollection.set("unarchive", archive).set("delete", deleteOption);
|
||||
return this.convertToArray(itemsCollection);
|
||||
};
|
||||
|
||||
|
@ -38,6 +38,8 @@ class FilesStore {
|
||||
filesSettingsStore;
|
||||
thirdPartyStore;
|
||||
|
||||
accessRightsStore;
|
||||
|
||||
isLoaded = false;
|
||||
isLoading = false;
|
||||
|
||||
@ -97,7 +99,8 @@ class FilesStore {
|
||||
selectedFolderStore,
|
||||
treeFoldersStore,
|
||||
filesSettingsStore,
|
||||
thirdPartyStore
|
||||
thirdPartyStore,
|
||||
accessRightsStore
|
||||
) {
|
||||
const pathname = window.location.pathname.toLowerCase();
|
||||
this.isEditor = pathname.indexOf("doceditor") !== -1;
|
||||
@ -109,6 +112,7 @@ class FilesStore {
|
||||
this.treeFoldersStore = treeFoldersStore;
|
||||
this.filesSettingsStore = filesSettingsStore;
|
||||
this.thirdPartyStore = thirdPartyStore;
|
||||
this.accessRightsStore = accessRightsStore;
|
||||
|
||||
const { socketHelper, withPaging } = authStore.settingsStore;
|
||||
|
||||
@ -973,6 +977,29 @@ class FilesStore {
|
||||
removeOptions = (options, toRemoveArray) =>
|
||||
options.filter((o) => !toRemoveArray.includes(o));
|
||||
|
||||
removeSeparator = (options) => {
|
||||
const newOptions = options.map((o, index) => {
|
||||
if (index === 0 && o.includes("separator")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index === options.length - 1 && o.includes("separator")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
o?.includes("separator") &&
|
||||
options[index + 1].includes("separator")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return o;
|
||||
});
|
||||
|
||||
return newOptions.filter((o) => o);
|
||||
};
|
||||
|
||||
getFilesContextOptions = (item, canOpenPlayer) => {
|
||||
const isVisitor =
|
||||
(this.authStore.userStore.user &&
|
||||
@ -999,8 +1026,6 @@ class FilesStore {
|
||||
isRecycleBinFolder,
|
||||
isPrivacyFolder,
|
||||
isRecentFolder,
|
||||
isCommon,
|
||||
isShare,
|
||||
isFavoritesFolder,
|
||||
isShareFolder,
|
||||
isMy,
|
||||
@ -1013,15 +1038,40 @@ class FilesStore {
|
||||
canFormFillingDocs,
|
||||
} = this.filesSettingsStore;
|
||||
|
||||
const {
|
||||
canEditRoom,
|
||||
canInviteUserInRoom,
|
||||
canArchiveRoom,
|
||||
canRemoveRoom,
|
||||
canEditFile,
|
||||
canFillForm,
|
||||
|
||||
canBlockFile,
|
||||
canShowVersionHistory,
|
||||
canManageVersionHistory,
|
||||
canDeleteFile,
|
||||
canMoveFile,
|
||||
canRenameFile,
|
||||
canCopyFile,
|
||||
} = this.accessRightsStore;
|
||||
|
||||
const editFile = canEditFile(item);
|
||||
const fillForm = canFillForm(item);
|
||||
const blockFile = canBlockFile(item);
|
||||
const showVersionHistory = canShowVersionHistory(item);
|
||||
const manageVersionHistory = canManageVersionHistory(item);
|
||||
const deleteFile = canDeleteFile(item);
|
||||
const moveFile = canMoveFile(item);
|
||||
const renameFile = canRenameFile(item);
|
||||
const copyFile = canCopyFile(item);
|
||||
|
||||
const { enablePlugins } = this.authStore.settingsStore;
|
||||
|
||||
const isThirdPartyFolder =
|
||||
item.providerKey && item.id === item.rootFolderId;
|
||||
const isShareItem = isShare(item.rootFolderType);
|
||||
const isCommonFolder = isCommon(item.rootFolderType);
|
||||
|
||||
const isMyFolder = isMy(item.rootFolderType);
|
||||
|
||||
const { personal } = this.authStore.settingsStore;
|
||||
const { isDesktopClient } = this.authStore.settingsStore;
|
||||
|
||||
const pluginAllKeys =
|
||||
@ -1068,30 +1118,63 @@ class FilesStore {
|
||||
"restore",
|
||||
"rename",
|
||||
"separator2",
|
||||
"unsubscribe",
|
||||
// "unsubscribe",
|
||||
"delete",
|
||||
];
|
||||
|
||||
if (!editFile) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["edit"]);
|
||||
}
|
||||
if (!fillForm) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["fill-form"]);
|
||||
}
|
||||
if (!blockFile) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"block-unblock-version",
|
||||
]);
|
||||
}
|
||||
if (!showVersionHistory) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["show-version-history"]);
|
||||
}
|
||||
if (!manageVersionHistory) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["finalize-version"]);
|
||||
}
|
||||
if (!deleteFile) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["delete"]);
|
||||
}
|
||||
if (!moveFile) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["move-to"]);
|
||||
}
|
||||
if (!renameFile) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["rename"]);
|
||||
}
|
||||
if (!copyFile) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["copy-to", "copy"]);
|
||||
}
|
||||
if (!showVersionHistory && !manageVersionHistory) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["version"]);
|
||||
if (item.rootFolderType === FolderType.Archive) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["separator0"]);
|
||||
}
|
||||
}
|
||||
if (!moveFile && !copyFile) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["move"]);
|
||||
}
|
||||
|
||||
if (item.rootFolderType === FolderType.Archive) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"mark-read",
|
||||
"mark-as-favorite",
|
||||
"remove-from-favorites",
|
||||
]);
|
||||
}
|
||||
|
||||
if (!isMasterForm)
|
||||
fileOptions = this.removeOptions(fileOptions, ["make-form"]);
|
||||
|
||||
if (!shouldFillForm)
|
||||
fileOptions = this.removeOptions(fileOptions, ["fill-form"]);
|
||||
|
||||
if (personal) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"owner-change",
|
||||
"link-for-portal-users",
|
||||
"docu-sign",
|
||||
"mark-read",
|
||||
"unsubscribe",
|
||||
]);
|
||||
|
||||
if (!shouldEdit && !shouldView) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["sharing-settings"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!canConvert) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["download-as"]);
|
||||
}
|
||||
@ -1225,29 +1308,6 @@ class FilesStore {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFullAccess) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"finalize-version",
|
||||
"rename",
|
||||
"block-unblock-version",
|
||||
"copy",
|
||||
"sharing-settings",
|
||||
]);
|
||||
}
|
||||
|
||||
if (isVisitor) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"block-unblock-version",
|
||||
"finalize-version",
|
||||
"mark-as-favorite",
|
||||
"remove-from-favorites",
|
||||
]);
|
||||
|
||||
if (!isFullAccess) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["rename"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.canShareOwnerChange(item)) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["owner-change"]);
|
||||
}
|
||||
@ -1260,28 +1320,6 @@ class FilesStore {
|
||||
]);
|
||||
}
|
||||
|
||||
if (isCommonFolder) {
|
||||
if (!this.userAccess) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"owner-change",
|
||||
"move-to",
|
||||
"delete",
|
||||
"copy",
|
||||
"separator2",
|
||||
]);
|
||||
if (!isFavorite) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["separator2"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (withoutShare) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"sharing-settings",
|
||||
"external-link",
|
||||
]);
|
||||
}
|
||||
|
||||
if (!hasNew) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["mark-read"]);
|
||||
}
|
||||
@ -1296,22 +1334,6 @@ class FilesStore {
|
||||
fileOptions = this.removeOptions(fileOptions, ["open-location"]);
|
||||
}
|
||||
|
||||
if (isShareItem) {
|
||||
if (!isFullAccess) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["edit"]);
|
||||
}
|
||||
|
||||
if (isShareFolder) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"copy",
|
||||
"move-to",
|
||||
"delete",
|
||||
]);
|
||||
}
|
||||
} else if (!isEncrypted) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["unsubscribe"]);
|
||||
}
|
||||
|
||||
if (isPrivacyFolder) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"preview",
|
||||
@ -1331,20 +1353,15 @@ class FilesStore {
|
||||
);
|
||||
}
|
||||
|
||||
if (!shouldEdit && !shouldView && !fileOptions.includes("view")) {
|
||||
fileOptions = this.removeOptions(fileOptions, [
|
||||
"edit",
|
||||
"preview",
|
||||
"separator0",
|
||||
]);
|
||||
}
|
||||
|
||||
if (!shouldEdit && shouldView) {
|
||||
fileOptions = this.removeOptions(fileOptions, ["edit"]);
|
||||
}
|
||||
fileOptions = this.removeSeparator(fileOptions);
|
||||
|
||||
return fileOptions;
|
||||
} else if (isRoom) {
|
||||
const canEdit = canEditRoom(item);
|
||||
const canInviteUser = canInviteUserInRoom(item);
|
||||
const canArchive = canArchiveRoom(item);
|
||||
const canRemove = canRemoveRoom(item);
|
||||
|
||||
let roomOptions = [
|
||||
"select",
|
||||
"separator0",
|
||||
@ -1360,6 +1377,32 @@ class FilesStore {
|
||||
"delete",
|
||||
];
|
||||
|
||||
if (!canEdit) {
|
||||
roomOptions = this.removeOptions(roomOptions, [
|
||||
"edit-room",
|
||||
"reconnect-storage",
|
||||
]);
|
||||
}
|
||||
|
||||
if (!canInviteUser) {
|
||||
roomOptions = this.removeOptions(roomOptions, ["invite-users-to-room"]);
|
||||
}
|
||||
|
||||
if (!canArchive) {
|
||||
roomOptions = this.removeOptions(roomOptions, [
|
||||
"archive-room",
|
||||
"unarchive-room",
|
||||
]);
|
||||
}
|
||||
|
||||
if (!canRemove) {
|
||||
roomOptions = this.removeOptions(roomOptions, ["delete"]);
|
||||
}
|
||||
|
||||
if (!canRemove && !canArchive) {
|
||||
roomOptions = this.removeOptions(roomOptions, ["separator1"]);
|
||||
}
|
||||
|
||||
if (!item.providerKey) {
|
||||
roomOptions = this.removeOptions(roomOptions, ["reconnect-storage"]);
|
||||
}
|
||||
@ -1398,12 +1441,14 @@ class FilesStore {
|
||||
}
|
||||
}
|
||||
|
||||
roomOptions = this.removeSeparator(roomOptions);
|
||||
|
||||
return roomOptions;
|
||||
} else {
|
||||
let folderOptions = [
|
||||
"select",
|
||||
"open",
|
||||
"separator0",
|
||||
// "separator0",
|
||||
// "sharing-settings",
|
||||
"owner-change",
|
||||
"show-info",
|
||||
@ -1419,20 +1464,25 @@ class FilesStore {
|
||||
"rename",
|
||||
"change-thirdparty-info",
|
||||
"separator2",
|
||||
"unsubscribe",
|
||||
// "unsubscribe",
|
||||
"delete",
|
||||
];
|
||||
|
||||
if (personal) {
|
||||
folderOptions = this.removeOptions(folderOptions, [
|
||||
"sharing-settings",
|
||||
"owner-change",
|
||||
"link-for-portal-users",
|
||||
"separator1",
|
||||
"docu-sign",
|
||||
"mark-read",
|
||||
"unsubscribe",
|
||||
]);
|
||||
if (!deleteFile) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["delete"]);
|
||||
}
|
||||
if (!moveFile) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["move-to"]);
|
||||
}
|
||||
if (!renameFile) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["rename"]);
|
||||
}
|
||||
if (!copyFile) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["copy-to", "copy"]);
|
||||
}
|
||||
|
||||
if (!moveFile && !copyFile) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["move"]);
|
||||
}
|
||||
|
||||
if (isPrivacyFolder) {
|
||||
@ -1447,17 +1497,6 @@ class FilesStore {
|
||||
}
|
||||
}
|
||||
|
||||
if (isShareItem) {
|
||||
if (isShareFolder) {
|
||||
folderOptions = this.removeOptions(folderOptions, [
|
||||
"move-to",
|
||||
"delete",
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
folderOptions = this.removeOptions(folderOptions, ["unsubscribe"]);
|
||||
}
|
||||
|
||||
if (isRecycleBinFolder) {
|
||||
folderOptions = this.removeOptions(folderOptions, [
|
||||
"open",
|
||||
@ -1486,39 +1525,10 @@ class FilesStore {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFullAccess) {
|
||||
//TODO: if added Projects, add project folder check
|
||||
folderOptions = this.removeOptions(folderOptions, [
|
||||
"rename",
|
||||
"change-thirdparty-info",
|
||||
]);
|
||||
}
|
||||
|
||||
if (!this.canShareOwnerChange(item)) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["owner-change"]);
|
||||
}
|
||||
|
||||
if (!isFullAccess) {
|
||||
folderOptions = this.removeOptions(folderOptions, [
|
||||
"owner-change",
|
||||
"move-to",
|
||||
"delete",
|
||||
"change-thirdparty-info",
|
||||
]);
|
||||
|
||||
if (!isShareItem) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["separator2"]);
|
||||
}
|
||||
|
||||
if (isVisitor) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["rename"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (withoutShare) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["sharing-settings"]);
|
||||
}
|
||||
|
||||
if (!hasNew) {
|
||||
folderOptions = this.removeOptions(folderOptions, ["mark-read"]);
|
||||
}
|
||||
@ -1574,6 +1584,8 @@ class FilesStore {
|
||||
folderOptions = this.removeOptions(folderOptions, ["open-location"]);
|
||||
}
|
||||
|
||||
folderOptions = this.removeSeparator(folderOptions);
|
||||
|
||||
return folderOptions;
|
||||
}
|
||||
};
|
||||
|
@ -59,7 +59,7 @@ class HotkeyStore {
|
||||
activateHotkeys = (e) => {
|
||||
if (
|
||||
this.dialogsStore.someDialogIsOpen ||
|
||||
e.target?.tagName === "INPUT" ||
|
||||
(e.target?.tagName === "INPUT" && e.target.type !== "checkbox") ||
|
||||
e.target?.tagName === "TEXTAREA"
|
||||
)
|
||||
return e;
|
||||
@ -528,10 +528,14 @@ class HotkeyStore {
|
||||
|
||||
get caretIndex() {
|
||||
const { filesList, hotkeyCaret, selection } = this.filesStore;
|
||||
const item =
|
||||
selection.length && selection.length === 1 && !hotkeyCaret
|
||||
|
||||
const item = hotkeyCaret
|
||||
? hotkeyCaret
|
||||
: selection.length
|
||||
? selection.length === 1
|
||||
? selection[0]
|
||||
: hotkeyCaret;
|
||||
: selection[selection.length - 1]
|
||||
: null;
|
||||
|
||||
const caretIndex = filesList.findIndex(
|
||||
(f) => f.id === item?.id && f.isFolder === item?.isFolder
|
||||
|
@ -39,10 +39,11 @@ class PeopleStore {
|
||||
loadingStore = null;
|
||||
infoPanelStore = null;
|
||||
setupStore = null;
|
||||
accessRightsStore = null;
|
||||
isInit = false;
|
||||
viewAs = isMobileRDD ? "row" : "table";
|
||||
|
||||
constructor(authStore, infoPanelStore, setupStore) {
|
||||
constructor(authStore, infoPanelStore, setupStore, accessRightsStore) {
|
||||
this.authStore = authStore;
|
||||
this.groupsStore = new GroupsStore(this);
|
||||
this.usersStore = new UsersStore(this, authStore);
|
||||
@ -58,6 +59,7 @@ class PeopleStore {
|
||||
this.loadingStore = new LoadingStore();
|
||||
this.infoPanelStore = infoPanelStore;
|
||||
this.setupStore = setupStore;
|
||||
this.accessRightsStore = accessRightsStore;
|
||||
|
||||
this.contextOptionsStore = new AccountsContextOptionsStore(this);
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import {
|
||||
EmployeeStatus,
|
||||
EmployeeActivationStatus,
|
||||
} from "@docspace/common/constants";
|
||||
import { EmployeeStatus } from "@docspace/common/constants";
|
||||
import { getUserStatus } from "../helpers/people-helpers";
|
||||
|
||||
class SelectionStore {
|
||||
@ -102,47 +99,17 @@ class SelectionStore {
|
||||
}
|
||||
|
||||
get hasUsersToMakeEmployees() {
|
||||
const { id, isOwner } = this.peopleStore.authStore.userStore.user;
|
||||
const { canMakeEmployeeUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) => x.status !== EmployeeStatus.Disabled && x.id !== id
|
||||
);
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.status !== EmployeeStatus.Disabled &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner &&
|
||||
x.isVisitor
|
||||
);
|
||||
const users = this.selection.filter((x) => canMakeEmployeeUser(x));
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
get getUsersToMakeEmployees() {
|
||||
const { id, isOwner } = this.peopleStore.authStore.userStore.user;
|
||||
const { canMakeEmployeeUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) => x.status !== EmployeeStatus.Disabled && x.id !== id
|
||||
);
|
||||
|
||||
return users.map((u) => u);
|
||||
}
|
||||
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.status !== EmployeeStatus.Disabled &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner &&
|
||||
x.isVisitor
|
||||
);
|
||||
const users = this.selection.filter((x) => canMakeEmployeeUser(x));
|
||||
|
||||
return users.map((u) => u);
|
||||
}
|
||||
@ -156,211 +123,67 @@ class SelectionStore {
|
||||
}
|
||||
|
||||
get hasUsersToActivate() {
|
||||
const { id, isOwner, isAdmin } = this.peopleStore.authStore.userStore.user;
|
||||
const { canActivateUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) => x.status !== EmployeeStatus.Active && x.id !== id
|
||||
);
|
||||
const users = this.selection.filter((x) => canActivateUser(x));
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
if (isAdmin && !isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.status !== EmployeeStatus.Active &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner
|
||||
);
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
get getUsersToActivate() {
|
||||
const { id, isOwner, isAdmin } = this.peopleStore.authStore.userStore.user;
|
||||
const { canActivateUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) => x.status !== EmployeeStatus.Active && x.id !== id
|
||||
);
|
||||
const users = this.selection.filter((x) => canActivateUser(x));
|
||||
|
||||
return users.map((u) => u);
|
||||
}
|
||||
|
||||
if (isAdmin && !isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.status !== EmployeeStatus.Active &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner
|
||||
);
|
||||
|
||||
return users.map((u) => u);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
get hasUsersToDisable() {
|
||||
const { id, isOwner, isAdmin } = this.peopleStore.authStore.userStore.user;
|
||||
const { canDisableUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) => x.status !== EmployeeStatus.Disabled && x.id !== id
|
||||
);
|
||||
const users = this.selection.filter((x) => canDisableUser(x));
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
if (isAdmin && !isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.status !== EmployeeStatus.Disabled &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner
|
||||
);
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
get getUsersToDisable() {
|
||||
const { id, isOwner, isAdmin } = this.peopleStore.authStore.userStore.user;
|
||||
const { canDisableUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) => x.status !== EmployeeStatus.Disabled && x.id !== id
|
||||
);
|
||||
const users = this.selection.filter((x) => canDisableUser(x));
|
||||
|
||||
return users.map((u) => u);
|
||||
}
|
||||
|
||||
if (isAdmin && !isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.status !== EmployeeStatus.Disabled &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner
|
||||
);
|
||||
|
||||
return users.map((u) => u);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
get hasUsersToInvite() {
|
||||
const { id, isOwner, isAdmin } = this.peopleStore.authStore.userStore.user;
|
||||
const { canInviteUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.activationStatus === EmployeeActivationStatus.Pending &&
|
||||
x.status === EmployeeStatus.Active &&
|
||||
x.id !== id
|
||||
);
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.activationStatus === EmployeeActivationStatus.Pending &&
|
||||
x.status === EmployeeStatus.Active &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner
|
||||
);
|
||||
const users = this.selection.filter((x) => canInviteUser(x));
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
get getUsersToInviteIds() {
|
||||
const { id, isOwner, isAdmin } = this.peopleStore.authStore.userStore.user;
|
||||
const { canRemoveUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.activationStatus === EmployeeActivationStatus.Pending &&
|
||||
x.status === EmployeeStatus.Active &&
|
||||
x.id !== id
|
||||
);
|
||||
const users = this.selection.filter((x) => canRemoveUser(x));
|
||||
|
||||
return users.map((u) => u.id);
|
||||
}
|
||||
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.activationStatus === EmployeeActivationStatus.Pending &&
|
||||
x.status === EmployeeStatus.Active &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner
|
||||
);
|
||||
|
||||
return users.map((u) => u.id);
|
||||
return users.length > 0 ? users.map((u) => u.id) : false;
|
||||
}
|
||||
|
||||
get hasUsersToRemove() {
|
||||
const { id, isOwner, isAdmin } = this.peopleStore.authStore.userStore.user;
|
||||
const { canRemoveUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) => x.status === EmployeeStatus.Disabled && x.id !== id
|
||||
);
|
||||
const users = this.selection.filter((x) => canRemoveUser(x));
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
if (isAdmin && !isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.status === EmployeeStatus.Disabled &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner
|
||||
);
|
||||
|
||||
return users.length > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
get getUsersToRemoveIds() {
|
||||
const { id, isOwner, isAdmin } = this.peopleStore.authStore.userStore.user;
|
||||
const { canRemoveUser } = this.peopleStore.accessRightsStore;
|
||||
|
||||
if (isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) => x.status === EmployeeStatus.Disabled && x.id !== id
|
||||
);
|
||||
const users = this.selection.filter((x) => canRemoveUser(x));
|
||||
|
||||
return users.map((u) => u.id);
|
||||
}
|
||||
|
||||
if (isAdmin && !isOwner) {
|
||||
const users = this.selection.filter(
|
||||
(x) =>
|
||||
x.status === EmployeeStatus.Disabled &&
|
||||
x.id !== id &&
|
||||
!x.isAdmin &&
|
||||
!x.isOwner
|
||||
);
|
||||
|
||||
return users.map((u) => u.id);
|
||||
}
|
||||
|
||||
return false;
|
||||
return users.length > 0 ? users : false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@ class SettingsSetupStore {
|
||||
selectionStore = null;
|
||||
authStore = null;
|
||||
isInit = false;
|
||||
logoutVisible = false;
|
||||
logoutAllVisible = false;
|
||||
viewAs = isMobile ? "row" : "table";
|
||||
|
||||
security = {
|
||||
@ -339,6 +341,26 @@ class SettingsSetupStore {
|
||||
|
||||
this.setCommonThirdPartyList(res);
|
||||
};
|
||||
|
||||
getAllSessions = () => {
|
||||
return api.settings.getAllActiveSessions();
|
||||
};
|
||||
|
||||
removeAllSessions = () => {
|
||||
return api.settings.removeAllActiveSessions();
|
||||
};
|
||||
|
||||
removeAllExecptThis = () => {
|
||||
return api.settings.removeAllExceptThisSession();
|
||||
};
|
||||
|
||||
removeSession = (id) => {
|
||||
return api.settings.removeActiveSession(id);
|
||||
};
|
||||
|
||||
setLogoutVisible = (visible) => (this.logoutVisible = visible);
|
||||
|
||||
setLogoutAllVisible = (visible) => (this.logoutAllVisible = visible);
|
||||
}
|
||||
|
||||
export default SettingsSetupStore;
|
||||
|
@ -30,9 +30,14 @@ import selectFileDialogStore from "./SelectFileDialogStore";
|
||||
import TagsStore from "./TagsStore";
|
||||
import PeopleStore from "./PeopleStore";
|
||||
import OformsStore from "./OformsStore";
|
||||
import AccessRightsStore from "./AccessRightsStore";
|
||||
|
||||
const oformsStore = new OformsStore(authStore);
|
||||
|
||||
const selectedFolderStore = new SelectedFolderStore(authStore.settingsStore);
|
||||
|
||||
const accessRightsStore = new AccessRightsStore(authStore, selectedFolderStore);
|
||||
|
||||
const paymentStore = new PaymentStore();
|
||||
const wizardStore = new WizardStore();
|
||||
const setupStore = new SettingsSetupStore();
|
||||
@ -46,13 +51,12 @@ const ssoStore = new SsoFormStore();
|
||||
const peopleStore = new PeopleStore(
|
||||
authStore,
|
||||
authStore.infoPanelStore,
|
||||
setupStore
|
||||
setupStore,
|
||||
accessRightsStore
|
||||
);
|
||||
|
||||
const tagsStore = new TagsStore();
|
||||
|
||||
const selectedFolderStore = new SelectedFolderStore(authStore.settingsStore);
|
||||
|
||||
const treeFoldersStore = new TreeFoldersStore(selectedFolderStore);
|
||||
const settingsStore = new SettingsStore(thirdPartyStore, treeFoldersStore);
|
||||
|
||||
@ -61,7 +65,8 @@ const filesStore = new FilesStore(
|
||||
selectedFolderStore,
|
||||
treeFoldersStore,
|
||||
settingsStore,
|
||||
thirdPartyStore
|
||||
thirdPartyStore,
|
||||
accessRightsStore
|
||||
);
|
||||
|
||||
const mediaViewerDataStore = new MediaViewerDataStore(
|
||||
@ -97,7 +102,8 @@ const filesActionsStore = new FilesActionsStore(
|
||||
selectedFolderStore,
|
||||
settingsStore,
|
||||
dialogsStore,
|
||||
mediaViewerDataStore
|
||||
mediaViewerDataStore,
|
||||
accessRightsStore
|
||||
);
|
||||
|
||||
const contextOptionsStore = new ContextOptionsStore(
|
||||
@ -167,6 +173,8 @@ const store = {
|
||||
tagsStore,
|
||||
|
||||
peopleStore,
|
||||
|
||||
accessRightsStore,
|
||||
};
|
||||
|
||||
export default store;
|
||||
|
@ -135,6 +135,7 @@ class Filter {
|
||||
break;
|
||||
case "manager":
|
||||
dtoFilter.employeeType = 1;
|
||||
dtoFilter.isadministrator = "false";
|
||||
break;
|
||||
case "user":
|
||||
dtoFilter.employeeType = 2;
|
||||
|
@ -664,3 +664,32 @@ export function getPortalQuota() {
|
||||
url: `/settings/quota`,
|
||||
});
|
||||
}
|
||||
|
||||
export function getAllActiveSessions() {
|
||||
return request({
|
||||
method: "get",
|
||||
url: "/security/activeconnections",
|
||||
});
|
||||
}
|
||||
|
||||
export function removeAllActiveSessions() {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/security/activeconnections/logoutallchangepassword",
|
||||
});
|
||||
}
|
||||
|
||||
export function removeAllExceptThisSession() {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/security/activeconnections/logoutallexceptthis",
|
||||
});
|
||||
}
|
||||
|
||||
export function removeActiveSession(eventId) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: `/security/activeconnections/logout/${eventId}`,
|
||||
data: { eventId },
|
||||
});
|
||||
}
|
||||
|
@ -186,6 +186,7 @@ const FilterBlockItem = ({
|
||||
<StyledFilterBlockItemTagText
|
||||
noSelect={true}
|
||||
isSelected={item.isSelected}
|
||||
truncate
|
||||
>
|
||||
{item.label}
|
||||
</StyledFilterBlockItemTagText>
|
||||
|
@ -106,6 +106,9 @@ const StyledFilterBlockItem = styled.div`
|
||||
props.withoutHeader ? "0" : props.isFirst ? "12px 0 0 0" : "16px 0 0 0"};
|
||||
padding: 0 15px 0 16px;
|
||||
|
||||
max-width: 480px;
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
@ -165,6 +168,8 @@ const StyledFilterBlockItemTag = styled.div`
|
||||
height: 28px;
|
||||
max-height: 28px;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
@ -23,6 +23,8 @@ import InfoPanel from "./sub-components/info-panel";
|
||||
import SubInfoPanelBody from "./sub-components/info-panel-body";
|
||||
import SubInfoPanelHeader from "./sub-components/info-panel-header";
|
||||
|
||||
import SubSectionFooter from "./sub-components/section-footer";
|
||||
|
||||
import ReactResizeDetector from "react-resize-detector";
|
||||
import FloatingButton from "../FloatingButton";
|
||||
import { inject, observer } from "mobx-react";
|
||||
@ -50,6 +52,11 @@ function SectionBody() {
|
||||
}
|
||||
SectionBody.displayName = "SectionBody";
|
||||
|
||||
function SectionFooter() {
|
||||
return null;
|
||||
}
|
||||
SectionFooter.displayName = "SectionFooter";
|
||||
|
||||
function SectionPaging() {
|
||||
return null;
|
||||
}
|
||||
@ -69,6 +76,7 @@ class Section extends React.Component {
|
||||
static SectionHeader = SectionHeader;
|
||||
static SectionFilter = SectionFilter;
|
||||
static SectionBody = SectionBody;
|
||||
static SectionFooter = SectionFooter;
|
||||
|
||||
static SectionPaging = SectionPaging;
|
||||
static InfoPanelBody = InfoPanelBody;
|
||||
@ -159,6 +167,7 @@ class Section extends React.Component {
|
||||
let sectionFilterContent = null;
|
||||
let sectionPagingContent = null;
|
||||
let sectionBodyContent = null;
|
||||
let sectionFooterContent = null;
|
||||
let infoPanelBodyContent = null;
|
||||
let infoPanelHeaderContent = null;
|
||||
|
||||
@ -173,13 +182,15 @@ class Section extends React.Component {
|
||||
case SectionFilter.displayName:
|
||||
sectionFilterContent = child;
|
||||
break;
|
||||
|
||||
case SectionPaging.displayName:
|
||||
sectionPagingContent = child;
|
||||
break;
|
||||
case SectionBody.displayName:
|
||||
sectionBodyContent = child;
|
||||
break;
|
||||
case SectionFooter.displayName:
|
||||
sectionFooterContent = child;
|
||||
break;
|
||||
case InfoPanelBody.displayName:
|
||||
infoPanelBodyContent = child;
|
||||
break;
|
||||
@ -297,6 +308,12 @@ class Section extends React.Component {
|
||||
: null}
|
||||
</SubSectionBodyContent>
|
||||
|
||||
<SubSectionFooter>
|
||||
{sectionFooterContent
|
||||
? sectionFooterContent.props.children
|
||||
: null}
|
||||
</SubSectionFooter>
|
||||
|
||||
{isSectionPagingAvailable && (
|
||||
<SubSectionPaging>
|
||||
{sectionPagingContent
|
||||
@ -353,6 +370,7 @@ class Section extends React.Component {
|
||||
<></>
|
||||
)}
|
||||
</SectionContainer>
|
||||
|
||||
{isInfoPanelAvailable && (
|
||||
<InfoPanel viewAs={viewAs}>
|
||||
<SubInfoPanelHeader>
|
||||
@ -443,6 +461,7 @@ Section.SectionHeader = SectionHeader;
|
||||
Section.SectionFilter = SectionFilter;
|
||||
Section.SectionBody = SectionBody;
|
||||
Section.SectionPaging = SectionPaging;
|
||||
Section.SectionFooter = SectionFooter;
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
const { infoPanelStore, settingsStore } = auth;
|
||||
|
@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledSectionFooter = styled.div`
|
||||
margin-top: 40px;
|
||||
`;
|
||||
|
||||
const SectionFooter = ({ children }) => {
|
||||
return <StyledSectionFooter>{children}</StyledSectionFooter>;
|
||||
};
|
||||
|
||||
SectionFooter.displayName = "SectionFooter";
|
||||
|
||||
export default SectionFooter;
|
@ -63,6 +63,15 @@ const StyledDropdownIcon = styled(ReactSVG)`
|
||||
align-items: center;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
svg {
|
||||
path:first-child {
|
||||
stroke: ${(props) => props.theme.tag.color};
|
||||
}
|
||||
path:last-child {
|
||||
fill: ${(props) => props.theme.tag.color};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDropdownText = styled(Text)`
|
||||
|
@ -2983,6 +2983,11 @@ const Base = {
|
||||
},
|
||||
},
|
||||
|
||||
activeSessions: {
|
||||
color: "#333",
|
||||
borderColor: "#eceef1",
|
||||
},
|
||||
|
||||
formWrapper: {
|
||||
background: white,
|
||||
boxShadow: "0px 5px 20px rgba(4, 15, 27, 0.07)",
|
||||
|
@ -2988,6 +2988,11 @@ const Dark = {
|
||||
},
|
||||
},
|
||||
|
||||
activeSessions: {
|
||||
borderColor: "#858585",
|
||||
color: "#eeeeee",
|
||||
},
|
||||
|
||||
formWrapper: {
|
||||
background: black,
|
||||
boxShadow: "0px 5px 20px rgba(0, 0, 0, 0.16);",
|
||||
|
@ -120,6 +120,9 @@ public class FolderDtoHelper : FileEntryDtoHelper
|
||||
FolderType.CustomRoom => RoomType.CustomRoom,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
result.ParentId = folder.ProviderEntry && folder.RootFolderType is FolderType.VirtualRooms ? await _globalFolderHelper.GetFolderVirtualRooms<T>() :
|
||||
folder.ProviderEntry && folder.RootFolderType is FolderType.VirtualRooms ? await _globalFolderHelper.GetFolderVirtualRooms<T>() : folder.ParentId;
|
||||
}
|
||||
|
||||
if (folder.RootFolderType == FolderType.USER
|
||||
|
@ -27,7 +27,7 @@
|
||||
namespace ASC.Web.Files;
|
||||
|
||||
[Scope]
|
||||
public class FilesSpaceUsageStatManager : SpaceUsageStatManager
|
||||
public class FilesSpaceUsageStatManager : SpaceUsageStatManager, IUserSpaceUsage
|
||||
{
|
||||
private readonly IDbContextFactory<FilesDbContext> _dbContextFactory;
|
||||
private readonly TenantManager _tenantManager;
|
||||
@ -37,6 +37,9 @@ public class FilesSpaceUsageStatManager : SpaceUsageStatManager
|
||||
private readonly CommonLinkUtility _commonLinkUtility;
|
||||
private readonly GlobalFolderHelper _globalFolderHelper;
|
||||
private readonly PathProvider _pathProvider;
|
||||
private readonly IDaoFactory _daoFactory;
|
||||
private readonly GlobalFolder _globalFolder;
|
||||
private readonly FileMarker _fileMarker;
|
||||
|
||||
public FilesSpaceUsageStatManager(
|
||||
IDbContextFactory<FilesDbContext> dbContextFactory,
|
||||
@ -46,7 +49,10 @@ public class FilesSpaceUsageStatManager : SpaceUsageStatManager
|
||||
DisplayUserSettingsHelper displayUserSettingsHelper,
|
||||
CommonLinkUtility commonLinkUtility,
|
||||
GlobalFolderHelper globalFolderHelper,
|
||||
PathProvider pathProvider)
|
||||
PathProvider pathProvider,
|
||||
IDaoFactory daoFactory,
|
||||
GlobalFolder globalFolder,
|
||||
FileMarker fileMarker)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_tenantManager = tenantManager;
|
||||
@ -56,6 +62,9 @@ public class FilesSpaceUsageStatManager : SpaceUsageStatManager
|
||||
_commonLinkUtility = commonLinkUtility;
|
||||
_globalFolderHelper = globalFolderHelper;
|
||||
_pathProvider = pathProvider;
|
||||
_daoFactory = daoFactory;
|
||||
_globalFolder = globalFolder;
|
||||
_fileMarker = fileMarker;
|
||||
}
|
||||
|
||||
public override ValueTask<List<UsageSpaceStatItem>> GetStatDataAsync()
|
||||
@ -107,30 +116,7 @@ public class FilesSpaceUsageStatManager : SpaceUsageStatManager
|
||||
.ToListAsync();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class FilesUserSpaceUsage : IUserSpaceUsage
|
||||
{
|
||||
private readonly IDbContextFactory<FilesDbContext> _dbContextFactory;
|
||||
private readonly TenantManager _tenantManager;
|
||||
private readonly GlobalFolder _globalFolder;
|
||||
private readonly FileMarker _fileMarker;
|
||||
private readonly IDaoFactory _daoFactory;
|
||||
|
||||
public FilesUserSpaceUsage(
|
||||
IDbContextFactory<FilesDbContext> dbContextFactory,
|
||||
TenantManager tenantManager,
|
||||
GlobalFolder globalFolder,
|
||||
FileMarker fileMarker,
|
||||
IDaoFactory daoFactory)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_tenantManager = tenantManager;
|
||||
_globalFolder = globalFolder;
|
||||
_fileMarker = fileMarker;
|
||||
_daoFactory = daoFactory;
|
||||
}
|
||||
|
||||
public async Task<long> GetUserSpaceUsageAsync(Guid userId)
|
||||
{
|
||||
@ -143,4 +129,15 @@ public class FilesUserSpaceUsage : IUserSpaceUsage
|
||||
.Where(r => r.TenantId == tenantId && r.CreateBy == userId && (r.ParentId == my || r.ParentId == trash))
|
||||
.SumAsync(r => r.ContentLength);
|
||||
}
|
||||
|
||||
public async Task RecalculateUserQuota(int TenantId, Guid userId)
|
||||
{
|
||||
_tenantManager.SetCurrentTenant(TenantId);
|
||||
var size = await GetUserSpaceUsageAsync(userId);
|
||||
|
||||
_tenantManager.SetTenantQuotaRow(
|
||||
new TenantQuotaRow { Tenant = TenantId, Path = $"/{FileConstant.ModuleId}/", Counter = size, Tag = WebItemManager.DocumentsProductID.ToString(), UserId = userId, LastModified = DateTime.UtcNow },
|
||||
false);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public class ProductEntryPoint : Product
|
||||
{
|
||||
internal const string ProductPath = "/products/files/";
|
||||
|
||||
//public FilesSpaceUsageStatManager FilesSpaceUsageStatManager { get; }
|
||||
private readonly FilesSpaceUsageStatManager _filesSpaceUsageStatManager;
|
||||
private readonly CoreBaseSettings _coreBaseSettings;
|
||||
private readonly AuthContext _authContext;
|
||||
private readonly UserManager _userManager;
|
||||
@ -42,7 +42,7 @@ public class ProductEntryPoint : Product
|
||||
public ProductEntryPoint() { }
|
||||
|
||||
public ProductEntryPoint(
|
||||
// FilesSpaceUsageStatManager filesSpaceUsageStatManager,
|
||||
FilesSpaceUsageStatManager filesSpaceUsageStatManager,
|
||||
CoreBaseSettings coreBaseSettings,
|
||||
AuthContext authContext,
|
||||
UserManager userManager,
|
||||
@ -50,7 +50,7 @@ public class ProductEntryPoint : Product
|
||||
// SubscriptionManager subscriptionManager
|
||||
)
|
||||
{
|
||||
// FilesSpaceUsageStatManager = filesSpaceUsageStatManager;
|
||||
_filesSpaceUsageStatManager = filesSpaceUsageStatManager;
|
||||
_coreBaseSettings = coreBaseSettings;
|
||||
_authContext = authContext;
|
||||
_userManager = userManager;
|
||||
@ -83,7 +83,7 @@ public class ProductEntryPoint : Product
|
||||
LargeIconFileName = "images/files.svg",
|
||||
DefaultSortOrder = 10,
|
||||
//SubscriptionManager = SubscriptionManager,
|
||||
//SpaceUsageStatManager = FilesSpaceUsageStatManager,
|
||||
SpaceUsageStatManager = _filesSpaceUsageStatManager,
|
||||
AdminOpportunities = adminOpportunities,
|
||||
UserOpportunities = userOpportunities,
|
||||
CanNotBeDisabled = true,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user