Merge branch 'develop' into feature/webhooks-page

This commit is contained in:
Vladimir Khvan 2023-03-07 00:41:32 +05:00
commit b2546b1c96
302 changed files with 7282 additions and 6906 deletions

View File

@ -12,4 +12,4 @@ Write-Host "Run Document server" -ForegroundColor Green
$DOCUMENT_SERVER_IMAGE_NAME = "onlyoffice/documentserver-de:latest" $DOCUMENT_SERVER_IMAGE_NAME = "onlyoffice/documentserver-de:latest"
docker run -i -t -d -p 8085:80 -e JWT_ENABLED=false -e JWT_IN_BODY=false --restart=always -v $RootDir/Data:/var/www/onlyoffice/Data $DOCUMENT_SERVER_IMAGE_NAME docker run -i -t -d -p 8085:80 -e JWT_ENABLED=true -e JWT_SECRET=secret -e JWT_HEADER=AuthorizationJwt --restart=always -v $RootDir/Data:/var/www/onlyoffice/Data $DOCUMENT_SERVER_IMAGE_NAME

View File

@ -186,7 +186,7 @@ WORKDIR ${BUILD_PATH}/services/ASC.Data.Backup.BackgroundTasks/
COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py
COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Data.Backup.BackgroundTasks/service/ . COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Data.Backup.BackgroundTasks/service/ .
CMD ["ASC.Data.Backup.BackgroundTasks.dll", "ASC.Data.Backup.BackgroundTasks"] CMD ["ASC.Data.Backup.BackgroundTasks.dll", "ASC.Data.Backup.BackgroundTasks", "core:eventBus:subscriptionClientName=asc_event_bus_backup_queue"]
# ASC.ApiSystem ## # ASC.ApiSystem ##
FROM dotnetrun AS api_system FROM dotnetrun AS api_system
@ -231,7 +231,7 @@ WORKDIR ${BUILD_PATH}/products/ASC.Files/service/
COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py
COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Files.Service/service/ . COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Files.Service/service/ .
CMD ["ASC.Files.Service.dll", "ASC.Files.Service"] CMD ["ASC.Files.Service.dll", "ASC.Files.Service", "core:eventBus:subscriptionClientName=asc_event_bus_files_service_queue"]
## ASC.Notify ## ## ASC.Notify ##
FROM dotnetrun AS notify FROM dotnetrun AS notify
@ -240,7 +240,7 @@ WORKDIR ${BUILD_PATH}/services/ASC.Notify/service
COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py
COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Notify/service/ . COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Notify/service/ .
CMD ["ASC.Notify.dll", "ASC.Notify"] CMD ["ASC.Notify.dll", "ASC.Notify", "core:eventBus:subscriptionClientName=asc_event_bus_notify_queue"]
## ASC.People ## ## ASC.People ##
FROM dotnetrun AS people_server FROM dotnetrun AS people_server

View File

@ -38,8 +38,9 @@ ELK_PORT = os.environ["ELK_PORT"] if environ.get("ELK_PORT") else "9200"
ELK_THREADS = os.environ["ELK_THREADS"] if environ.get("ELK_THREADS") else "1" ELK_THREADS = os.environ["ELK_THREADS"] if environ.get("ELK_THREADS") else "1"
KAFKA_HOST = os.environ["KAFKA_HOST"] if environ.get("KAFKA_HOST") else "kafka:9092" KAFKA_HOST = os.environ["KAFKA_HOST"] if environ.get("KAFKA_HOST") else "kafka:9092"
RUN_FILE = sys.argv[1] if sys.argv[1] else "none" RUN_FILE = sys.argv[1] if (len(sys.argv) > 1) else "none"
LOG_FILE = sys.argv[2] if sys.argv[2] else "none" LOG_FILE = sys.argv[2] if (len(sys.argv) > 2) else "none"
CORE_EVENT_BUS = sys.argv[3] if (len(sys.argv) > 3) else ""
REDIS_HOST = os.environ["REDIS_HOST"] if environ.get("REDIS_HOST") else "onlyoffice-redis" REDIS_HOST = os.environ["REDIS_HOST"] if environ.get("REDIS_HOST") else "onlyoffice-redis"
REDIS_PORT = os.environ["REDIS_PORT"] if environ.get("REDIS_PORT") else "6379" REDIS_PORT = os.environ["REDIS_PORT"] if environ.get("REDIS_PORT") else "6379"
@ -84,7 +85,8 @@ class RunServices:
" --log:dir=" + LOG_DIR +\ " --log:dir=" + LOG_DIR +\
" --log:name=" + LOG_FILE +\ " --log:name=" + LOG_FILE +\
" core:products:folder=/var/www/products/" +\ " core:products:folder=/var/www/products/" +\
" core:products:subfolder=server") " core:products:subfolder=server" + " " +\
CORE_EVENT_BUS)
else: else:
os.system("dotnet " + RUN_FILE + " --urls=" + URLS + self.SERVICE_PORT +\ os.system("dotnet " + RUN_FILE + " --urls=" + URLS + self.SERVICE_PORT +\
" --\'$STORAGE_ROOT\'=" + APP_STORAGE_ROOT +\ " --\'$STORAGE_ROOT\'=" + APP_STORAGE_ROOT +\
@ -93,7 +95,8 @@ class RunServices:
" --log:name=" + LOG_FILE +\ " --log:name=" + LOG_FILE +\
" --ENVIRONMENT=" + ENV_EXTENSION +\ " --ENVIRONMENT=" + ENV_EXTENSION +\
" core:products:folder=/var/www/products/" +\ " core:products:folder=/var/www/products/" +\
" core:products:subfolder=server") " core:products:subfolder=server" + " " +\
CORE_EVENT_BUS)
def openJsonFile(filePath): def openJsonFile(filePath):
try: try:

View File

@ -57,13 +57,13 @@ public class LdapNotifyService : BackgroundService
{ {
var tId = t.Id; var tId = t.Id;
var ldapSettings = settingsManager.LoadForTenant<LdapSettings>(tId); var ldapSettings = settingsManager.Load<LdapSettings>(tId);
if (!ldapSettings.EnableLdapAuthentication) if (!ldapSettings.EnableLdapAuthentication)
{ {
continue; continue;
} }
var cronSettings = settingsManager.LoadForTenant<LdapCronSettings>(tId); var cronSettings = settingsManager.Load<LdapCronSettings>(tId);
if (string.IsNullOrEmpty(cronSettings.Cron)) if (string.IsNullOrEmpty(cronSettings.Cron))
{ {
continue; continue;
@ -103,13 +103,13 @@ public class LdapNotifyService : BackgroundService
{ {
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
var settingsManager = scope.ServiceProvider.GetRequiredService<SettingsManager>(); var settingsManager = scope.ServiceProvider.GetRequiredService<SettingsManager>();
var ldapSettings = settingsManager.LoadForTenant<LdapSettings>(tenant.Id); var ldapSettings = settingsManager.Load<LdapSettings>(tenant.Id);
if (!ldapSettings.EnableLdapAuthentication) if (!ldapSettings.EnableLdapAuthentication)
{ {
var cronSettings = settingsManager.LoadForTenant<LdapCronSettings>(tenant.Id); var cronSettings = settingsManager.Load<LdapCronSettings>(tenant.Id);
cronSettings.Cron = ""; cronSettings.Cron = "";
settingsManager.SaveForTenant(cronSettings, tenant.Id); settingsManager.Save(cronSettings, tenant.Id);
UnregisterAutoSync(tenant); UnregisterAutoSync(tenant);
return; return;
} }

View File

@ -151,7 +151,7 @@ public class LdapUserManager
var quotaSettings = _settingsManager.Load<TenantUserQuotaSettings>(); var quotaSettings = _settingsManager.Load<TenantUserQuotaSettings>();
if (quotaSettings.EnableUserQuota) if (quotaSettings.EnableUserQuota)
{ {
_settingsManager.SaveForUser(new UserQuotaSettings { UserQuota = ldapUserInfo.LdapQouta }, ldapUserInfo.Id); _settingsManager.Save(new UserQuotaSettings { UserQuota = ldapUserInfo.LdapQouta }, ldapUserInfo.Id);
} }

View File

@ -64,25 +64,26 @@ public abstract class BaseStartup
public virtual void ConfigureServices(IServiceCollection services) public virtual void ConfigureServices(IServiceCollection services)
{ {
services.AddCustomHealthCheck(_configuration); services.AddCustomHealthCheck(_configuration);
services.AddHttpContextAccessor(); services.AddHttpContextAccessor();
services.AddMemoryCache(); services.AddMemoryCache();
services.AddHttpClient(); services.AddHttpClient();
services.AddScoped<EFLoggerFactory>(); services.AddScoped<EFLoggerFactory>();
services.AddBaseDbContextPool<AccountLinkContext>();
services.AddBaseDbContextPool<CoreDbContext>(); services.AddBaseDbContextPool<AccountLinkContext>()
services.AddBaseDbContextPool<TenantDbContext>(); .AddBaseDbContextPool<CoreDbContext>()
services.AddBaseDbContextPool<UserDbContext>(); .AddBaseDbContextPool<TenantDbContext>()
services.AddBaseDbContextPool<TelegramDbContext>(); .AddBaseDbContextPool<UserDbContext>()
services.AddBaseDbContextPool<FirebaseDbContext>(); .AddBaseDbContextPool<TelegramDbContext>()
services.AddBaseDbContextPool<CustomDbContext>(); .AddBaseDbContextPool<FirebaseDbContext>()
services.AddBaseDbContextPool<WebstudioDbContext>(); .AddBaseDbContextPool<CustomDbContext>()
services.AddBaseDbContextPool<InstanceRegistrationContext>(); .AddBaseDbContextPool<WebstudioDbContext>()
services.AddBaseDbContextPool<IntegrationEventLogContext>(); .AddBaseDbContextPool<InstanceRegistrationContext>()
services.AddBaseDbContextPool<FeedDbContext>(); .AddBaseDbContextPool<IntegrationEventLogContext>()
services.AddBaseDbContextPool<MessagesContext>(); .AddBaseDbContextPool<FeedDbContext>()
services.AddBaseDbContextPool<WebhooksDbContext>(); .AddBaseDbContextPool<MessagesContext>()
.AddBaseDbContextPool<WebhooksDbContext>();
if (AddAndUseSession) if (AddAndUseSession)
{ {
@ -294,20 +295,35 @@ public abstract class BaseStartup
app.UseLoggerMiddleware(); app.UseLoggerMiddleware();
app.UseEndpoints(async endpoints => app.UseEndpoints(async endpoints =>
{ {
await endpoints.MapCustom(WebhooksEnabled, app.ApplicationServices); await endpoints.MapCustomAsync(WebhooksEnabled, app.ApplicationServices);
endpoints.MapHealthChecks("/health", new HealthCheckOptions() endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{ {
Predicate = _ => true, Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
}); });
endpoints.MapHealthChecks("/ready", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("services")
});
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
{ {
Predicate = r => r.Name.Contains("self") Predicate = r => r.Name.Contains("self")
}); });
}); });
app.Map("/switch", appBuilder =>
{
appBuilder.Run(async context =>
{
CustomHealthCheck.Running = !CustomHealthCheck.Running;
await context.Response.WriteAsync($"{Environment.MachineName} running {CustomHealthCheck.Running}");
});
});
} }
public void ConfigureContainer(ContainerBuilder builder) public void ConfigureContainer(ContainerBuilder builder)

View File

@ -83,7 +83,7 @@ public static class EndpointExtension
"DELETE" "DELETE"
}; };
public static async Task<IEndpointRouteBuilder> MapCustom(this IEndpointRouteBuilder endpoints, bool webhooksEnabled = false, IServiceProvider serviceProvider = null) public static async Task<IEndpointRouteBuilder> MapCustomAsync(this IEndpointRouteBuilder endpoints, bool webhooksEnabled = false, IServiceProvider serviceProvider = null)
{ {
endpoints.MapControllers(); endpoints.MapControllers();

View File

@ -23,67 +23,35 @@
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing // 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 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Api.Core.Core; namespace ASC.Api.Core.Core;
public static class CustomHealthCheck public static class CustomHealthCheck
{ {
public static bool Running { get; set;}
static CustomHealthCheck()
{
Running = true;
}
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration)
{ {
var hcBuilder = services.AddHealthChecks(); var hcBuilder = services.AddHealthChecks();
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy());
var configurationExtension = new ConfigurationExtension(configuration);
var connectionString = configurationExtension.GetConnectionStrings("default");
if (string.Equals(connectionString.ProviderName, "MySql.Data.MySqlClient")) hcBuilder.AddCheck("self", () => Running ? HealthCheckResult.Healthy()
{ : HealthCheckResult.Unhealthy())
hcBuilder.AddMySql(connectionString.ConnectionString, .AddDatabase(configuration)
name: "mysqldb", .AddDistibutedCache(configuration)
tags: new string[] { "mysqldb" }, .AddMessageQueue(configuration)
timeout: new TimeSpan(0, 0, 15)); .AddSearch(configuration);
}
return services;
if (string.Equals(connectionString.ProviderName, "Npgsql")) }
{
hcBuilder.AddNpgSql(connectionString.ConnectionString,
name: "postgredb",
tags: new string[] { "postgredb" },
timeout: new TimeSpan(0, 0, 15));
}
var kafkaSettings = configurationExtension.GetSetting<KafkaSettings>("kafka");
if (kafkaSettings != null && !string.IsNullOrEmpty(kafkaSettings.BootstrapServers))
{
var clientConfig = new ClientConfig { BootstrapServers = kafkaSettings.BootstrapServers };
hcBuilder.AddKafka(new ProducerConfig(clientConfig),
name: "kafka",
tags: new string[] { "kafka" },
timeout: new TimeSpan(0,0,15)
);
}
var elasticSettings = configuration.GetSection("elastic");
if (elasticSettings != null && elasticSettings.GetChildren().Any())
{
var host = elasticSettings.GetSection("Host").Value ?? "localhost";
var scheme = elasticSettings.GetSection("Scheme").Value ?? "http";
var port = elasticSettings.GetSection("Port").Value ?? "9200";
var elasticSearchUri = $"{scheme}://{host}:{port}";
if (Uri.IsWellFormedUriString(elasticSearchUri, UriKind.Absolute))
{
hcBuilder.AddElasticsearch(elasticSearchUri,
name: "elasticsearch",
tags: new string[] { "elasticsearch" });
}
}
public static IHealthChecksBuilder AddDistibutedCache(
this IHealthChecksBuilder hcBuilder, IConfiguration configuration)
{
var redisConfiguration = configuration.GetSection("Redis").Get<RedisConfiguration>(); var redisConfiguration = configuration.GetSection("Redis").Get<RedisConfiguration>();
if (redisConfiguration != null) if (redisConfiguration != null)
@ -97,20 +65,97 @@ public static class CustomHealthCheck
hcBuilder.AddRedis(redisConfiguration.ConfigurationOptions.ToString(), hcBuilder.AddRedis(redisConfiguration.ConfigurationOptions.ToString(),
name: "redis", name: "redis",
tags: new string[] { "redis" }, tags: new string[] { "redis", "services" },
timeout: new TimeSpan(0, 0, 15)); timeout: new TimeSpan(0, 0, 15));
} }
return hcBuilder;
}
public static IHealthChecksBuilder AddSearch(
this IHealthChecksBuilder hcBuilder, IConfiguration configuration)
{
var elasticSettings = configuration.GetSection("elastic");
if (elasticSettings != null && elasticSettings.GetChildren().Any())
{
var host = elasticSettings.GetSection("Host").Value ?? "localhost";
var scheme = elasticSettings.GetSection("Scheme").Value ?? "http";
var port = elasticSettings.GetSection("Port").Value ?? "9200";
var elasticSearchUri = $"{scheme}://{host}:{port}";
if (Uri.IsWellFormedUriString(elasticSearchUri, UriKind.Absolute))
{
hcBuilder.AddElasticsearch(elasticSearchUri,
name: "elasticsearch",
tags: new string[] { "elasticsearch", "services" },
timeout: new TimeSpan(0, 0, 15));
}
}
return hcBuilder;
}
public static IHealthChecksBuilder AddDatabase(
this IHealthChecksBuilder hcBuilder, IConfiguration configuration)
{
var configurationExtension = new ConfigurationExtension(configuration);
var connectionString = configurationExtension.GetConnectionStrings("default");
if (string.Equals(connectionString.ProviderName, "MySql.Data.MySqlClient"))
{
hcBuilder.AddMySql(connectionString.ConnectionString,
name: "mysqldb",
tags: new string[] { "mysqldb", "services" },
timeout: new TimeSpan(0, 0, 15));
}
else if (string.Equals(connectionString.ProviderName, "Npgsql"))
{
hcBuilder.AddNpgSql(connectionString.ConnectionString,
name: "postgredb",
tags: new string[] { "postgredb", "services" },
timeout: new TimeSpan(0, 0, 15));
}
return hcBuilder;
}
public static IHealthChecksBuilder AddMessageQueue(
this IHealthChecksBuilder hcBuilder, IConfiguration configuration)
{
var rabbitMQConfiguration = configuration.GetSection("RabbitMQ").Get<RabbitMQSettings>(); var rabbitMQConfiguration = configuration.GetSection("RabbitMQ").Get<RabbitMQSettings>();
if (rabbitMQConfiguration != null) if (rabbitMQConfiguration != null)
{ {
hcBuilder.AddRabbitMQ(x => rabbitMQConfiguration.GetConnectionFactory(), hcBuilder.AddRabbitMQ(x => rabbitMQConfiguration.GetConnectionFactory(),
name: "rabbitMQ", name: "rabbitMQ",
tags: new string[] { "rabbitMQ" }, tags: new string[] { "rabbitMQ", "services" },
timeout: new TimeSpan(0, 0, 15)); timeout: new TimeSpan(0, 0, 15));
} }
else
return services; {
} var configurationExtension = new ConfigurationExtension(configuration);
var kafkaSettings = configurationExtension.GetSetting<KafkaSettings>("kafka");
if (kafkaSettings != null && !string.IsNullOrEmpty(kafkaSettings.BootstrapServers))
{
var clientConfig = new ClientConfig { BootstrapServers = kafkaSettings.BootstrapServers };
hcBuilder.AddKafka(new ProducerConfig(clientConfig),
name: "kafka",
tags: new string[] { "kafka", "services" },
timeout: new TimeSpan(0, 0, 15)
);
}
}
return hcBuilder;
}
} }

View File

@ -29,11 +29,11 @@ public static class QuotaExtension
{ {
public static IServiceCollection RegisterFeature(this IServiceCollection services) public static IServiceCollection RegisterFeature(this IServiceCollection services)
{ {
services.AddScoped<ITenantQuotaFeatureChecker, CountRoomAdminChecker>(); services.AddScoped<ITenantQuotaFeatureChecker, CountPaidUserChecker>();
services.AddScoped<TenantQuotaFeatureCheckerCount<CountRoomAdminFeature>, CountRoomAdminChecker>(); services.AddScoped<TenantQuotaFeatureCheckerCount<CountPaidUserFeature>, CountPaidUserChecker>();
services.AddScoped<CountRoomAdminChecker>(); services.AddScoped<CountPaidUserChecker>();
services.AddScoped<ITenantQuotaFeatureStat<CountRoomAdminFeature, int>, CountRoomAdminStatistic>(); services.AddScoped<ITenantQuotaFeatureStat<CountPaidUserFeature, int>, CountPaidUserStatistic>();
services.AddScoped<CountRoomAdminStatistic>(); services.AddScoped<CountPaidUserStatistic>();
services.AddScoped<ITenantQuotaFeatureChecker, CountUserChecker>(); services.AddScoped<ITenantQuotaFeatureChecker, CountUserChecker>();
services.AddScoped<TenantQuotaFeatureCheckerCount<CountUserFeature>, CountUserChecker>(); services.AddScoped<TenantQuotaFeatureCheckerCount<CountUserFeature>, CountUserChecker>();

View File

@ -49,7 +49,7 @@ public static class ServiceCollectionExtension
{ {
services.AddSingleton(typeof(ICacheNotify<>), typeof(RabbitMQCache<>)); services.AddSingleton(typeof(ICacheNotify<>), typeof(RabbitMQCache<>));
} }
else if (kafkaConfiguration != null) else if (kafkaConfiguration != null && !string.IsNullOrEmpty(kafkaConfiguration.BootstrapServers))
{ {
services.AddSingleton(typeof(ICacheNotify<>), typeof(KafkaCacheNotify<>)); services.AddSingleton(typeof(ICacheNotify<>), typeof(KafkaCacheNotify<>));
} }

View File

@ -1,139 +1,140 @@
// (c) Copyright Ascensio System SIA 2010-2022 // (c) Copyright Ascensio System SIA 2010-2022
// //
// This program is a free software product. // This program is a free software product.
// You can redistribute it and/or modify it under the terms // 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 // 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 // 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 // to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights. // any third-party rights.
// //
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html // 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. // 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 // 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. // 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 // 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 // distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks. // trademark law for use of our trademarks.
// //
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing // 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 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Web.Api.Models; namespace ASC.Web.Api.Models;
public class EmployeeFullDto : EmployeeDto public class EmployeeFullDto : EmployeeDto
{ {
public string FirstName { get; set; } public string FirstName { get; set; }
public string LastName { get; set; } public string LastName { get; set; }
public string UserName { get; set; } public string UserName { get; set; }
public string Email { get; set; } public string Email { get; set; }
public List<Contact> Contacts { get; set; } public List<Contact> Contacts { get; set; }
public ApiDateTime Birthday { get; set; } public ApiDateTime Birthday { get; set; }
public string Sex { get; set; } public string Sex { get; set; }
public EmployeeStatus Status { get; set; } public EmployeeStatus Status { get; set; }
public EmployeeActivationStatus ActivationStatus { get; set; } public EmployeeActivationStatus ActivationStatus { get; set; }
public ApiDateTime Terminated { get; set; } public ApiDateTime Terminated { get; set; }
public string Department { get; set; } public string Department { get; set; }
public ApiDateTime WorkFrom { get; set; } public ApiDateTime WorkFrom { get; set; }
public List<GroupSummaryDto> Groups { get; set; } public List<GroupSummaryDto> Groups { get; set; }
public string Location { get; set; } public string Location { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
public string AvatarMax { get; set; } public string AvatarMax { get; set; }
public string AvatarMedium { get; set; } public string AvatarMedium { get; set; }
public string Avatar { get; set; } public string Avatar { get; set; }
public bool IsAdmin { get; set; } public bool IsAdmin { get; set; }
public bool IsLDAP { get; set; } public bool IsLDAP { get; set; }
public List<string> ListAdminModules { get; set; } public List<string> ListAdminModules { get; set; }
public bool IsOwner { get; set; } public bool IsOwner { get; set; }
public bool IsVisitor { get; set; } public bool IsVisitor { get; set; }
public string CultureName { get; set; } public bool IsCollaborator { get; set; }
public string MobilePhone { get; set; } public string CultureName { get; set; }
public MobilePhoneActivationStatus MobilePhoneActivationStatus { get; set; } public string MobilePhone { get; set; }
public MobilePhoneActivationStatus MobilePhoneActivationStatus { get; set; }
public bool IsSSO { get; set; } public bool IsSSO { get; set; }
public DarkThemeSettingsEnum? Theme { get; set; } public DarkThemeSettingsEnum? Theme { get; set; }
public long QuotaLimit { get; set; } public long QuotaLimit { get; set; }
public double UsedSpace { get; set; } public double UsedSpace { get; set; }
public static new EmployeeFullDto GetSample() public static new EmployeeFullDto GetSample()
{ {
return new EmployeeFullDto return new EmployeeFullDto
{ {
Avatar = "url to big avatar", Avatar = "url to big avatar",
AvatarSmall = "url to small avatar", AvatarSmall = "url to small avatar",
AvatarMax = "url to max avatar", AvatarMax = "url to max avatar",
Contacts = new List<Contact> { Contact.GetSample() }, Contacts = new List<Contact> { Contact.GetSample() },
Email = "my@gmail.com", Email = "my@gmail.com",
FirstName = "Mike", FirstName = "Mike",
Id = Guid.Empty, Id = Guid.Empty,
IsAdmin = false, IsAdmin = false,
ListAdminModules = new List<string> { "projects", "crm" }, ListAdminModules = new List<string> { "projects", "crm" },
UserName = "Mike.Zanyatski", UserName = "Mike.Zanyatski",
LastName = "Zanyatski", LastName = "Zanyatski",
Title = "Manager", Title = "Manager",
Groups = new List<GroupSummaryDto> { GroupSummaryDto.GetSample() }, Groups = new List<GroupSummaryDto> { GroupSummaryDto.GetSample() },
AvatarMedium = "url to medium avatar", AvatarMedium = "url to medium avatar",
Birthday = ApiDateTime.GetSample(), Birthday = ApiDateTime.GetSample(),
Department = "Marketing", Department = "Marketing",
Location = "Palo Alto", Location = "Palo Alto",
Notes = "Notes to worker", Notes = "Notes to worker",
Sex = "male", Sex = "male",
Status = EmployeeStatus.Active, Status = EmployeeStatus.Active,
WorkFrom = ApiDateTime.GetSample(), WorkFrom = ApiDateTime.GetSample(),
Terminated = ApiDateTime.GetSample(), Terminated = ApiDateTime.GetSample(),
CultureName = "en-EN", CultureName = "en-EN",
IsLDAP = false, IsLDAP = false,
IsSSO = false IsSSO = false
}; };
} }
} }
[Scope] [Scope]
public class EmployeeFullDtoHelper : EmployeeDtoHelper public class EmployeeFullDtoHelper : EmployeeDtoHelper
{ {
private readonly ApiContext _context; private readonly ApiContext _context;
private readonly WebItemSecurity _webItemSecurity; private readonly WebItemSecurity _webItemSecurity;
private readonly ApiDateTimeHelper _apiDateTimeHelper; private readonly ApiDateTimeHelper _apiDateTimeHelper;
private readonly WebItemManager _webItemManager; private readonly WebItemManager _webItemManager;
private readonly SettingsManager _settingsManager; private readonly SettingsManager _settingsManager;
private readonly IQuotaService _quotaService; private readonly IQuotaService _quotaService;
public EmployeeFullDtoHelper( public EmployeeFullDtoHelper(
ApiContext context, ApiContext context,
UserManager userManager, UserManager userManager,
UserPhotoManager userPhotoManager, UserPhotoManager userPhotoManager,
WebItemSecurity webItemSecurity, WebItemSecurity webItemSecurity,
CommonLinkUtility commonLinkUtility, CommonLinkUtility commonLinkUtility,
DisplayUserSettingsHelper displayUserSettingsHelper, DisplayUserSettingsHelper displayUserSettingsHelper,
ApiDateTimeHelper apiDateTimeHelper, ApiDateTimeHelper apiDateTimeHelper,
WebItemManager webItemManager, WebItemManager webItemManager,
SettingsManager settingsManager, SettingsManager settingsManager,
IQuotaService quotaService) IQuotaService quotaService)
: base(context, displayUserSettingsHelper, userPhotoManager, commonLinkUtility, userManager) : base(context, displayUserSettingsHelper, userPhotoManager, commonLinkUtility, userManager)
{ {
_context = context; _context = context;
_webItemSecurity = webItemSecurity; _webItemSecurity = webItemSecurity;
_apiDateTimeHelper = apiDateTimeHelper; _apiDateTimeHelper = apiDateTimeHelper;
_webItemManager = webItemManager; _webItemManager = webItemManager;
_settingsManager = settingsManager; _settingsManager = settingsManager;
_quotaService = quotaService; _quotaService = quotaService;
} }
public static Expression<Func<User, UserInfo>> GetExpression(ApiContext apiContext) public static Expression<Func<User, UserInfo>> GetExpression(ApiContext apiContext)
{ {
if (apiContext?.Fields == null) if (apiContext?.Fields == null)
{ {
return null; return null;
} }
var newExpr = Expression.New(typeof(UserInfo)); var newExpr = Expression.New(typeof(UserInfo));
//i => new UserInfo { ID = i.id } //i => new UserInfo { ID = i.id }
var parameter = Expression.Parameter(typeof(User), "i"); var parameter = Expression.Parameter(typeof(User), "i");
var bindExprs = new List<MemberAssignment>(); var bindExprs = new List<MemberAssignment>();
//foreach (var field in apiContext.Fields) //foreach (var field in apiContext.Fields)
@ -151,12 +152,12 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper
bindExprs.Add(Expression.Bind(typeof(UserInfo).GetProperty("Id"), bindExprs.Add(Expression.Bind(typeof(UserInfo).GetProperty("Id"),
Expression.Property(parameter, typeof(User).GetProperty("Id")))); Expression.Property(parameter, typeof(User).GetProperty("Id"))));
} }
var body = Expression.MemberInit(newExpr, bindExprs); var body = Expression.MemberInit(newExpr, bindExprs);
var lambda = Expression.Lambda<Func<User, UserInfo>>(body, parameter); var lambda = Expression.Lambda<Func<User, UserInfo>>(body, parameter);
return lambda; return lambda;
} }
public async Task<EmployeeFullDto> GetSimple(UserInfo userInfo) public async Task<EmployeeFullDto> GetSimple(UserInfo userInfo)
{ {
var result = new EmployeeFullDto var result = new EmployeeFullDto
@ -176,97 +177,97 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper
return result; return result;
} }
public async Task<EmployeeFullDto> GetFull(UserInfo userInfo) public async Task<EmployeeFullDto> GetFull(UserInfo userInfo)
{ {
var result = new EmployeeFullDto var result = new EmployeeFullDto
{ {
UserName = userInfo.UserName, UserName = userInfo.UserName,
FirstName = userInfo.FirstName, FirstName = userInfo.FirstName,
LastName = userInfo.LastName, LastName = userInfo.LastName,
Birthday = _apiDateTimeHelper.Get(userInfo.BirthDate), Birthday = _apiDateTimeHelper.Get(userInfo.BirthDate),
Status = userInfo.Status, Status = userInfo.Status,
ActivationStatus = userInfo.ActivationStatus & ~EmployeeActivationStatus.AutoGenerated, ActivationStatus = userInfo.ActivationStatus & ~EmployeeActivationStatus.AutoGenerated,
Terminated = _apiDateTimeHelper.Get(userInfo.TerminatedDate), Terminated = _apiDateTimeHelper.Get(userInfo.TerminatedDate),
WorkFrom = _apiDateTimeHelper.Get(userInfo.WorkFromDate), WorkFrom = _apiDateTimeHelper.Get(userInfo.WorkFromDate),
Email = userInfo.Email, Email = userInfo.Email,
IsVisitor = _userManager.IsUser(userInfo), IsVisitor = _userManager.IsUser(userInfo),
IsAdmin = _userManager.IsDocSpaceAdmin(userInfo), IsAdmin = _userManager.IsDocSpaceAdmin(userInfo),
IsOwner = userInfo.IsOwner(_context.Tenant), IsOwner = userInfo.IsOwner(_context.Tenant),
IsLDAP = userInfo.IsLDAP(), IsCollaborator = _userManager.IsCollaborator(userInfo),
IsLDAP = userInfo.IsLDAP(),
IsSSO = userInfo.IsSSO() IsSSO = userInfo.IsSSO()
}; };
await Init(result, userInfo); await Init(result, userInfo);
var quotaSettings = _settingsManager.Load<TenantUserQuotaSettings>(); var quotaSettings = _settingsManager.Load<TenantUserQuotaSettings>();
if (quotaSettings.EnableUserQuota) if (quotaSettings.EnableUserQuota)
{ {
result.UsedSpace = Math.Max(0, _quotaService.FindUserQuotaRows(_context.Tenant.Id, userInfo.Id).Where(r => !string.IsNullOrEmpty(r.Tag)).Sum(r => r.Counter)); result.UsedSpace = Math.Max(0, _quotaService.FindUserQuotaRows(_context.Tenant.Id, userInfo.Id).Where(r => !string.IsNullOrEmpty(r.Tag)).Sum(r => r.Counter));
var userQuotaSettings = _settingsManager.LoadForUser<UserQuotaSettings>(userInfo); var userQuotaSettings = _settingsManager.Load<UserQuotaSettings>(userInfo);
result.QuotaLimit = userQuotaSettings != null ? userQuotaSettings.UserQuota : quotaSettings.DefaultUserQuota; result.QuotaLimit = userQuotaSettings != null ? userQuotaSettings.UserQuota : quotaSettings.DefaultUserQuota;
} }
if (userInfo.Sex.HasValue) if (userInfo.Sex.HasValue)
{ {
result.Sex = userInfo.Sex.Value ? "male" : "female"; result.Sex = userInfo.Sex.Value ? "male" : "female";
} }
if (!string.IsNullOrEmpty(userInfo.Location)) if (!string.IsNullOrEmpty(userInfo.Location))
{ {
result.Location = userInfo.Location; result.Location = userInfo.Location;
} }
if (!string.IsNullOrEmpty(userInfo.Notes)) if (!string.IsNullOrEmpty(userInfo.Notes))
{ {
result.Notes = userInfo.Notes; result.Notes = userInfo.Notes;
} }
if (!string.IsNullOrEmpty(userInfo.MobilePhone)) if (!string.IsNullOrEmpty(userInfo.MobilePhone))
{ {
result.MobilePhone = userInfo.MobilePhone; result.MobilePhone = userInfo.MobilePhone;
} }
result.MobilePhoneActivationStatus = userInfo.MobilePhoneActivationStatus; result.MobilePhoneActivationStatus = userInfo.MobilePhoneActivationStatus;
if (!string.IsNullOrEmpty(userInfo.CultureName)) if (!string.IsNullOrEmpty(userInfo.CultureName))
{ {
result.CultureName = userInfo.CultureName; result.CultureName = userInfo.CultureName;
} }
FillConacts(result, userInfo); FillConacts(result, userInfo);
FillGroups(result, userInfo); FillGroups(result, userInfo);
var cacheKey = Math.Abs(userInfo.LastModified.GetHashCode()); var cacheKey = Math.Abs(userInfo.LastModified.GetHashCode());
if (_context.Check("avatarMax")) if (_context.Check("avatarMax"))
{ {
result.AvatarMax = await _userPhotoManager.GetMaxPhotoURL(userInfo.Id) + $"?hash={cacheKey}"; result.AvatarMax = await _userPhotoManager.GetMaxPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
} }
if (_context.Check("avatarMedium")) if (_context.Check("avatarMedium"))
{ {
result.AvatarMedium = await _userPhotoManager.GetMediumPhotoURL(userInfo.Id) + $"?hash={cacheKey}"; result.AvatarMedium = await _userPhotoManager.GetMediumPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
} }
if (_context.Check("avatar")) if (_context.Check("avatar"))
{ {
result.Avatar = await _userPhotoManager.GetBigPhotoURL(userInfo.Id) + $"?hash={cacheKey}"; result.Avatar = await _userPhotoManager.GetBigPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
} }
if (_context.Check("listAdminModules")) if (_context.Check("listAdminModules"))
{ {
var listAdminModules = userInfo.GetListAdminModules(_webItemSecurity, _webItemManager); var listAdminModules = userInfo.GetListAdminModules(_webItemSecurity, _webItemManager);
if (listAdminModules.Count > 0) if (listAdminModules.Count > 0)
{ {
result.ListAdminModules = listAdminModules; result.ListAdminModules = listAdminModules;
} }
} }
return result; return result;
} }
private void FillGroups(EmployeeFullDto result, UserInfo userInfo) private void FillGroups(EmployeeFullDto result, UserInfo userInfo)
{ {
@ -289,27 +290,27 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper
result.Department = ""; result.Department = "";
} }
} }
private void FillConacts(EmployeeFullDto employeeWraperFull, UserInfo userInfo) private void FillConacts(EmployeeFullDto employeeWraperFull, UserInfo userInfo)
{ {
if (userInfo.ContactsList == null) if (userInfo.ContactsList == null)
{ {
return; return;
}
var contacts = new List<Contact>();
for (var i = 0; i < userInfo.ContactsList.Count; i += 2)
{
if (i + 1 < userInfo.ContactsList.Count)
{
contacts.Add(new Contact(userInfo.ContactsList[i], userInfo.ContactsList[i + 1]));
}
} }
if (contacts.Count > 0) var contacts = new List<Contact>();
{
employeeWraperFull.Contacts = contacts; for (var i = 0; i < userInfo.ContactsList.Count; i += 2)
} {
} if (i + 1 < userInfo.ContactsList.Count)
{
contacts.Add(new Contact(userInfo.ContactsList[i], userInfo.ContactsList[i + 1]));
}
}
if (contacts.Count > 0)
{
employeeWraperFull.Contacts = contacts;
}
}
} }

View File

@ -106,3 +106,4 @@ global using RabbitMQ.Client.Events;
global using StackExchange.Redis.Extensions.Core.Abstractions; global using StackExchange.Redis.Extensions.Core.Abstractions;
global using ILogger = Microsoft.Extensions.Logging.ILogger; global using ILogger = Microsoft.Extensions.Logging.ILogger;
global using System.Threading.Channels;

View File

@ -28,19 +28,12 @@ namespace ASC.Common.Security.Authorizing;
public static class Constants public static class Constants
{ {
public static readonly Role DocSpaceAdmin = new Role(new Guid("cd84e66b-b803-40fc-99f9-b2969a54a1de"), "Admin"); public static readonly Role DocSpaceAdmin = new Role(new Guid("cd84e66b-b803-40fc-99f9-b2969a54a1de"), "DocSpaceAdmin");
public static readonly Role Everyone = new Role(new Guid("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), "Everyone"); public static readonly Role Everyone = new Role(new Guid("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), "Everyone");
public static readonly Role RoomAdmin = new Role(new Guid("abef62db-11a8-4673-9d32-ef1d8af19dc0"), "RoomAdmin");
public static readonly Role Collaborator = new Role(new Guid("88f11e7c-7407-4bea-b4cb-070010cdbb6b"), "Collaborator");
public static readonly Role RoomAdmin = new Role(new Guid("abef62db-11a8-4673-9d32-ef1d8af19dc0"), "User"); public static readonly Role User = new Role(new Guid("aced04fa-dd96-4b35-af3e-346bf1eb972d"), "User");
public static readonly Role User = new Role(new Guid("aced04fa-dd96-4b35-af3e-346bf1eb972d"), "Visitor");
public static readonly Role Member = new Role(new Guid("ba74ca02-873f-43dc-8470-8620c156bc67"), "Member"); public static readonly Role Member = new Role(new Guid("ba74ca02-873f-43dc-8470-8620c156bc67"), "Member");
public static readonly Role Owner = new Role(new Guid("bba32183-a14d-48ed-9d39-c6b4d8925fbf"), "Owner"); public static readonly Role Owner = new Role(new Guid("bba32183-a14d-48ed-9d39-c6b4d8925fbf"), "Owner");
public static readonly Role Self = new Role(new Guid("5d5b7260-f7f7-49f1-a1c9-95fbb6a12604"), "Self"); public static readonly Role Self = new Role(new Guid("5d5b7260-f7f7-49f1-a1c9-95fbb6a12604"), "Self");
} }

View File

@ -0,0 +1,114 @@
// (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.Common.Threading;
public static class ChannelExtension
{
public static IList<ChannelReader<T>> Split<T>(this ChannelReader<T> ch, int n, Func<int, int, T, int> selector = null, CancellationToken cancellationToken = default)
{
var outputs = new Channel<T>[n];
for (var i = 0; i < n; i++)
{
outputs[i] = Channel.CreateUnbounded<T>();
}
Task.Run(async () =>
{
try
{
cancellationToken.ThrowIfCancellationRequested();
var index = 0;
await foreach (var item in ch.ReadAllAsync(cancellationToken))
{
if (selector == null)
{
index = (index + 1) % n;
}
else
{
index = selector(n, index, item);
}
await outputs[index].Writer.WriteAsync(item, cancellationToken);
}
}
catch (OperationCanceledException)
{
// TODO: catch error via additional channel
// var error = Channel.CreateUnbounded<T>();
// await error.Writer.WriteAsync(ex);
}
finally
{
foreach (var ch in outputs)
{
ch.Writer.Complete();
}
}
});
return outputs.Select(ch => ch.Reader).ToArray();
}
public static ChannelReader<T> Merge<T>(this IEnumerable<ChannelReader<T>> inputs, CancellationToken cancellationToken = default)
{
var output = Channel.CreateUnbounded<T>();
Task.Run(async () =>
{
try
{
cancellationToken.ThrowIfCancellationRequested();
async Task Redirect(ChannelReader<T> input)
{
await foreach (var item in input.ReadAllAsync(cancellationToken))
{
await output.Writer.WriteAsync(item, cancellationToken);
}
}
await Task.WhenAll(inputs.Select(i => Redirect(i)).ToArray());
}
catch (OperationCanceledException)
{
// TODO: catch error via additional channel
// var error = Channel.CreateUnbounded<T>();
// await error.Writer.WriteAsync(ex);
}
finally
{
output.Writer.Complete();
}
});
return output;
}
}

View File

@ -39,16 +39,17 @@ public class SubscriptionManager
{ {
Constants.DocSpaceAdmin.ID, Constants.DocSpaceAdmin.ID,
Constants.Everyone.ID, Constants.Everyone.ID,
Constants.RoomAdmin.ID Constants.RoomAdmin.ID,
Constants.Collaborator.ID,
}; };
public SubscriptionManager(CachedSubscriptionService service, TenantManager tenantManager, ICache cache) public SubscriptionManager(CachedSubscriptionService service, TenantManager tenantManager, ICache cache)
{ {
_service = service ?? throw new ArgumentNullException("subscriptionManager"); _service = service ?? throw new ArgumentNullException("subscriptionManager");
_tenantManager = tenantManager; _tenantManager = tenantManager;
_cache = cache; _cache = cache;
} }
public void Subscribe(string sourceID, string actionID, string objectID, string recipientID) public void Subscribe(string sourceID, string actionID, string objectID, string recipientID)
{ {
var s = new SubscriptionRecord var s = new SubscriptionRecord
@ -79,7 +80,7 @@ public class SubscriptionManager
_service.SaveSubscription(s); _service.SaveSubscription(s);
} }
public void Unsubscribe(string sourceID, string actionID, string objectID, string recipientID) public void Unsubscribe(string sourceID, string actionID, string objectID, string recipientID)
{ {
var s = new SubscriptionRecord var s = new SubscriptionRecord
@ -110,17 +111,17 @@ public class SubscriptionManager
_service.SaveSubscription(s); _service.SaveSubscription(s);
} }
public void UnsubscribeAll(string sourceID, string actionID, string objectID) public void UnsubscribeAll(string sourceID, string actionID, string objectID)
{ {
_service.RemoveSubscriptions(GetTenant(), sourceID, actionID, objectID); _service.RemoveSubscriptions(GetTenant(), sourceID, actionID, objectID);
} }
public void UnsubscribeAll(string sourceID, string actionID) public void UnsubscribeAll(string sourceID, string actionID)
{ {
_service.RemoveSubscriptions(GetTenant(), sourceID, actionID); _service.RemoveSubscriptions(GetTenant(), sourceID, actionID);
} }
public string[] GetSubscriptionMethod(string sourceID, string actionID, string recipientID) public string[] GetSubscriptionMethod(string sourceID, string actionID, string recipientID)
{ {
IEnumerable<SubscriptionMethod> methods; IEnumerable<SubscriptionMethod> methods;
@ -144,27 +145,27 @@ public class SubscriptionManager
return m != null ? m.Methods : Array.Empty<string>(); return m != null ? m.Methods : Array.Empty<string>();
} }
public string[] GetRecipients(string sourceID, string actionID, string objectID) public string[] GetRecipients(string sourceID, string actionID, string objectID)
{ {
return _service.GetRecipients(GetTenant(), sourceID, actionID, objectID); return _service.GetRecipients(GetTenant(), sourceID, actionID, objectID);
} }
public object GetSubscriptionRecord(string sourceID, string actionID, string recipientID, string objectID) public object GetSubscriptionRecord(string sourceID, string actionID, string recipientID, string objectID)
{ {
return _service.GetSubscription(GetTenant(), sourceID, actionID, recipientID, objectID); return _service.GetSubscription(GetTenant(), sourceID, actionID, recipientID, objectID);
} }
public string[] GetSubscriptions(string sourceID, string actionID, string recipientID, bool checkSubscribe = true) public string[] GetSubscriptions(string sourceID, string actionID, string recipientID, bool checkSubscribe = true)
{ {
return _service.GetSubscriptions(GetTenant(), sourceID, actionID, recipientID, checkSubscribe); return _service.GetSubscriptions(GetTenant(), sourceID, actionID, recipientID, checkSubscribe);
} }
public bool IsUnsubscribe(string sourceID, string recipientID, string actionID, string objectID) public bool IsUnsubscribe(string sourceID, string recipientID, string actionID, string objectID)
{ {
return _service.IsUnsubscribe(GetTenant(), sourceID, actionID, recipientID, objectID); return _service.IsUnsubscribe(GetTenant(), sourceID, actionID, recipientID, objectID);
} }
public void UpdateSubscriptionMethod(string sourceID, string actionID, string recipientID, string[] senderNames) public void UpdateSubscriptionMethod(string sourceID, string actionID, string recipientID, string[] senderNames)
{ {
var m = new SubscriptionMethod var m = new SubscriptionMethod
@ -215,5 +216,5 @@ public class SubscriptionManager
private int GetTenant() private int GetTenant()
{ {
return _tenantManager.GetCurrentTenant().Id; return _tenantManager.GetCurrentTenant().Id;
} }
} }

View File

@ -45,6 +45,8 @@ public class TenantManager
internal CoreBaseSettings CoreBaseSettings { get; set; } internal CoreBaseSettings CoreBaseSettings { get; set; }
internal CoreSettings CoreSettings { get; set; } internal CoreSettings CoreSettings { get; set; }
private readonly static object _lock = new object();
static TenantManager() static TenantManager()
{ {
_thisCompAddresses.Add("localhost"); _thisCompAddresses.Add("localhost");
@ -333,7 +335,10 @@ public class TenantManager
public void SetTenantQuotaRow(TenantQuotaRow row, bool exchange) public void SetTenantQuotaRow(TenantQuotaRow row, bool exchange)
{ {
QuotaService.SetTenantQuotaRow(row, exchange); lock (_lock)
{
QuotaService.SetTenantQuotaRow(row, exchange);
}
} }
public List<TenantQuotaRow> FindTenantQuotaRows(int tenantId) public List<TenantQuotaRow> FindTenantQuotaRows(int tenantId)

View File

@ -61,7 +61,7 @@ public class UserManager
private readonly CardDavAddressbook _cardDavAddressbook; private readonly CardDavAddressbook _cardDavAddressbook;
private readonly ILogger<UserManager> _log; private readonly ILogger<UserManager> _log;
private readonly ICache _cache; private readonly ICache _cache;
private readonly TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> _countRoomAdminChecker; private readonly TenantQuotaFeatureCheckerCount<CountPaidUserFeature> _countPaidUserChecker;
private readonly TenantQuotaFeatureCheckerCount<CountUserFeature> _activeUsersFeatureChecker; private readonly TenantQuotaFeatureCheckerCount<CountUserFeature> _activeUsersFeatureChecker;
private readonly Constants _constants; private readonly Constants _constants;
private readonly UserFormatter _userFormatter; private readonly UserFormatter _userFormatter;
@ -86,7 +86,7 @@ public class UserManager
CardDavAddressbook cardDavAddressbook, CardDavAddressbook cardDavAddressbook,
ILogger<UserManager> log, ILogger<UserManager> log,
ICache cache, ICache cache,
TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> countRoomAdrminChecker, TenantQuotaFeatureCheckerCount<CountPaidUserFeature> countPaidUserChecker,
TenantQuotaFeatureCheckerCount<CountUserFeature> activeUsersFeatureChecker, TenantQuotaFeatureCheckerCount<CountUserFeature> activeUsersFeatureChecker,
UserFormatter userFormatter UserFormatter userFormatter
) )
@ -102,7 +102,7 @@ public class UserManager
_cardDavAddressbook = cardDavAddressbook; _cardDavAddressbook = cardDavAddressbook;
_log = log; _log = log;
_cache = cache; _cache = cache;
_countRoomAdminChecker = countRoomAdrminChecker; _countPaidUserChecker = countPaidUserChecker;
_activeUsersFeatureChecker = activeUsersFeatureChecker; _activeUsersFeatureChecker = activeUsersFeatureChecker;
_constants = _userManagerConstants.Constants; _constants = _userManagerConstants.Constants;
_userFormatter = userFormatter; _userFormatter = userFormatter;
@ -120,7 +120,7 @@ public class UserManager
CardDavAddressbook cardDavAddressbook, CardDavAddressbook cardDavAddressbook,
ILogger<UserManager> log, ILogger<UserManager> log,
ICache cache, ICache cache,
TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> tenantQuotaFeatureChecker, TenantQuotaFeatureCheckerCount<CountPaidUserFeature> tenantQuotaFeatureChecker,
TenantQuotaFeatureCheckerCount<CountUserFeature> activeUsersFeatureChecker, TenantQuotaFeatureCheckerCount<CountUserFeature> activeUsersFeatureChecker,
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
UserFormatter userFormatter) UserFormatter userFormatter)
@ -157,10 +157,16 @@ public class UserManager
switch (type) switch (type)
{ {
case EmployeeType.RoomAdmin: case EmployeeType.RoomAdmin:
users = users.Where(u => !this.IsUser(u)); users = users.Where(u => !this.IsUser(u) && !this.IsCollaborator(u) && !this.IsDocSpaceAdmin(u));
break;
case EmployeeType.DocSpaceAdmin:
users = users.Where(this.IsDocSpaceAdmin);
break;
case EmployeeType.Collaborator:
users = users.Where(this.IsCollaborator);
break; break;
case EmployeeType.User: case EmployeeType.User:
users = users.Where(u => this.IsUser(u)); users = users.Where(this.IsUser);
break; break;
} }
@ -354,15 +360,14 @@ public class UserManager
return newUser; return newUser;
} }
public async Task<UserInfo> SaveUserInfo(UserInfo u, bool isVisitor = false, bool syncCardDav = false) public async Task<UserInfo> SaveUserInfo(UserInfo u, EmployeeType type = EmployeeType.RoomAdmin, bool syncCardDav = false, bool paidUserQuotaCheck = true)
{ {
if (IsSystemUser(u.Id)) if (IsSystemUser(u.Id))
{ {
return SystemUsers[u.Id]; return SystemUsers[u.Id];
} }
_permissionContext.DemandPermissions(new UserSecurityProvider(u.Id, isVisitor ? EmployeeType.User : EmployeeType.RoomAdmin), _permissionContext.DemandPermissions(new UserSecurityProvider(u.Id, type), Constants.Action_AddRemoveUser);
Constants.Action_AddRemoveUser);
if (!_coreBaseSettings.Personal) if (!_coreBaseSettings.Personal)
{ {
@ -379,13 +384,13 @@ public class UserManager
throw new InvalidOperationException("User already exist."); throw new InvalidOperationException("User already exist.");
} }
if (isVisitor) if (type is EmployeeType.User)
{ {
await _activeUsersFeatureChecker.CheckAppend(); await _activeUsersFeatureChecker.CheckAppend();
} }
else else if (paidUserQuotaCheck)
{ {
await _countRoomAdminChecker.CheckAppend(); await _countPaidUserChecker.CheckAppend();
} }
var newUser = _userService.SaveUser(_tenantManager.GetCurrentTenant().Id, u); var newUser = _userService.SaveUser(_tenantManager.GetCurrentTenant().Id, u);
@ -886,16 +891,23 @@ public class UserManager
} }
UserGroupRef r; UserGroupRef r;
if (groupId == Constants.GroupManager.ID || groupId == Constants.GroupUser.ID) if (groupId == Constants.GroupManager.ID || groupId == Constants.GroupUser.ID || groupId == Constants.GroupCollaborator.ID)
{ {
var user = refs.TryGetValue(UserGroupRef.CreateKey(Tenant.Id, userId, Constants.GroupUser.ID, UserGroupRefType.Contains), out r) && !r.Removed; var isUser = refs.TryGetValue(UserGroupRef.CreateKey(Tenant.Id, userId, Constants.GroupUser.ID, UserGroupRefType.Contains), out r) && !r.Removed;
if (groupId == Constants.GroupUser.ID) if (groupId == Constants.GroupUser.ID)
{ {
return user; return isUser;
} }
var isCollaborator = refs.TryGetValue(UserGroupRef.CreateKey(Tenant.Id, userId, Constants.GroupCollaborator.ID, UserGroupRefType.Contains), out r) && !r.Removed;
if (groupId == Constants.GroupCollaborator.ID)
{
return isCollaborator;
}
if (groupId == Constants.GroupManager.ID) if (groupId == Constants.GroupManager.ID)
{ {
return !user; return !isUser && !isCollaborator;
} }
} }

View File

@ -38,4 +38,5 @@ public enum EmployeeType
RoomAdmin = 1, RoomAdmin = 1,
User = 2, User = 2,
DocSpaceAdmin = 3, DocSpaceAdmin = 3,
} Collaborator = 4,
}

View File

@ -88,16 +88,9 @@ public class DbLoginEventsManager
{ {
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync(); using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var events = await loginEventContext.LoginEvents await loginEventContext.LoginEvents
.Where(r => r.Id == loginEventId) .Where(r => r.Id == loginEventId)
.ToListAsync(); .ExecuteUpdateAsync(r => r.SetProperty(p => p.Active, false));
foreach (var e in events)
{
e.Active = false;
}
await loginEventContext.SaveChangesAsync();
ResetCache(); ResetCache();
} }
@ -106,16 +99,9 @@ public class DbLoginEventsManager
{ {
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync(); using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var events = await loginEventContext.LoginEvents await loginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && r.Active) .Where(r => r.TenantId == tenantId && r.UserId == userId && r.Active)
.ToListAsync(); .ExecuteUpdateAsync(r => r.SetProperty(p => p.Active, false));
foreach (var e in events)
{
e.Active = false;
}
await loginEventContext.SaveChangesAsync();
ResetCache(tenantId, userId); ResetCache(tenantId, userId);
} }
@ -124,32 +110,18 @@ public class DbLoginEventsManager
{ {
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync(); using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var events = await loginEventContext.LoginEvents await loginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.Active) .Where(r => r.TenantId == tenantId && r.Active)
.ToListAsync(); .ExecuteUpdateAsync(r => r.SetProperty(p => p.Active, false));
foreach (var e in events)
{
e.Active = false;
}
await loginEventContext.SaveChangesAsync();
} }
public async Task LogOutAllActiveConnectionsExceptThis(int loginEventId, int tenantId, Guid userId) public async Task LogOutAllActiveConnectionsExceptThis(int loginEventId, int tenantId, Guid userId)
{ {
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync(); using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var events = await loginEventContext.LoginEvents await loginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && r.Id != loginEventId && r.Active) .Where(r => r.TenantId == tenantId && r.UserId == userId && r.Id != loginEventId && r.Active)
.ToListAsync(); .ExecuteUpdateAsync(r => r.SetProperty(p => p.Active, false));
foreach (var e in events)
{
e.Active = false;
}
await loginEventContext.SaveChangesAsync();
ResetCache(tenantId, userId); ResetCache(tenantId, userId);
} }

View File

@ -24,7 +24,7 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Data; namespace ASC.Core.Common.Settings;
[Singletone] [Singletone]
public class DbSettingsManagerCache public class DbSettingsManagerCache
@ -46,11 +46,11 @@ public class DbSettingsManagerCache
} }
[Scope] [Scope]
public class DbSettingsManager public class SettingsManager
{ {
private readonly TimeSpan _expirationTimeout = TimeSpan.FromMinutes(5); private readonly TimeSpan _expirationTimeout = TimeSpan.FromMinutes(5);
private readonly ILogger<DbSettingsManager> _logger; private readonly ILogger<SettingsManager> _logger;
private readonly ICache _cache; private readonly ICache _cache;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly DbSettingsManagerCache _dbSettingsManagerCache; private readonly DbSettingsManagerCache _dbSettingsManagerCache;
@ -58,10 +58,10 @@ public class DbSettingsManager
private readonly TenantManager _tenantManager; private readonly TenantManager _tenantManager;
private readonly IDbContextFactory<WebstudioDbContext> _dbContextFactory; private readonly IDbContextFactory<WebstudioDbContext> _dbContextFactory;
public DbSettingsManager( public SettingsManager(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
DbSettingsManagerCache dbSettingsManagerCache, DbSettingsManagerCache dbSettingsManagerCache,
ILogger<DbSettingsManager> logger, ILogger<SettingsManager> logger,
AuthContext authContext, AuthContext authContext,
TenantManager tenantManager, TenantManager tenantManager,
IDbContextFactory<WebstudioDbContext> dbContextFactory) IDbContextFactory<WebstudioDbContext> dbContextFactory)
@ -100,96 +100,100 @@ public class DbSettingsManager
} }
} }
public bool SaveSettings<T>(T settings, int tenantId) where T : class, ISettings<T> public void ClearCache<T>() where T : class, ISettings<T>
{ {
return SaveSettingsFor(settings, tenantId, Guid.Empty); ClearCache<T>(TenantID);
}
public T LoadSettings<T>(int tenantId) where T : class, ISettings<T>
{
return LoadSettingsFor<T>(tenantId, Guid.Empty);
} }
public void ClearCache<T>(int tenantId) where T : class, ISettings<T> public void ClearCache<T>(int tenantId) where T : class, ISettings<T>
{ {
var settings = LoadSettings<T>(tenantId); var settings = Load<T>(tenantId, Guid.Empty);
var key = settings.ID.ToString() + tenantId + Guid.Empty; var key = settings.ID.ToString() + tenantId + Guid.Empty;
_dbSettingsManagerCache.Remove(key); _dbSettingsManagerCache.Remove(key);
} }
public T GetDefault<T>() where T : class, ISettings<T>
public bool SaveSettingsFor<T>(T settings, int tenantId, Guid userId) where T : class, ISettings<T>
{ {
ArgumentNullException.ThrowIfNull(settings); var settingsInstance = ActivatorUtilities.CreateInstance<T>(_serviceProvider);
return settingsInstance.GetDefault();
using var webstudioDbContext = _dbContextFactory.CreateDbContext();
try
{
var key = settings.ID.ToString() + tenantId + userId;
var data = Serialize(settings);
var def = GetDefault<T>();
var defaultData = Serialize(def);
if (data.SequenceEqual(defaultData))
{
var strategy = webstudioDbContext.Database.CreateExecutionStrategy();
strategy.Execute(() =>
{
using var tr = webstudioDbContext.Database.BeginTransaction();
// remove default settings
var s = webstudioDbContext.WebstudioSettings
.Where(r => r.Id == settings.ID)
.Where(r => r.TenantId == tenantId)
.Where(r => r.UserId == userId)
.FirstOrDefault();
if (s != null)
{
webstudioDbContext.WebstudioSettings.Remove(s);
}
webstudioDbContext.SaveChanges();
tr.Commit();
});
}
else
{
var s = new DbWebstudioSettings
{
Id = settings.ID,
UserId = userId,
TenantId = tenantId,
Data = data
};
webstudioDbContext.AddOrUpdate(webstudioDbContext.WebstudioSettings, s);
webstudioDbContext.SaveChanges();
}
_dbSettingsManagerCache.Remove(key);
_cache.Insert(key, settings, _expirationTimeout);
return true;
}
catch (Exception ex)
{
_logger.ErrorSaveSettingsFor(ex);
return false;
}
} }
internal T LoadSettingsFor<T>(int tenantId, Guid userId) where T : class, ISettings<T> public T Load<T>() where T : class, ISettings<T>
{
return Load<T>(TenantID, Guid.Empty);
}
public T Load<T>(Guid userId) where T : class, ISettings<T>
{
return Load<T>(TenantID, userId);
}
public T Load<T>(UserInfo user) where T : class, ISettings<T>
{
return Load<T>(TenantID, user.Id);
}
public T Load<T>(int tenantId) where T : class, ISettings<T>
{
return Load<T>(tenantId, Guid.Empty);
}
public T LoadForDefaultTenant<T>() where T : class, ISettings<T>
{
return Load<T>(Tenant.DefaultTenant);
}
public T LoadForCurrentUser<T>() where T : class, ISettings<T>
{
return Load<T>(CurrentUserID);
}
public bool Save<T>(T data) where T : class, ISettings<T>
{
return Save(data, TenantID, Guid.Empty);
}
public bool Save<T>(T data, Guid userId) where T : class, ISettings<T>
{
return Save(data, TenantID, userId);
}
public bool Save<T>(T data, UserInfo user) where T : class, ISettings<T>
{
return Save(data, TenantID, user.Id);
}
public bool Save<T>(T data, int tenantId) where T : class, ISettings<T>
{
return Save(data, tenantId, Guid.Empty);
}
public bool SaveForDefaultTenant<T>(T data) where T : class, ISettings<T>
{
return Save(data, Tenant.DefaultTenant);
}
public bool SaveForCurrentUser<T>(T data) where T : class, ISettings<T>
{
return Save(data, CurrentUserID);
}
public bool Manage<T>(Action<T> action) where T : class, ISettings<T>
{
var settings = Load<T>();
action(settings);
return Save(settings);
}
public bool ManageForCurrentUser<T>(Action<T> action) where T : class, ISettings<T>
{
var settings = LoadForCurrentUser<T>();
action(settings);
return SaveForCurrentUser(settings);
}
internal T Load<T>(int tenantId, Guid userId) where T : class, ISettings<T>
{ {
var def = GetDefault<T>(); var def = GetDefault<T>();
var key = def.ID.ToString() + tenantId + userId; var key = def.ID.ToString() + tenantId + userId;
@ -231,75 +235,71 @@ public class DbSettingsManager
return def; return def;
} }
public T GetDefault<T>() where T : class, ISettings<T> private bool Save<T>(T settings, int tenantId, Guid userId) where T : class, ISettings<T>
{ {
var settingsInstance = ActivatorUtilities.CreateInstance<T>(_serviceProvider); ArgumentNullException.ThrowIfNull(settings);
return settingsInstance.GetDefault();
}
public T Load<T>() where T : class, ISettings<T> using var webstudioDbContext = _dbContextFactory.CreateDbContext();
{
return LoadSettings<T>(TenantID);
}
public T LoadForCurrentUser<T>() where T : class, ISettings<T> try
{ {
return LoadForUser<T>(CurrentUserID); var key = settings.ID.ToString() + tenantId + userId;
} var data = Serialize(settings);
var def = GetDefault<T>();
public T LoadForUser<T>(Guid userId) where T : class, ISettings<T> var defaultData = Serialize(def);
{
return LoadSettingsFor<T>(TenantID, userId);
}
public T LoadForUser<T>(UserInfo user) where T : class, ISettings<T> if (data.SequenceEqual(defaultData))
{ {
return LoadSettingsFor<T>(TenantID, user.Id); var strategy = webstudioDbContext.Database.CreateExecutionStrategy();
}
public T LoadForDefaultTenant<T>() where T : class, ISettings<T> strategy.Execute(() =>
{ {
return LoadForTenant<T>(Tenant.DefaultTenant); using var tr = webstudioDbContext.Database.BeginTransaction();
} // remove default settings
var s = webstudioDbContext.WebstudioSettings
.Where(r => r.Id == settings.ID)
.Where(r => r.TenantId == tenantId)
.Where(r => r.UserId == userId)
.FirstOrDefault();
public T LoadForTenant<T>(int tenantId) where T : class, ISettings<T> if (s != null)
{ {
return LoadSettings<T>(tenantId); webstudioDbContext.WebstudioSettings.Remove(s);
} }
public virtual bool Save<T>(T data) where T : class, ISettings<T> webstudioDbContext.SaveChanges();
{
return SaveSettings(data, TenantID);
}
public bool SaveForCurrentUser<T>(T data) where T : class, ISettings<T> tr.Commit();
{ });
return SaveForUser(data, CurrentUserID); }
} else
{
var s = new DbWebstudioSettings
{
Id = settings.ID,
UserId = userId,
TenantId = tenantId,
Data = data
};
public bool SaveForUser<T>(T data, Guid userId) where T : class, ISettings<T> webstudioDbContext.AddOrUpdate(webstudioDbContext.WebstudioSettings, s);
{
return SaveSettingsFor(data, TenantID, userId);
}
public bool SaveForUser<T>(T data, UserInfo user) where T : class, ISettings<T> webstudioDbContext.SaveChanges();
{ }
return SaveSettingsFor(data, TenantID, user.Id);
}
public bool SaveForDefaultTenant<T>(T data) where T : class, ISettings<T> _dbSettingsManagerCache.Remove(key);
{
return SaveForTenant(data, Tenant.DefaultTenant);
}
public bool SaveForTenant<T>(T data, int tenantId) where T : class, ISettings<T> _cache.Insert(key, settings, _expirationTimeout);
{
return SaveSettings(data, tenantId);
}
public void ClearCache<T>() where T : class, ISettings<T> return true;
{ }
ClearCache<T>(TenantID); catch (Exception ex)
{
_logger.ErrorSaveSettingsFor(ex);
return false;
}
} }
private T Deserialize<T>(string data) private T Deserialize<T>(string data)

View File

@ -227,7 +227,8 @@ public class EFUserService : IUserService
if (sortBy == "type") if (sortBy == "type")
{ {
var q1 = from user in q var q1 = from user in q
join userGroup in userDbContext.UserGroups.Where(g => !g.Removed && (g.UserGroupId == Users.Constants.GroupAdmin.ID || g.UserGroupId == Users.Constants.GroupUser.ID)) join userGroup in userDbContext.UserGroups.Where(g => !g.Removed && (g.UserGroupId == Users.Constants.GroupAdmin.ID || g.UserGroupId == Users.Constants.GroupUser.ID
|| g.UserGroupId == Users.Constants.GroupCollaborator.ID))
on user.Id equals userGroup.Userid into joinedGroup on user.Id equals userGroup.Userid into joinedGroup
from @group in joinedGroup.DefaultIfEmpty() from @group in joinedGroup.DefaultIfEmpty()
select new { user, @group }; select new { user, @group };
@ -235,12 +236,18 @@ public class EFUserService : IUserService
if (sortOrderAsc) if (sortOrderAsc)
{ {
q = q1.OrderBy(r => r.user.ActivationStatus == EmployeeActivationStatus.Pending) q = q1.OrderBy(r => r.user.ActivationStatus == EmployeeActivationStatus.Pending)
.ThenBy(r => r.group != null && r.group.UserGroupId == Users.Constants.GroupAdmin.ID ? 1 : r.group == null ? 2 : 3).Select(r => r.user); .ThenBy(r => r.group == null ? 2 :
r.group.UserGroupId == Users.Constants.GroupAdmin.ID ? 1 :
r.group.UserGroupId == Users.Constants.GroupCollaborator.ID ? 3 : 4)
.Select(r => r.user);
} }
else else
{ {
q = q1.OrderBy(r => r.user.ActivationStatus == EmployeeActivationStatus.Pending) q = q1.OrderBy(r => r.user.ActivationStatus == EmployeeActivationStatus.Pending)
.ThenByDescending(u => u.group != null && u.group.UserGroupId == Users.Constants.GroupAdmin.ID ? 1 : u.group == null ? 2 : 3).Select(r => r.user); .ThenByDescending(u => u.group == null ? 2 :
u.group.UserGroupId == Users.Constants.GroupAdmin.ID ? 1 :
u.group.UserGroupId == Users.Constants.GroupCollaborator.ID ? 3 : 4)
.Select(r => r.user);
} }
} }
else else
@ -300,33 +307,29 @@ public class EFUserService : IUserService
using var userDbContext = _dbContextFactory.CreateDbContext(); using var userDbContext = _dbContextFactory.CreateDbContext();
using var tr = userDbContext.Database.BeginTransaction(); using var tr = userDbContext.Database.BeginTransaction();
userDbContext.Acl.RemoveRange(userDbContext.Acl.Where(r => r.Tenant == tenant && ids.Any(i => i == r.Subject))); userDbContext.Acl.Where(r => r.Tenant == tenant && ids.Any(i => i == r.Subject)).ExecuteDelete();
userDbContext.Subscriptions.RemoveRange(userDbContext.Subscriptions.Where(r => r.Tenant == tenant && stringIds.Any(i => i == r.Recipient))); userDbContext.Subscriptions.Where(r => r.Tenant == tenant && stringIds.Any(i => i == r.Recipient)).ExecuteDelete();
userDbContext.SubscriptionMethods.RemoveRange(userDbContext.SubscriptionMethods.Where(r => r.Tenant == tenant && stringIds.Any(i => i == r.Recipient))); userDbContext.SubscriptionMethods.Where(r => r.Tenant == tenant && stringIds.Any(i => i == r.Recipient)).ExecuteDelete();
var userGroups = userDbContext.UserGroups.Where(r => r.Tenant == tenant && ids.Any(i => i == r.UserGroupId)); var userGroups = userDbContext.UserGroups.Where(r => r.Tenant == tenant && ids.Any(i => i == r.UserGroupId));
var groups = userDbContext.Groups.Where(r => r.Tenant == tenant && ids.Any(i => i == r.Id)); var groups = userDbContext.Groups.Where(r => r.Tenant == tenant && ids.Any(i => i == r.Id));
if (immediate) if (immediate)
{ {
userDbContext.UserGroups.RemoveRange(userGroups); userGroups.ExecuteDelete();
userDbContext.Groups.RemoveRange(groups); groups.ExecuteDelete();
} }
else else
{ {
foreach (var ug in userGroups) userGroups.ExecuteUpdate(ug => ug
{ .SetProperty(p => p.Removed, true)
ug.Removed = true; .SetProperty(p => p.LastModified, DateTime.UtcNow));
ug.LastModified = DateTime.UtcNow;
} groups.ExecuteUpdate(g => g
foreach (var g in groups) .SetProperty(p => p.Removed, true)
{ .SetProperty(p => p.LastModified, DateTime.UtcNow));
g.Removed = true;
g.LastModified = DateTime.UtcNow;
}
} }
userDbContext.SaveChanges();
tr.Commit(); tr.Commit();
}); });
} }
@ -346,10 +349,10 @@ public class EFUserService : IUserService
using var userDbContext = _dbContextFactory.CreateDbContext(); using var userDbContext = _dbContextFactory.CreateDbContext();
using var tr = userDbContext.Database.BeginTransaction(); using var tr = userDbContext.Database.BeginTransaction();
userDbContext.Acl.RemoveRange(userDbContext.Acl.Where(r => r.Tenant == tenant && r.Subject == id)); userDbContext.Acl.Where(r => r.Tenant == tenant && r.Subject == id).ExecuteDelete();
userDbContext.Subscriptions.RemoveRange(userDbContext.Subscriptions.Where(r => r.Tenant == tenant && r.Recipient == id.ToString())); userDbContext.Subscriptions.Where(r => r.Tenant == tenant && r.Recipient == id.ToString()).ExecuteDelete();
userDbContext.SubscriptionMethods.RemoveRange(userDbContext.SubscriptionMethods.Where(r => r.Tenant == tenant && r.Recipient == id.ToString())); userDbContext.SubscriptionMethods.Where(r => r.Tenant == tenant && r.Recipient == id.ToString()).ExecuteDelete();
userDbContext.Photos.RemoveRange(userDbContext.Photos.Where(r => r.Tenant == tenant && r.UserId == id)); userDbContext.Photos.Where(r => r.Tenant == tenant && r.UserId == id).ExecuteDelete();
var userGroups = userDbContext.UserGroups.Where(r => r.Tenant == tenant && r.Userid == id); var userGroups = userDbContext.UserGroups.Where(r => r.Tenant == tenant && r.Userid == id);
var users = userDbContext.Users.Where(r => r.Tenant == tenant && r.Id == id); var users = userDbContext.Users.Where(r => r.Tenant == tenant && r.Id == id);
@ -357,29 +360,24 @@ public class EFUserService : IUserService
if (immediate) if (immediate)
{ {
userDbContext.UserGroups.RemoveRange(userGroups); userGroups.ExecuteDelete();
userDbContext.Users.RemoveRange(users); users.ExecuteDelete();
userDbContext.UserSecurity.RemoveRange(userSecurity); userSecurity.ExecuteDelete();
} }
else else
{ {
foreach (var ug in userGroups) userGroups.ExecuteUpdate(ug => ug
{ .SetProperty(p => p.Removed, true)
ug.Removed = true; .SetProperty(p => p.LastModified, DateTime.UtcNow));
ug.LastModified = DateTime.UtcNow;
}
foreach (var u in users) users.ExecuteUpdate(ug => ug
{ .SetProperty(p => p.Removed, true)
u.Removed = true; .SetProperty(p => p.LastModified, DateTime.UtcNow)
u.Status = EmployeeStatus.Terminated; .SetProperty(p => p.TerminatedDate, DateTime.UtcNow)
u.TerminatedDate = DateTime.UtcNow; .SetProperty(p => p.Status, EmployeeStatus.Terminated)
u.LastModified = DateTime.UtcNow; );
}
} }
userDbContext.SaveChanges();
tr.Commit(); tr.Commit();
}); });
} }
@ -402,16 +400,15 @@ public class EFUserService : IUserService
var userGroups = userDbContext.UserGroups.Where(r => r.Tenant == tenant && r.Userid == userId && r.UserGroupId == groupId && r.RefType == refType); var userGroups = userDbContext.UserGroups.Where(r => r.Tenant == tenant && r.Userid == userId && r.UserGroupId == groupId && r.RefType == refType);
if (immediate) if (immediate)
{ {
userDbContext.UserGroups.RemoveRange(userGroups); userGroups.ExecuteDelete();
} }
else else
{ {
foreach (var u in userGroups) userGroups.ExecuteUpdate(ug => ug
{ .SetProperty(p => p.Removed, true)
u.LastModified = DateTime.UtcNow; .SetProperty(p => p.LastModified, DateTime.UtcNow));
u.Removed = true;
}
} }
var user = userDbContext.Users.First(r => r.Tenant == tenant && r.Id == userId); var user = userDbContext.Users.First(r => r.Tenant == tenant && r.Id == userId);
user.LastModified = DateTime.UtcNow; user.LastModified = DateTime.UtcNow;
userDbContext.SaveChanges(); userDbContext.SaveChanges();
@ -749,4 +746,4 @@ public class DbUserSecurity
{ {
public User User { get; set; } public User User { get; set; }
public UserSecurity UserSecurity { get; set; } public UserSecurity UserSecurity { get; set; }
} }

View File

@ -39,7 +39,7 @@ public class HostedSolution
internal UserFormatter UserFormatter { get; set; } internal UserFormatter UserFormatter { get; set; }
internal TenantManager ClientTenantManager { get; set; } internal TenantManager ClientTenantManager { get; set; }
internal TenantUtil TenantUtil { get; set; } internal TenantUtil TenantUtil { get; set; }
internal DbSettingsManager SettingsManager { get; set; } internal SettingsManager SettingsManager { get; set; }
internal CoreSettings CoreSettings { get; set; } internal CoreSettings CoreSettings { get; set; }
public string Region { get; private set; } public string Region { get; private set; }
@ -51,7 +51,7 @@ public class HostedSolution
UserFormatter userFormatter, UserFormatter userFormatter,
TenantManager clientTenantManager, TenantManager clientTenantManager,
TenantUtil tenantUtil, TenantUtil tenantUtil,
DbSettingsManager settingsManager, SettingsManager settingsManager,
CoreSettings coreSettings) CoreSettings coreSettings)
{ {
TenantService = tenantService; TenantService = tenantService;
@ -196,9 +196,9 @@ public class HostedSolution
return null; return null;
} }
var tenantSettings = SettingsManager.LoadSettingsFor<TenantCookieSettings>(tenantId, Guid.Empty); var tenantSettings = SettingsManager.Load<TenantCookieSettings>(tenantId, Guid.Empty);
var expires = tenantSettings.IsDefault() ? DateTime.UtcNow.AddYears(1) : DateTime.UtcNow.AddMinutes(tenantSettings.LifeTime); var expires = tenantSettings.IsDefault() ? DateTime.UtcNow.AddYears(1) : DateTime.UtcNow.AddMinutes(tenantSettings.LifeTime);
var userSettings = SettingsManager.LoadSettingsFor<TenantCookieSettings>(tenantId, user.Id); var userSettings = SettingsManager.Load<TenantCookieSettings>(tenantId, user.Id);
return cookieStorage.EncryptCookie(tenantId, user.Id, tenantSettings.Index, expires, userSettings.Index, 0); return cookieStorage.EncryptCookie(tenantId, user.Id, tenantSettings.Index, expires, userSettings.Index, 0);
} }

View File

@ -28,8 +28,8 @@ namespace ASC.Core.Common.Log;
internal static partial class DbSettingsManagerLogger internal static partial class DbSettingsManagerLogger
{ {
[LoggerMessage(Level = LogLevel.Error, Message = "SaveSettingsFor")] [LoggerMessage(Level = LogLevel.Error, Message = "SaveSettingsFor")]
public static partial void ErrorSaveSettingsFor(this ILogger<DbSettingsManager> logger, Exception exception); public static partial void ErrorSaveSettingsFor(this ILogger<SettingsManager> logger, Exception exception);
[LoggerMessage(Level = LogLevel.Error, Message = "LoadSettingsFor")] [LoggerMessage(Level = LogLevel.Error, Message = "LoadSettingsFor")]
public static partial void ErrorLoadSettingsFor(this ILogger<DbSettingsManager> logger, Exception exception); public static partial void ErrorLoadSettingsFor(this ILogger<SettingsManager> logger, Exception exception);
} }

View File

@ -445,7 +445,8 @@ public enum MessageAction
DocumentsForcesave = 5049, DocumentsForcesave = 5049,
DocumentsStoreForcesave = 5048, DocumentsStoreForcesave = 5048,
DocumentsUploadingFormatsSettingsUpdated = 5033, DocumentsUploadingFormatsSettingsUpdated = 5033,
DocumentsExternalShareSettingsUpdated = 5069, // last DocumentsExternalShareSettingsUpdated = 5069,
DocumentsKeepNewFileNameSettingsUpdated = 5083, // last
FileConverted = 5035, FileConverted = 5035,

View File

@ -71,24 +71,18 @@ public class TelegramDao
{ {
using var dbContext = _dbContextFactory.CreateDbContext(); using var dbContext = _dbContextFactory.CreateDbContext();
var toRemove = dbContext.Users dbContext.Users
.Where(r => r.PortalUserId == userId) .Where(r => r.PortalUserId == userId)
.Where(r => r.TenantId == tenantId) .Where(r => r.TenantId == tenantId)
.ToList(); .ExecuteDelete();
dbContext.Users.RemoveRange(toRemove);
dbContext.SaveChanges();
} }
public void Delete(long telegramId) public void Delete(long telegramId)
{ {
using var dbContext = _dbContextFactory.CreateDbContext(); using var dbContext = _dbContextFactory.CreateDbContext();
var toRemove = dbContext.Users dbContext.Users
.Where(r => r.TelegramUserId == telegramId) .Where(r => r.TelegramUserId == telegramId)
.ToList(); .ExecuteDelete();
dbContext.Users.RemoveRange(toRemove);
dbContext.SaveChanges();
} }
} }

View File

@ -1,36 +1,36 @@
// (c) Copyright Ascensio System SIA 2010-2022 // (c) Copyright Ascensio System SIA 2010-2022
// //
// This program is a free software product. // This program is a free software product.
// You can redistribute it and/or modify it under the terms // 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 // 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 // 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 // to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights. // any third-party rights.
// //
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html // 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. // 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 // 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. // 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 // 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 // distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks. // trademark law for use of our trademarks.
// //
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing // 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 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Quota.Features; namespace ASC.Core.Common.Quota.Features;
public class CountRoomAdminFeature : TenantQuotaFeatureCount public class CountPaidUserFeature : TenantQuotaFeatureCount
{ {
public override bool Paid { get => true; } public override bool Paid { get => true; }
public override string Name { get => "manager"; } public override string Name { get => "manager"; }
public CountRoomAdminFeature(TenantQuota tenantQuota) : base(tenantQuota) public CountPaidUserFeature(TenantQuota tenantQuota) : base(tenantQuota)
{ {
} }
} }

View File

@ -24,6 +24,8 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using Constants = ASC.Common.Security.Authorizing.Constants;
namespace ASC.Core.Security.Authorizing; namespace ASC.Core.Security.Authorizing;
[Scope] [Scope]
@ -50,6 +52,11 @@ class RoleProvider : IRoleProvider
.ToList(); .ToList();
} }
} }
if (roles.Any(r => r.ID == Constants.Collaborator.ID || r.ID == Constants.User.ID))
{
roles = roles.Where(r => r.ID != Constants.RoomAdmin.ID).ToList();
}
return roles; return roles;
} }
@ -75,4 +82,4 @@ class RoleProvider : IRoleProvider
return roles; return roles;
} }
} }

View File

@ -31,23 +31,30 @@ namespace ASC.Core.Common.Security;
public static class Security public static class Security
{ {
public static readonly Dictionary<Guid, Dictionary<Guid, HashSet<Rule>>> Rules = new Dictionary<Guid, Dictionary<Guid, HashSet<Rule>>> public static readonly Dictionary<Guid, Dictionary<Guid, HashSet<Rule>>> Rules = new()
{ {
{ {
Constants.RoomAdmin.ID, new Dictionary<Guid, HashSet<Rule>>() Constants.RoomAdmin.ID, new Dictionary<Guid, HashSet<Rule>>
{ {
{ {
Constants.User.ID, new HashSet<Rule>() Constants.User.ID, new HashSet<Rule>
{ {
new Rule(UserConstants.Action_EditGroups.ID, Constants.User), new(UserConstants.Action_EditGroups.ID, Constants.User),
new Rule(UserConstants.Action_AddRemoveUser.ID), new(UserConstants.Action_AddRemoveUser.ID),
} }
}, },
{ {
Constants.RoomAdmin.ID, new HashSet<Rule>() Constants.RoomAdmin.ID, new HashSet<Rule>
{ {
new Rule(UserConstants.Action_EditGroups.ID, Constants.User), new(UserConstants.Action_EditGroups.ID, Constants.User),
new Rule(UserConstants.Action_AddRemoveUser.ID), new(UserConstants.Action_AddRemoveUser.ID),
}
},
{
Constants.Collaborator.ID, new HashSet<Rule>
{
new(UserConstants.Action_EditGroups.ID, Constants.Collaborator),
new(UserConstants.Action_AddRemoveUser.ID),
} }
} }
} }

View File

@ -24,29 +24,44 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using AuthConstants = ASC.Common.Security.Authorizing.Constants;
namespace ASC.Core.Common.Security; namespace ASC.Core.Common.Security;
public class UserGroupObject : SecurityObject public class UserGroupObject : SecurityObject
{ {
private ISubject User { get; set; } private readonly Guid _groupId;
private Guid GroupId { get; set; }
public UserGroupObject(ISubject user, Guid groupId) public UserGroupObject(ISubject user, Guid groupId)
{ {
SecurityId = user.ID; SecurityId = user.ID;
User = user; _groupId = groupId;
GroupId = groupId;
ObjectType = typeof(UserGroupObject); ObjectType = typeof(UserGroupObject);
FullId = $"{ObjectType.FullName}|{User.ID}|{GroupId}"; FullId = $"{ObjectType.FullName}|{user.ID}|{_groupId}";
} }
protected override IEnumerable<IRole> GetTargetRoles(IRoleProvider roleProvider) protected override IEnumerable<IRole> GetTargetRoles(IRoleProvider roleProvider)
{ {
return roleProvider.GetRoles(User); if (_groupId == Users.Constants.GroupAdmin.ID)
{
return new[] { AuthConstants.DocSpaceAdmin };
}
if (_groupId == Users.Constants.GroupUser.ID)
{
return new[] { AuthConstants.User };
}
if (_groupId == Users.Constants.GroupCollaborator.ID)
{
return new[] { AuthConstants.Collaborator };
}
return Array.Empty<IRole>();
} }
protected override IRuleData GetRuleData() protected override IRuleData GetRuleData()
{ {
return new Role(GroupId, "ruleData"); return new Role(_groupId, "ruleData");
} }
} }

View File

@ -67,6 +67,7 @@ public class UserSecurityProvider : SecurityObject
{ {
EmployeeType.DocSpaceAdmin => new[] { AuthConstants.DocSpaceAdmin }, EmployeeType.DocSpaceAdmin => new[] { AuthConstants.DocSpaceAdmin },
EmployeeType.RoomAdmin => new[] { AuthConstants.RoomAdmin }, EmployeeType.RoomAdmin => new[] { AuthConstants.RoomAdmin },
EmployeeType.Collaborator => new[] { AuthConstants.Collaborator },
EmployeeType.User => new[] { AuthConstants.User }, EmployeeType.User => new[] { AuthConstants.User },
_ => Array.Empty<IRole>(), _ => Array.Empty<IRole>(),
}; };

View File

@ -72,7 +72,7 @@ public class TenantCookieSettingsHelper
public TenantCookieSettings GetForTenant(int tenantId) public TenantCookieSettings GetForTenant(int tenantId)
{ {
return IsVisibleSettings return IsVisibleSettings
? _settingsManager.LoadForTenant<TenantCookieSettings>(tenantId) ? _settingsManager.Load<TenantCookieSettings>(tenantId)
: TenantCookieSettings.GetInstance(); : TenantCookieSettings.GetInstance();
} }
@ -83,20 +83,20 @@ public class TenantCookieSettingsHelper
return; return;
} }
_settingsManager.SaveForTenant(settings ?? TenantCookieSettings.GetInstance(), tenantId); _settingsManager.Save(settings ?? TenantCookieSettings.GetInstance(), tenantId);
} }
public TenantCookieSettings GetForUser(Guid userId) public TenantCookieSettings GetForUser(Guid userId)
{ {
return IsVisibleSettings return IsVisibleSettings
? _settingsManager.LoadForUser<TenantCookieSettings>(userId) ? _settingsManager.Load<TenantCookieSettings>(userId)
: TenantCookieSettings.GetInstance(); : TenantCookieSettings.GetInstance();
} }
public TenantCookieSettings GetForUser(int tenantId, Guid userId) public TenantCookieSettings GetForUser(int tenantId, Guid userId)
{ {
return IsVisibleSettings return IsVisibleSettings
? _settingsManager.LoadSettingsFor<TenantCookieSettings>(tenantId, userId) ? _settingsManager.Load<TenantCookieSettings>(tenantId, userId)
: TenantCookieSettings.GetInstance(); : TenantCookieSettings.GetInstance();
} }
@ -107,7 +107,7 @@ public class TenantCookieSettingsHelper
return; return;
} }
_settingsManager.SaveForUser(settings ?? TenantCookieSettings.GetInstance(), userId); _settingsManager.Save(settings ?? TenantCookieSettings.GetInstance(), userId);
} }
public DateTime GetExpiresTime(int tenantId) public DateTime GetExpiresTime(int tenantId)

View File

@ -90,11 +90,11 @@ public class TenantQuota : IMapFrom<DbQuota>
set => _countUserFeature.Value = value; set => _countUserFeature.Value = value;
} }
private readonly CountRoomAdminFeature _countRoomAdminFeature; private readonly CountPaidUserFeature _countPaidUserFeature;
public int CountRoomAdmin public int CountRoomAdmin
{ {
get => _countRoomAdminFeature.Value; get => _countPaidUserFeature.Value;
set => _countRoomAdminFeature.Value = value; set => _countPaidUserFeature.Value = value;
} }
private readonly UsersInRoomFeature _usersInRoomFeature; private readonly UsersInRoomFeature _usersInRoomFeature;
@ -221,7 +221,7 @@ public class TenantQuota : IMapFrom<DbQuota>
_featuresList = new List<string>(); _featuresList = new List<string>();
_countUserFeature = new CountUserFeature(this) { Order = 1 }; _countUserFeature = new CountUserFeature(this) { Order = 1 };
_countRoomAdminFeature = new CountRoomAdminFeature(this); _countPaidUserFeature = new CountPaidUserFeature(this);
_usersInRoomFeature = new UsersInRoomFeature(this) { Order = 8 }; _usersInRoomFeature = new UsersInRoomFeature(this) { Order = 8 };
_countRoomFeature = new CountRoomFeature(this) { Order = 2 }; _countRoomFeature = new CountRoomFeature(this) { Order = 2 };
_maxTotalSizeFeature = new MaxTotalSizeFeature(this); _maxTotalSizeFeature = new MaxTotalSizeFeature(this);
@ -245,7 +245,7 @@ public class TenantQuota : IMapFrom<DbQuota>
TenantQuotaFeatures = new List<TenantQuotaFeature> TenantQuotaFeatures = new List<TenantQuotaFeature>
{ {
_countUserFeature, _countUserFeature,
_countRoomAdminFeature, _countPaidUserFeature,
_usersInRoomFeature, _usersInRoomFeature,
_countRoomFeature, _countRoomFeature,
_maxTotalSizeFeature, _maxTotalSizeFeature,

View File

@ -24,9 +24,9 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using Action = ASC.Common.Security.Authorizing.Action; using Action = ASC.Common.Security.Authorizing.Action;
using AuthConst = ASC.Common.Security.Authorizing.Constants; using AuthConst = ASC.Common.Security.Authorizing.Constants;
namespace ASC.Core.Users; namespace ASC.Core.Users;
[Singletone] [Singletone]
@ -59,7 +59,7 @@ public sealed class Constants
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
#region system group and category groups #region system group and category groups
public static readonly Guid SysGroupCategoryId = new Guid("{7717039D-FBE9-45ad-81C1-68A1AA10CE1F}"); public static readonly Guid SysGroupCategoryId = new Guid("{7717039D-FBE9-45ad-81C1-68A1AA10CE1F}");
@ -87,12 +87,19 @@ public sealed class Constants
Name = AuthConst.DocSpaceAdmin.Name, Name = AuthConst.DocSpaceAdmin.Name,
}; };
public static readonly GroupInfo GroupCollaborator = new(SysGroupCategoryId)
{
ID = AuthConst.Collaborator.ID,
Name = AuthConst.Collaborator.Name,
};
public static readonly GroupInfo[] BuildinGroups = new[] public static readonly GroupInfo[] BuildinGroups = new[]
{ {
GroupEveryone, GroupEveryone,
GroupUser, GroupUser,
GroupManager, GroupManager,
GroupAdmin, GroupAdmin,
GroupCollaborator,
}; };
public static readonly UserInfo LostUser = new UserInfo public static readonly UserInfo LostUser = new UserInfo
@ -119,10 +126,10 @@ public sealed class Constants
Name = "Unknown" Name = "Unknown"
}; };
#endregion #endregion
#region authorization rules module to work with users #region authorization rules module to work with users
public static readonly Action Action_EditUser = new Action( public static readonly Action Action_EditUser = new Action(
new Guid("{EF5E6790-F346-4b6e-B662-722BC28CB0DB}"), new Guid("{EF5E6790-F346-4b6e-B662-722BC28CB0DB}"),
@ -136,5 +143,5 @@ public sealed class Constants
new Guid("{1D4FEEAC-0BF3-4aa9-B096-6D6B104B79B5}"), new Guid("{1D4FEEAC-0BF3-4aa9-B096-6D6B104B79B5}"),
"Edit categories and groups"); "Edit categories and groups");
#endregion #endregion
} }

View File

@ -40,7 +40,12 @@ public static class UserExtensions
public static bool IsMe(this UserInfo ui, AuthContext authContext) public static bool IsMe(this UserInfo ui, AuthContext authContext)
{ {
return ui != null && ui.Id == authContext.CurrentAccount.ID; return IsMe(ui, authContext.CurrentAccount.ID);
}
public static bool IsMe(this UserInfo user, Guid id)
{
return user != null && user.Id == id;
} }
public static bool IsDocSpaceAdmin(this UserManager userManager, Guid id) public static bool IsDocSpaceAdmin(this UserManager userManager, Guid id)
@ -65,6 +70,17 @@ public static class UserExtensions
return ui != null && userManager.IsUserInGroup(ui.Id, Constants.GroupUser.ID); return ui != null && userManager.IsUserInGroup(ui.Id, Constants.GroupUser.ID);
} }
public static bool IsCollaborator(this UserManager userManager, UserInfo userInfo)
{
return userInfo != null && userManager.IsUserInGroup(userInfo.Id, Constants.GroupCollaborator.ID);
}
public static bool IsCollaborator(this UserManager userManager, Guid id)
{
var userInfo = userManager.GetUsers(id);
return userManager.IsCollaborator(userInfo);
}
public static bool IsOutsider(this UserManager userManager, Guid id) public static bool IsOutsider(this UserManager userManager, Guid id)
{ {
return userManager.IsUser(id) && id == Constants.OutsideUser.Id; return userManager.IsUser(id) && id == Constants.OutsideUser.Id;
@ -98,7 +114,15 @@ public static class UserExtensions
public static EmployeeType GetUserType(this UserManager userManager, Guid id) public static EmployeeType GetUserType(this UserManager userManager, Guid id)
{ {
return userManager.IsDocSpaceAdmin(id) ? EmployeeType.DocSpaceAdmin : userManager.IsUser(id) ? EmployeeType.User : EmployeeType.RoomAdmin; if (userManager.GetUsers(id).Equals(Constants.LostUser))
{
return EmployeeType.User;
}
return userManager.IsDocSpaceAdmin(id) ? EmployeeType.DocSpaceAdmin :
userManager.IsUser(id) ? EmployeeType.User :
userManager.IsCollaborator(id) ? EmployeeType.Collaborator :
EmployeeType.RoomAdmin;
} }
private const string _extMobPhone = "extmobphone"; private const string _extMobPhone = "extmobphone";
@ -146,4 +170,4 @@ public static class UserExtensions
ui.ContactsList = newContacts; ui.ContactsList = newContacts;
} }
} }

View File

@ -115,10 +115,7 @@ public class BackupRepository : IBackupRepository
public void DeleteBackupSchedule(int tenantId) public void DeleteBackupSchedule(int tenantId)
{ {
using var backupContext = _dbContextFactory.CreateDbContext(); using var backupContext = _dbContextFactory.CreateDbContext();
var shedule = backupContext.Schedules.AsQueryable().Where(s => s.TenantId == tenantId).ToList(); backupContext.Schedules.Where(s => s.TenantId == tenantId).ExecuteDelete();
backupContext.Schedules.RemoveRange(shedule);
backupContext.SaveChanges();
} }
public List<BackupSchedule> GetBackupSchedules() public List<BackupSchedule> GetBackupSchedules()

View File

@ -40,7 +40,7 @@ public class Helpers
ConfigurationConstants.CoreSystem.ID, ConfigurationConstants.CoreSystem.ID,
ConfigurationConstants.Guest.ID, ConfigurationConstants.Guest.ID,
UserConstants.LostUser.Id UserConstants.LostUser.Id
}; };
private readonly Guid[] _systemGroups = new[] private readonly Guid[] _systemGroups = new[]
{ {
@ -50,6 +50,7 @@ public class Helpers
UserConstants.GroupEveryone.ID, UserConstants.GroupEveryone.ID,
UserConstants.GroupUser.ID, UserConstants.GroupUser.ID,
UserConstants.GroupManager.ID, UserConstants.GroupManager.ID,
UserConstants.GroupCollaborator.ID,
new Guid("{EA942538-E68E-4907-9394-035336EE0BA8}"), //community product new Guid("{EA942538-E68E-4907-9394-035336EE0BA8}"), //community product
new Guid("{1e044602-43b5-4d79-82f3-fd6208a11960}"), //projects product new Guid("{1e044602-43b5-4d79-82f3-fd6208a11960}"), //projects product
new Guid("{6743007C-6F95-4d20-8C88-A8601CE5E76D}"), //crm product new Guid("{6743007C-6F95-4d20-8C88-A8601CE5E76D}"), //crm product
@ -59,7 +60,7 @@ public class Helpers
new Guid("{32D24CB5-7ECE-4606-9C94-19216BA42086}"), //calendar product new Guid("{32D24CB5-7ECE-4606-9C94-19216BA42086}"), //calendar product
new Guid("{37620AE5-C40B-45ce-855A-39DD7D76A1FA}"), //birthdays product new Guid("{37620AE5-C40B-45ce-855A-39DD7D76A1FA}"), //birthdays product
new Guid("{BF88953E-3C43-4850-A3FB-B1E43AD53A3E}") //talk product new Guid("{BF88953E-3C43-4850-A3FB-B1E43AD53A3E}") //talk product
}; };
public Helpers(InstanceCrypto instanceCrypto) public Helpers(InstanceCrypto instanceCrypto)
{ {

View File

@ -117,14 +117,17 @@ public class CommonChunkedUploadSession : ICloneable
switch (value.ValueKind) switch (value.ValueKind)
{ {
case JsonValueKind.String: case JsonValueKind.String:
newItems.Add(item.Key, item.Value.ToString()); newItems.Add(item.Key, item.Value.ToString());
break; break;
case JsonValueKind.Number: case JsonValueKind.Number:
newItems.Add(item.Key, Int32.Parse(item.Value.ToString())); newItems.Add(item.Key, Int32.Parse(item.Value.ToString()));
break; break;
case JsonValueKind.Array: case JsonValueKind.Array:
newItems.Add(item.Key, value.EnumerateArray().Select(o => o.ToString()).ToList()); newItems.Add(item.Key, value.EnumerateArray().Select(o => o.ToString()).ToList());
break; break;
case JsonValueKind.Object:
newItems.Add(item.Key, JsonSerializer.Deserialize<Dictionary<int, string>>(item.Value.ToString()));
break;
default: default:
newItems.Add(item.Key, item.Value); newItems.Add(item.Key, item.Value);
break; break;

View File

@ -63,13 +63,13 @@ public class BaseStorageSettingsListener
var scopeClass = scope.ServiceProvider.GetService<BaseStorageSettingsListenerScope>(); var scopeClass = scope.ServiceProvider.GetService<BaseStorageSettingsListenerScope>();
var (storageSettingsHelper, settingsManager) = scopeClass; var (storageSettingsHelper, settingsManager) = scopeClass;
var settings = settingsManager.LoadForTenant<StorageSettings>(i.TenantId); var settings = settingsManager.Load<StorageSettings>(i.TenantId);
if (i.Name == settings.Module) if (i.Name == settings.Module)
{ {
storageSettingsHelper.Clear(settings); storageSettingsHelper.Clear(settings);
} }
var cdnSettings = settingsManager.LoadForTenant<CdnStorageSettings>(i.TenantId); var cdnSettings = settingsManager.Load<CdnStorageSettings>(i.TenantId);
if (i.Name == cdnSettings.Module) if (i.Name == cdnSettings.Module)
{ {
storageSettingsHelper.Clear(cdnSettings); storageSettingsHelper.Clear(cdnSettings);

View File

@ -155,7 +155,7 @@ public class StorageFactory
throw new InvalidOperationException("config section not found"); throw new InvalidOperationException("config section not found");
} }
var settings = _settingsManager.LoadForTenant<StorageSettings>(tenant.Value); var settings = _settingsManager.Load<StorageSettings>(tenant.Value);
//TODO:GetStoreAndCache //TODO:GetStoreAndCache
return GetDataStore(tenantPath, module, _storageSettingsHelper.DataStoreConsumer(settings), controller, region); return GetDataStore(tenantPath, module, _storageSettingsHelper.DataStoreConsumer(settings), controller, region);
} }

View File

@ -89,8 +89,8 @@ public class S3ZipWriteOperator : IDataWriteOperator
{ {
var fs = _fileStream; var fs = _fileStream;
_fileStream = null; _fileStream = null;
Upload(fs);
Computehash(fs, false); Computehash(fs, false);
Upload(fs);
} }
} }
@ -99,7 +99,7 @@ public class S3ZipWriteOperator : IDataWriteOperator
stream.Position = 0; stream.Position = 0;
var buffer = new byte[_sessionHolder.MaxChunkUploadSize]; var buffer = new byte[_sessionHolder.MaxChunkUploadSize];
int bytesRead; int bytesRead;
while ((bytesRead = _fileStream.Read(buffer, 0, (int)_sessionHolder.MaxChunkUploadSize)) > 0) while ((bytesRead = stream.Read(buffer, 0, (int)_sessionHolder.MaxChunkUploadSize)) > 0)
{ {
_sha.TransformBlock(buffer, 0, bytesRead, buffer, 0); _sha.TransformBlock(buffer, 0, bytesRead, buffer, 0);
} }
@ -134,12 +134,13 @@ public class S3ZipWriteOperator : IDataWriteOperator
_tarOutputStream.Close(); _tarOutputStream.Close();
_tarOutputStream.Dispose(); _tarOutputStream.Dispose();
Computehash(_fileStream, true);
Upload(_fileStream); Upload(_fileStream);
Task.WaitAll(_tasks.ToArray()); Task.WaitAll(_tasks.ToArray());
StoragePath = await _sessionHolder.FinalizeAsync(_chunkedUploadSession); StoragePath = await _sessionHolder.FinalizeAsync(_chunkedUploadSession);
Computehash(_fileStream, true);
Hash = BitConverter.ToString(_sha.Hash).Replace("-", string.Empty); Hash = BitConverter.ToString(_sha.Hash).Replace("-", string.Empty);
_sha.Dispose(); _sha.Dispose();

View File

@ -154,11 +154,8 @@ public class FeedAggregateDataProvider
using var feedDbContext = _dbContextFactory.CreateDbContext(); using var feedDbContext = _dbContextFactory.CreateDbContext();
using var tx = feedDbContext.Database.BeginTransaction(IsolationLevel.ReadUncommitted); using var tx = feedDbContext.Database.BeginTransaction(IsolationLevel.ReadUncommitted);
var aggregates = feedDbContext.FeedAggregates.Where(r => r.AggregateDate <= fromTime); feedDbContext.FeedAggregates.Where(r => r.AggregateDate <= fromTime).ExecuteDelete();
feedDbContext.FeedAggregates.RemoveRange(aggregates); feedDbContext.FeedUsers.Where(r => feedDbContext.FeedAggregates.Where(r => r.AggregateDate <= fromTime).Any(a => a.Id == r.FeedId)).ExecuteDelete();
var users = feedDbContext.FeedUsers.Where(r => feedDbContext.FeedAggregates.Where(r => r.AggregateDate <= fromTime).Any(a => a.Id == r.FeedId));
feedDbContext.FeedUsers.RemoveRange(users);
tx.Commit(); tx.Commit();
}); });
@ -310,13 +307,8 @@ public class FeedAggregateDataProvider
using var feedDbContext = _dbContextFactory.CreateDbContext(); using var feedDbContext = _dbContextFactory.CreateDbContext();
using var tx = feedDbContext.Database.BeginTransaction(IsolationLevel.ReadUncommitted); using var tx = feedDbContext.Database.BeginTransaction(IsolationLevel.ReadUncommitted);
var aggregates = feedDbContext.FeedAggregates.Where(r => r.Id == id); feedDbContext.FeedAggregates.Where(r => r.Id == id).ExecuteDelete();
feedDbContext.FeedAggregates.RemoveRange(aggregates); feedDbContext.FeedUsers.Where(r => r.FeedId == id).ExecuteDelete();
var users = feedDbContext.FeedUsers.Where(r => r.FeedId == id);
feedDbContext.FeedUsers.RemoveRange(users);
feedDbContext.SaveChanges();
tx.Commit(); tx.Commit();
}); });

View File

@ -57,16 +57,14 @@ public class IPRestrictionsRepository
using var tenantDbContext = _dbContextManager.CreateDbContext(); using var tenantDbContext = _dbContextManager.CreateDbContext();
using var tx = tenantDbContext.Database.BeginTransaction(); using var tx = tenantDbContext.Database.BeginTransaction();
var restrictions = tenantDbContext.TenantIpRestrictions.Where(r => r.Tenant == tenant).ToList(); tenantDbContext.TenantIpRestrictions.Where(r => r.Tenant == tenant).ExecuteDelete();
tenantDbContext.TenantIpRestrictions.RemoveRange(restrictions);
tenantDbContext.SaveChanges();
var ipsList = ips.Select(r => new TenantIpRestrictions var ipsList = ips.Select(r => new TenantIpRestrictions
{ {
Tenant = tenant, Tenant = tenant,
Ip = r.Ip, Ip = r.Ip,
ForAdmin = r.ForAdmin ForAdmin = r.ForAdmin
}); });
tenantDbContext.TenantIpRestrictions.AddRange(ipsList); tenantDbContext.TenantIpRestrictions.AddRange(ipsList);

View File

@ -69,7 +69,13 @@ public class DbWorker
return objForCreate; return objForCreate;
} }
var toAdd = new WebhooksConfig { TenantId = Tenant, Uri = uri, SecretKey = secretKey }; var toAdd = new WebhooksConfig
{
TenantId = Tenant,
Uri = uri,
SecretKey = secretKey
};
toAdd = await webhooksDbContext.AddOrUpdateAsync(r => r.WebhooksConfigs, toAdd); toAdd = await webhooksDbContext.AddOrUpdateAsync(r => r.WebhooksConfigs, toAdd);
await webhooksDbContext.SaveChangesAsync(); await webhooksDbContext.SaveChangesAsync();

View File

@ -123,9 +123,9 @@ public class Startup
app.UseAuthorization(); app.UseAuthorization();
app.UseEndpoints(endpoints => app.UseEndpoints(async endpoints =>
{ {
endpoints.MapCustom(); await endpoints.MapCustomAsync();
endpoints.MapHealthChecks("/health", new HealthCheckOptions() endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{ {

View File

@ -144,19 +144,20 @@ public class Startup
app.UseAuthorization(); app.UseAuthorization();
app.UseEndpoints(endpoints => app.UseEndpoints( async endpoints =>
{ {
endpoints.MapCustom(); await endpoints.MapCustomAsync();
endpoints.MapHealthChecks("/health", new HealthCheckOptions() endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{ {
Predicate = _ => true, Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
}); });
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
{ {
Predicate = r => r.Name.Contains("self") Predicate = r => r.Name.Contains("self")
}); });
}); });
} }
} }

View File

@ -57,10 +57,7 @@ try
startup.ConfigureServices(builder.Services); startup.ConfigureServices(builder.Services);
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => builder.Host.ConfigureContainer<ContainerBuilder>(startup.ConfigureContainer);
{
startup.ConfigureContainer(containerBuilder);
});
var app = builder.Build(); var app = builder.Build();

View File

@ -57,10 +57,7 @@ try
startup.ConfigureServices(builder.Services); startup.ConfigureServices(builder.Services);
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => builder.Host.ConfigureContainer<ContainerBuilder>(startup.ConfigureContainer);
{
startup.ConfigureContainer(containerBuilder);
});
var app = builder.Build(); var app = builder.Build();

View File

@ -158,7 +158,7 @@ public class SearchSettingsHelper
return true; return true;
} }
var settings = _settingsManager.LoadForTenant<SearchSettings>(tenantId); var settings = _settingsManager.Load<SearchSettings>(tenantId);
return settings.IsEnabled(((ISearchItemDocument)_serviceProvider.GetService(t)).IndexName); return settings.IsEnabled(((ISearchItemDocument)_serviceProvider.GetService(t)).IndexName);
} }

View File

@ -85,15 +85,11 @@ public class NotifyCleanerService : BackgroundService
{ {
using var tx = dbContext.Database.BeginTransaction(); using var tx = dbContext.Database.BeginTransaction();
var info = dbContext.NotifyInfo.Where(r => r.ModifyDate < date && r.State == 4).ToList(); var infoCount = dbContext.NotifyInfo.Where(r => r.ModifyDate < date && r.State == 4).ExecuteDelete();
var queue = dbContext.NotifyQueue.Where(r => r.CreationDate < date).ToList(); var queueCount = dbContext.NotifyQueue.Where(r => r.CreationDate < date).ExecuteDelete();
dbContext.NotifyInfo.RemoveRange(info);
dbContext.NotifyQueue.RemoveRange(queue);
dbContext.SaveChanges();
tx.Commit(); tx.Commit();
_logger.InformationClearNotifyMessages(info.Count, queue.Count); _logger.InformationClearNotifyMessages(infoCount, queueCount);
}); });
} }
catch (ThreadAbortException) catch (ThreadAbortException)

View File

@ -69,6 +69,7 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
<script src="/static/scripts/browserDetector.js"></script>
<script> <script>
console.log("It's WEB CLIENT INIT"); console.log("It's WEB CLIENT INIT");
fetch("/static/scripts/config.json") fetch("/static/scripts/config.json")

View File

@ -1,4 +1,5 @@
{ {
"ResetApplicationDescription": "Authenticator application configuration will be reset.", "ResetApplicationDescription": "Authenticator application configuration will be reset.",
"ResetApplicationTitle": "Reset application configuration" "ResetApplicationTitle": "Reset application configuration",
"SuccessResetApplication": "Application settings for authentication have been successfully reset"
} }

View File

@ -44,6 +44,7 @@
"Remove": "Remove", "Remove": "Remove",
"RoleCommentator": "Commentator", "RoleCommentator": "Commentator",
"RoleCommentatorDescription": "Operations with existing files: viewing, commenting.", "RoleCommentatorDescription": "Operations with existing files: viewing, commenting.",
"RoleCollaboratorDescription": "Collaborators can create and edit files in the room, but can't create rooms, manage users, or access settings.",
"RoleDocSpaceAdminDescription": "DocSpace admins can access DocSpace settings, manage and archive rooms, invite new users and assign roles below their level. All admins have access to the Personal section.", "RoleDocSpaceAdminDescription": "DocSpace admins can access DocSpace settings, manage and archive rooms, invite new users and assign roles below their level. All admins have access to the Personal section.",
"RoleEditor": "Editor", "RoleEditor": "Editor",
"RoleEditorDescription": "Operations with existing files: viewing, editing, form filling, reviewing, commenting.", "RoleEditorDescription": "Operations with existing files: viewing, editing, form filling, reviewing, commenting.",

View File

@ -13,6 +13,7 @@
"BackToParentFolderButton": "Вернуться в папку на уровень выше", "BackToParentFolderButton": "Вернуться в папку на уровень выше",
"ByAuthor": "Автор", "ByAuthor": "Автор",
"ByCreation": "Создан", "ByCreation": "Создан",
"ByErasure": "Стирание",
"ByLastModified": "Изменен", "ByLastModified": "Изменен",
"ByOwner": "владелец", "ByOwner": "владелец",
"CollaborationRooms": "Совместное редактирование", "CollaborationRooms": "Совместное редактирование",
@ -22,6 +23,7 @@
"CopyItems": "Скопировано элементов: <strong>{{qty}}</strong>", "CopyItems": "Скопировано элементов: <strong>{{qty}}</strong>",
"CreateRoom": "Создание комнаты", "CreateRoom": "Создание комнаты",
"CustomRooms": "Пользовательская", "CustomRooms": "Пользовательская",
"DaysRemaining": "Дней осталось: {{daysRemaining}}",
"Document": "Документ", "Document": "Документ",
"EditRoom": "Изменить комнату", "EditRoom": "Изменить комнату",
"EmptyFile": "Пустой файл", "EmptyFile": "Пустой файл",

View File

@ -1,4 +1,5 @@
{ {
"ResetApplicationDescription": "Настройки приложения для аутентификации будут сброшены.", "ResetApplicationDescription": "Настройки приложения для аутентификации будут сброшены.",
"ResetApplicationTitle": "Сбросить настройки приложения" "ResetApplicationTitle": "Сбросить настройки приложения",
"SuccessResetApplication": "Настройки приложения для аутентификации успешно сброшены"
} }

View File

@ -44,6 +44,7 @@
"Remove": "Удалить", "Remove": "Удалить",
"RoleCommentator": "Комментатор", "RoleCommentator": "Комментатор",
"RoleCommentatorDescription": "Операции с существующими файлами: просмотр, комментирование.", "RoleCommentatorDescription": "Операции с существующими файлами: просмотр, комментирование.",
"RoleCollaboratorDescription": "Сотрудники могут создавать и редактировать файлы в комнате, но не могут создавать комнаты, управлять пользователями или получать доступ к настройкам.",
"RoleDocSpaceAdminDescription": "Администраторы DocSpace могут получить доступ к настройкам DocSpace, управлять и архивировать комнаты, приглашать новых пользователей и назначать роли ниже своего уровня. Все администраторы имеют доступ к Личному разделу.", "RoleDocSpaceAdminDescription": "Администраторы DocSpace могут получить доступ к настройкам DocSpace, управлять и архивировать комнаты, приглашать новых пользователей и назначать роли ниже своего уровня. Все администраторы имеют доступ к Личному разделу.",
"RoleEditor": "Редактор", "RoleEditor": "Редактор",
"RoleEditorDescription": "Операции с существующими файлами: просмотр, редактирование, заполнение форм, рецензирование, комментирование.", "RoleEditorDescription": "Операции с существующими файлами: просмотр, редактирование, заполнение форм, рецензирование, комментирование.",

View File

@ -55,7 +55,7 @@ const withLoader = (WrappedComponent) => (Loader) => {
return (!isEditor && firstLoad && !isGallery) || return (!isEditor && firstLoad && !isGallery) ||
!isLoaded || !isLoaded ||
(isMobile && inLoad) || (isMobile && inLoad && !firstLoad) ||
(isLoadingFilesFind && !Loader) || (isLoadingFilesFind && !Loader) ||
!tReady || !tReady ||
!isInit ? ( !isInit ? (

View File

@ -517,7 +517,6 @@ const ShellWrapper = inject(({ auth, backup }) => {
setSnackbarExist, setSnackbarExist,
socketHelper, socketHelper,
setTheme, setTheme,
getWhiteLabelLogoUrls,
whiteLabelLogoUrls, whiteLabelLogoUrls,
} = settingsStore; } = settingsStore;
const isBase = settingsStore.theme.isBase; const isBase = settingsStore.theme.isBase;

View File

@ -2,7 +2,8 @@ import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import App from "./App"; import App from "./App";
//import { registerSW } from "@docspace/common/sw/helper"; //import { registerSW } from "@docspace/common/sw/helper";
const root = document.getElementById("root");
ReactDOM.render(<App />, document.getElementById("root")); if (root) ReactDOM.render(<App />, root);
//registerSW(); //registerSW();

View File

@ -154,6 +154,7 @@ const Items = ({
startUpload, startUpload,
uploadEmptyFolders, uploadEmptyFolders,
isVisitor, isVisitor,
isCollaborator,
isAdmin, isAdmin,
myId, myId,
commonId, commonId,
@ -169,6 +170,8 @@ const Items = ({
onHide, onHide,
firstLoad, firstLoad,
deleteAction,
startDrag,
}) => { }) => {
useEffect(() => { useEffect(() => {
data.forEach((elem) => { data.forEach((elem) => {
@ -263,7 +266,12 @@ const Items = ({
return false; return false;
} }
if (!draggableItems || draggableItems.find((x) => x.id === item.id)) if (
!draggableItems ||
draggableItems.find(
(x) => x.id === item.id && x.isFolder === item.isFolder
)
)
return false; return false;
if ( if (
@ -278,7 +286,8 @@ const Items = ({
(item.pathParts && (item.pathParts &&
(item.pathParts[0] === myId || item.pathParts[0] === commonId)) || (item.pathParts[0] === myId || item.pathParts[0] === commonId)) ||
item.rootFolderType === FolderType.USER || item.rootFolderType === FolderType.USER ||
item.rootFolderType === FolderType.COMMON item.rootFolderType === FolderType.COMMON ||
(item.rootFolderType === FolderType.TRASH && startDrag)
) { ) {
return true; return true;
} }
@ -306,6 +315,18 @@ const Items = ({
[moveDragItems, t] [moveDragItems, t]
); );
const onRemove = React.useCallback(() => {
const translations = {
deleteOperation: t("Translations:DeleteOperation"),
deleteFromTrash: t("Translations:DeleteFromTrash"),
deleteSelectedElem: t("Translations:DeleteSelectedElem"),
FileRemoved: t("Files:FileRemoved"),
FolderRemoved: t("Files:FolderRemoved"),
};
deleteAction(translations);
}, [deleteAction]);
const onEmptyTrashAction = () => { const onEmptyTrashAction = () => {
isMobile && onHide(); isMobile && onHide();
setEmptyTrashDialogVisible(true); setEmptyTrashDialogVisible(true);
@ -335,7 +356,7 @@ const Items = ({
getEndOfBlock={getEndOfBlock} getEndOfBlock={getEndOfBlock}
showText={showText} showText={showText}
onClick={onClick} onClick={onClick}
onMoveTo={onMoveTo} onMoveTo={isTrash ? onRemove : onMoveTo}
onBadgeClick={isTrash ? onEmptyTrashAction : onBadgeClick} onBadgeClick={isTrash ? onEmptyTrashAction : onBadgeClick}
showDragItems={showDragItems} showDragItems={showDragItems}
showBadge={showBadge} showBadge={showBadge}
@ -347,7 +368,8 @@ const Items = ({
}); });
if (!firstLoad) items.splice(3, 0, <SettingsItem key="settings-item" />); if (!firstLoad) items.splice(3, 0, <SettingsItem key="settings-item" />);
if (!isVisitor) items.splice(3, 0, <AccountsItem key="accounts-item" />); if (!isVisitor && !isCollaborator)
items.splice(3, 0, <AccountsItem key="accounts-item" />);
if (!isVisitor) items.splice(3, 0, <CatalogDivider key="other-header" />); if (!isVisitor) items.splice(3, 0, <CatalogDivider key="other-header" />);
else items.splice(2, 0, <CatalogDivider key="other-header" />); else items.splice(2, 0, <CatalogDivider key="other-header" />);
@ -401,9 +423,9 @@ export default inject(
selection, selection,
dragging, dragging,
setDragging, setDragging,
setStartDrag,
trashIsEmpty, trashIsEmpty,
firstLoad, firstLoad,
startDrag,
} = filesStore; } = filesStore;
const { startUpload } = uploadDataStore; const { startUpload } = uploadDataStore;
@ -417,12 +439,17 @@ export default inject(
} = treeFoldersStore; } = treeFoldersStore;
const { id, pathParts, rootFolderType } = selectedFolderStore; const { id, pathParts, rootFolderType } = selectedFolderStore;
const { moveDragItems, uploadEmptyFolders } = filesActionsStore; const {
moveDragItems,
uploadEmptyFolders,
deleteAction,
} = filesActionsStore;
const { setEmptyTrashDialogVisible } = dialogsStore; const { setEmptyTrashDialogVisible } = dialogsStore;
return { return {
isAdmin: auth.isAdmin, isAdmin: auth.isAdmin,
isVisitor: auth.userStore.user.isVisitor, isVisitor: auth.userStore.user.isVisitor,
isCollaborator: auth.userStore.user.isCollaborator,
myId: myFolderId, myId: myFolderId,
commonId: commonFolderId, commonId: commonFolderId,
isPrivacy: isPrivacyFolder, isPrivacy: isPrivacyFolder,
@ -435,14 +462,15 @@ export default inject(
draggableItems: dragging ? selection : null, draggableItems: dragging ? selection : null,
dragging, dragging,
setDragging, setDragging,
setStartDrag,
moveDragItems, moveDragItems,
deleteAction,
startUpload, startUpload,
uploadEmptyFolders, uploadEmptyFolders,
setEmptyTrashDialogVisible, setEmptyTrashDialogVisible,
trashIsEmpty, trashIsEmpty,
rootFolderType, rootFolderType,
firstLoad, firstLoad,
startDrag,
}; };
} }
)(withTranslation(["Files", "Common", "Translations"])(observer(Items))); )(withTranslation(["Files", "Common", "Translations"])(observer(Items)));

View File

@ -50,10 +50,17 @@ const ArticleBodyContent = (props) => {
const [disableBadgeClick, setDisableBadgeClick] = React.useState(false); const [disableBadgeClick, setDisableBadgeClick] = React.useState(false);
let loadTimeout = null;
const campaigns = (localStorage.getItem("campaigns") || "") const campaigns = (localStorage.getItem("campaigns") || "")
.split(",") .split(",")
.filter((campaign) => campaign.length > 0); .filter((campaign) => campaign.length > 0);
const cleanTimer = () => {
loadTimeout && clearTimeout(loadTimeout);
loadTimeout = null;
};
const onClick = React.useCallback( const onClick = React.useCallback(
(folderId) => { (folderId) => {
const { const {
@ -72,7 +79,9 @@ const ArticleBodyContent = (props) => {
const filesSection = window.location.pathname.indexOf("/filter") > 0; const filesSection = window.location.pathname.indexOf("/filter") > 0;
if (filesSection) { if (filesSection) {
setIsLoading(true); loadTimeout = setTimeout(() => {
setIsLoading(true);
}, 200);
} else { } else {
showLoader(); showLoader();
} }
@ -86,54 +95,16 @@ const ArticleBodyContent = (props) => {
? RoomSearchArea.Archive ? RoomSearchArea.Archive
: RoomSearchArea.Active; : RoomSearchArea.Active;
fetchRooms(folderId, filter) fetchRooms(folderId, filter).finally(() => {
.then(() => { if (filesSection) {
const url = getCategoryUrl( cleanTimer();
folderId === archiveFolderId setIsLoading(false);
? CategoryType.Archive } else {
: CategoryType.Shared hideLoader();
); }
});
const filterParamsStr = filter.toUrlParams();
history.push(
combineUrl(
window.DocSpaceConfig?.proxy?.url,
homepage,
`${url}?${filterParamsStr}`
)
);
})
.finally(() => {
if (filesSection) {
setIsLoading(false);
} else {
hideLoader();
}
});
} else { } else {
fetchFiles(folderId, null, true, false) fetchFiles(folderId, null, true, false)
.then(() => {
if (!filesSection) {
const filter = FilesFilter.getDefault();
filter.folder = folderId;
const filterParamsStr = filter.toUrlParams();
const url = getCategoryUrl(categoryType, filter.folder);
const pathname = `${url}?${filterParamsStr}`;
history.push(
combineUrl(
window.DocSpaceConfig?.proxy?.url,
config.homepage,
pathname
)
);
}
})
.catch((err) => toastr.error(err)) .catch((err) => toastr.error(err))
.finally(() => { .finally(() => {
if (filesSection) { if (filesSection) {

View File

@ -9,6 +9,7 @@ import ActionsPresentationReactSvgUrl from "PUBLIC_DIR/images/actions.presentati
import CatalogFolderReactSvgUrl from "PUBLIC_DIR/images/catalog.folder.react.svg?url"; import CatalogFolderReactSvgUrl from "PUBLIC_DIR/images/catalog.folder.react.svg?url";
import PersonAdminReactSvgUrl from "PUBLIC_DIR/images/person.admin.react.svg?url"; import PersonAdminReactSvgUrl from "PUBLIC_DIR/images/person.admin.react.svg?url";
import PersonManagerReactSvgUrl from "PUBLIC_DIR/images/person.manager.react.svg?url"; import PersonManagerReactSvgUrl from "PUBLIC_DIR/images/person.manager.react.svg?url";
import PersonReactSvgUrl from "PUBLIC_DIR/images/person.react.svg?url";
import PersonUserReactSvgUrl from "PUBLIC_DIR/images/person.user.react.svg?url"; import PersonUserReactSvgUrl from "PUBLIC_DIR/images/person.user.react.svg?url";
import InviteAgainReactSvgUrl from "PUBLIC_DIR/images/invite.again.react.svg?url"; import InviteAgainReactSvgUrl from "PUBLIC_DIR/images/invite.again.react.svg?url";
import React from "react"; import React from "react";
@ -29,7 +30,7 @@ import { Events, EmployeeType } from "@docspace/common/constants";
import { getMainButtonItems } from "SRC_DIR/helpers/plugins"; import { getMainButtonItems } from "SRC_DIR/helpers/plugins";
import toastr from "@docspace/components/toast/toastr"; import toastr from "@docspace/components/toast/toastr";
import styled from "styled-components"; import styled, { css } from "styled-components";
import Button from "@docspace/components/button"; import Button from "@docspace/components/button";
import { resendInvitesAgain } from "@docspace/common/api/people"; import { resendInvitesAgain } from "@docspace/common/api/people";
@ -38,30 +39,36 @@ const StyledButton = styled(Button)`
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
padding: 0; padding: 0;
opacity: 1; opacity: ${(props) => (props.isDisabled ? 0.6 : 1)};
background-color: ${({ currentColorScheme }) => background-color: ${({ currentColorScheme }) =>
currentColorScheme.main.accent} !important; currentColorScheme.main.accent} !important;
background: ${({ currentColorScheme }) => currentColorScheme.main.accent}; background: ${({ currentColorScheme }) => currentColorScheme.main.accent};
border: ${({ currentColorScheme }) => currentColorScheme.main.accent}; border: ${({ currentColorScheme }) => currentColorScheme.main.accent};
:hover { ${(props) =>
background-color: ${({ currentColorScheme }) => !props.isDisabled &&
currentColorScheme.main.accent}; css`
opacity: 0.85; :hover {
background: ${({ currentColorScheme }) => currentColorScheme.main.accent}; background-color: ${({ currentColorScheme }) =>
border: ${({ currentColorScheme }) => currentColorScheme.main.accent}; currentColorScheme.main.accent};
} opacity: 0.85;
background: ${({ currentColorScheme }) =>
currentColorScheme.main.accent};
border: ${({ currentColorScheme }) => currentColorScheme.main.accent};
}
:active { :active {
background-color: ${({ currentColorScheme }) => background-color: ${({ currentColorScheme }) =>
currentColorScheme.main.accent}; currentColorScheme.main.accent};
background: ${({ currentColorScheme }) => currentColorScheme.main.accent}; background: ${({ currentColorScheme }) =>
border: ${({ currentColorScheme }) => currentColorScheme.main.accent}; currentColorScheme.main.accent};
opacity: 1; border: ${({ currentColorScheme }) => currentColorScheme.main.accent};
filter: brightness(90%); opacity: 1;
cursor: pointer; filter: brightness(90%);
} cursor: pointer;
}
`}
.button-content { .button-content {
color: ${({ currentColorScheme }) => currentColorScheme.text.accent}; color: ${({ currentColorScheme }) => currentColorScheme.text.accent};
@ -113,6 +120,8 @@ const ArticleMainButtonContent = (props) => {
setInvitePanelOptions, setInvitePanelOptions,
mainButtonMobileVisible, mainButtonMobileVisible,
security,
} = props; } = props;
const isAccountsPage = selectedTreeNode[0] === "accounts"; const isAccountsPage = selectedTreeNode[0] === "accounts";
@ -297,6 +306,15 @@ const ArticleMainButtonContent = (props) => {
action: EmployeeType.User, action: EmployeeType.User,
key: "manager", key: "manager",
}, },
{
id: "invite_room-collaborator",
className: "main-button_drop-down",
icon: PersonReactSvgUrl,
label: t("Common:Collaborator"),
onClick: onInvite,
action: EmployeeType.Collaborator,
key: "collaborator",
},
{ {
id: "invite_user", id: "invite_user",
className: "main-button_drop-down", className: "main-button_drop-down",
@ -429,26 +447,27 @@ const ArticleMainButtonContent = (props) => {
? t("Common:Invite") ? t("Common:Invite")
: t("Common:Actions"); : t("Common:Actions");
const isDisabled = const isDisabled = isAccountsPage ? !canInvite : !security?.Create;
((!canCreate || (!canCreateFiles && !isRoomsFolder)) && !canInvite) ||
isArchiveFolder;
const isProfile = history.location.pathname === "/accounts/view/@self"; const isProfile = history.location.pathname === "/accounts/view/@self";
return ( return (
<> <>
{isMobileArticle ? ( {isMobileArticle ? (
<> <>
{!isArticleLoading && !isProfile && (canCreateFiles || canInvite) && ( {!isArticleLoading &&
<MobileView !isProfile &&
t={t} (security?.Create || canInvite) && (
titleProp={t("Upload")} <MobileView
actionOptions={actions} t={t}
buttonOptions={uploadActions} titleProp={t("Upload")}
isRooms={isRoomsFolder} actionOptions={actions}
mainButtonMobileVisible={mainButtonMobileVisible} buttonOptions={uploadActions}
onMainButtonClick={onCreateRoom} isRooms={isRoomsFolder}
/> mainButtonMobileVisible={mainButtonMobileVisible}
)} onMainButtonClick={onCreateRoom}
/>
)}
</> </>
) : isRoomsFolder ? ( ) : isRoomsFolder ? (
<StyledButton <StyledButton
@ -534,9 +553,11 @@ export default inject(
const { enablePlugins, currentColorScheme } = auth.settingsStore; const { enablePlugins, currentColorScheme } = auth.settingsStore;
const security = selectedFolderStore.security;
const currentFolderId = selectedFolderStore.id; const currentFolderId = selectedFolderStore.id;
const { isAdmin, isOwner, isVisitor } = auth.userStore.user; const { isAdmin, isOwner } = auth.userStore.user;
const { canCreateFiles } = accessRightsStore; const { canCreateFiles } = accessRightsStore;
@ -572,9 +593,9 @@ export default inject(
isAdmin, isAdmin,
isOwner, isOwner,
isVisitor,
mainButtonMobileVisible, mainButtonMobileVisible,
security,
}; };
} }
)( )(

View File

@ -65,8 +65,10 @@ const RootFolderContainer = (props) => {
isEmptyPage, isEmptyPage,
setIsEmptyPage, setIsEmptyPage,
isVisitor, isVisitor,
isCollaborator,
sectionWidth, sectionWidth,
setIsLoadedEmptyPage, setIsLoadedEmptyPage,
security,
} = props; } = props;
const personalDescription = t("EmptyFolderDecription"); const personalDescription = t("EmptyFolderDecription");
@ -77,12 +79,14 @@ const RootFolderContainer = (props) => {
const favoritesDescription = t("FavoritesEmptyContainerDescription"); const favoritesDescription = t("FavoritesEmptyContainerDescription");
const recentDescription = t("RecentEmptyContainerDescription"); const recentDescription = t("RecentEmptyContainerDescription");
const roomsDescription = isVisitor const roomsDescription =
? t("RoomEmptyContainerDescriptionUser") isVisitor || isCollaborator
: t("RoomEmptyContainerDescription"); ? t("RoomEmptyContainerDescriptionUser")
const archiveRoomsDescription = isVisitor : t("RoomEmptyContainerDescription");
? t("ArchiveEmptyScreenUser") const archiveRoomsDescription =
: t("ArchiveEmptyScreen"); isVisitor || isCollaborator
? t("ArchiveEmptyScreenUser")
: t("ArchiveEmptyScreen");
const privateRoomHeader = t("PrivateRoomHeader"); const privateRoomHeader = t("PrivateRoomHeader");
const privacyIcon = <img alt="" src={PrivacySvgUrl} />; const privacyIcon = <img alt="" src={PrivacySvgUrl} />;
@ -201,7 +205,7 @@ const RootFolderContainer = (props) => {
imageSrc: theme.isBase imageSrc: theme.isBase
? EmptyScreenCorporateSvgUrl ? EmptyScreenCorporateSvgUrl
: EmptyScreenCorporateDarkSvgUrl, : EmptyScreenCorporateDarkSvgUrl,
buttons: isVisitor ? null : roomsButtons, buttons: !security?.Create ? null : roomsButtons,
}; };
case FolderType.Archive: case FolderType.Archive:
return { return {
@ -386,7 +390,7 @@ export default inject(
setIsEmptyPage, setIsEmptyPage,
setIsLoadedEmptyPage, setIsLoadedEmptyPage,
} = filesStore; } = filesStore;
const { title, rootFolderType } = selectedFolderStore; const { title, rootFolderType, security } = selectedFolderStore;
const { isPrivacyFolder, myFolderId } = treeFoldersStore; const { isPrivacyFolder, myFolderId } = treeFoldersStore;
return { return {
@ -394,6 +398,7 @@ export default inject(
isPrivacyFolder, isPrivacyFolder,
isDesktop: isDesktopClient, isDesktop: isDesktopClient,
isVisitor: auth.userStore.user.isVisitor, isVisitor: auth.userStore.user.isVisitor,
isCollaborator: auth.userStore.user.isCollaborator,
isEncryptionSupport, isEncryptionSupport,
organizationName, organizationName,
privacyInstructions, privacyInstructions,
@ -411,6 +416,7 @@ export default inject(
isEmptyPage, isEmptyPage,
setIsEmptyPage, setIsEmptyPage,
setIsLoadedEmptyPage, setIsLoadedEmptyPage,
security,
}; };
} }
)(withTranslation(["Files"])(observer(RootFolderContainer))); )(withTranslation(["Files"])(observer(RootFolderContainer)));

View File

@ -419,7 +419,7 @@ class TreeFolders extends React.Component {
data: incomingDate, data: incomingDate,
certainFolders, certainFolders,
roomsFolderId, roomsFolderId,
expandedPanelKeys, expandedPanelKeys = [],
} = this.props; } = this.props;
isExpand && this.setState({ isExpand: true }); isExpand && this.setState({ isExpand: true });
//console.log("load data...", treeNode); //console.log("load data...", treeNode);

View File

@ -15,6 +15,7 @@ const ChangeUserTypeEvent = ({
peopleFilter, peopleFilter,
updateUserType, updateUserType,
getUsersList, getUsersList,
onClose,
}) => { }) => {
const { const {
toType, toType,
@ -58,7 +59,7 @@ const ChangeUserTypeEvent = ({
}; };
const onChangeUserType = () => { const onChangeUserType = () => {
onClose(); onClosePanel();
updateUserType(toType, userIDs, peopleFilter, fromType) updateUserType(toType, userIDs, peopleFilter, fromType)
.then(() => { .then(() => {
toastr.success(t("SuccessChangeUserType")); toastr.success(t("SuccessChangeUserType"));
@ -83,14 +84,15 @@ const ChangeUserTypeEvent = ({
}); });
}; };
const onClose = () => { const onClosePanel = () => {
setVisible(false); setVisible(false);
onClose();
}; };
const onCloseAction = async () => { const onCloseAction = async () => {
await getUsersList(peopleFilter); await getUsersList(peopleFilter);
abortCallback && abortCallback(); abortCallback && abortCallback();
onClose(); onClosePanel();
}; };
const getType = (type) => { const getType = (type) => {
@ -99,6 +101,8 @@ const ChangeUserTypeEvent = ({
return t("Common:DocSpaceAdmin"); return t("Common:DocSpaceAdmin");
case "manager": case "manager":
return t("Common:RoomAdmin"); return t("Common:RoomAdmin");
case "collaborator":
return t("Common:Collaborator");
case "user": case "user":
default: default:
return t("Common:User"); return t("Common:User");

View File

@ -48,7 +48,7 @@ const CreateEvent = ({
setEventDialogVisible, setEventDialogVisible,
eventDialogVisible, eventDialogVisible,
createWithoutDialog, keepNewFileName,
}) => { }) => {
const [headerTitle, setHeaderTitle] = React.useState(null); const [headerTitle, setHeaderTitle] = React.useState(null);
const [startValue, setStartValue] = React.useState(""); const [startValue, setStartValue] = React.useState("");
@ -77,7 +77,7 @@ const CreateEvent = ({
if (!extension) return setEventDialogVisible(true); if (!extension) return setEventDialogVisible(true);
if (!createWithoutDialog) { if (!keepNewFileName) {
setEventDialogVisible(true); setEventDialogVisible(true);
} else { } else {
onSave(null, title || defaultName); onSave(null, title || defaultName);
@ -289,6 +289,7 @@ export default inject(
uploadDataStore, uploadDataStore,
dialogsStore, dialogsStore,
oformsStore, oformsStore,
settingsStore,
}) => { }) => {
const { const {
setIsLoading, setIsLoading,
@ -321,7 +322,7 @@ export default inject(
eventDialogVisible, eventDialogVisible,
} = dialogsStore; } = dialogsStore;
const { createWithoutDialog } = filesStore; const { keepNewFileName } = settingsStore;
return { return {
setEventDialogVisible, setEventDialogVisible,
@ -352,7 +353,7 @@ export default inject(
replaceFileStream, replaceFileStream,
setEncryptionAccess, setEncryptionAccess,
createWithoutDialog, keepNewFileName,
}; };
} }
)(observer(CreateEvent)); )(observer(CreateEvent));

View File

@ -22,9 +22,9 @@ const Dialog = ({
onCancel, onCancel,
onClose, onClose,
isCreateDialog, isCreateDialog,
createWithoutDialog,
setCreateWithoutDialog,
extension, extension,
keepNewFileName,
setKeepNewFileName,
}) => { }) => {
const [value, setValue] = useState(""); const [value, setValue] = useState("");
const [isDisabled, setIsDisabled] = useState(false); const [isDisabled, setIsDisabled] = useState(false);
@ -32,8 +32,8 @@ const Dialog = ({
const [isChanged, setIsChanged] = useState(false); const [isChanged, setIsChanged] = useState(false);
useEffect(() => { useEffect(() => {
createWithoutDialog && isCreateDialog && setIsChecked(createWithoutDialog); keepNewFileName && isCreateDialog && setIsChecked(keepNewFileName);
}, [isCreateDialog, createWithoutDialog]); }, [isCreateDialog, keepNewFileName]);
useEffect(() => { useEffect(() => {
let input = document?.getElementById("create-text-input"); let input = document?.getElementById("create-text-input");
@ -80,7 +80,7 @@ const Dialog = ({
const onSaveAction = useCallback( const onSaveAction = useCallback(
(e) => { (e) => {
setIsDisabled(true); setIsDisabled(true);
isCreateDialog && setCreateWithoutDialog(isChecked); isCreateDialog && setKeepNewFileName(isChecked);
onSave && onSave(e, value); onSave && onSave(e, value);
}, },
[onSave, isCreateDialog, value, isChecked] [onSave, isCreateDialog, value, isChecked]
@ -88,7 +88,7 @@ const Dialog = ({
const onCancelAction = useCallback((e) => { const onCancelAction = useCallback((e) => {
if (isChecked) { if (isChecked) {
setCreateWithoutDialog(false); setKeepNewFileName(false);
} }
onCancel && onCancel(e); onCancel && onCancel(e);
}, []); }, []);
@ -96,7 +96,7 @@ const Dialog = ({
const onCloseAction = useCallback( const onCloseAction = useCallback(
(e) => { (e) => {
if (!isDisabled && isChecked) { if (!isDisabled && isChecked) {
setCreateWithoutDialog(false); setKeepNewFileName(false);
} }
onClose && onClose(e); onClose && onClose(e);
}, },
@ -171,9 +171,9 @@ const Dialog = ({
); );
}; };
export default inject(({ auth, filesStore }) => { export default inject(({ auth, settingsStore }) => {
const { folderFormValidation } = auth.settingsStore; const { folderFormValidation } = auth.settingsStore;
const { createWithoutDialog, setCreateWithoutDialog } = filesStore; const { keepNewFileName, setKeepNewFileName } = settingsStore;
return { folderFormValidation, createWithoutDialog, setCreateWithoutDialog }; return { folderFormValidation, keepNewFileName, setKeepNewFileName };
})(observer(Dialog)); })(observer(Dialog));

View File

@ -39,6 +39,8 @@ const Bar = (props) => {
showRoomQuotaBar, showRoomQuotaBar,
showStorageQuotaBar, showStorageQuotaBar,
currentColorScheme,
} = props; } = props;
const [barVisible, setBarVisible] = useState({ const [barVisible, setBarVisible] = useState({
@ -112,13 +114,7 @@ const Bar = (props) => {
}, []); }, []);
const sendActivationLinkAction = () => { const sendActivationLinkAction = () => {
if (sendActivationLink) { sendActivationLink && sendActivationLink(t);
sendActivationLink(t).finally(() => {
return onCloseActivationBar();
});
} else {
onCloseActivationBar();
}
}; };
const onCloseActivationBar = () => { const onCloseActivationBar = () => {
@ -183,6 +179,7 @@ const Bar = (props) => {
return (isRoomQuota || isStorageQuota) && tReady ? ( return (isRoomQuota || isStorageQuota) && tReady ? (
<QuotasBar <QuotasBar
currentColorScheme={currentColorScheme}
isRoomQuota={isRoomQuota} isRoomQuota={isRoomQuota}
{...quotasValue} {...quotasValue}
onClick={onClickQuota} onClick={onClickQuota}
@ -191,6 +188,7 @@ const Bar = (props) => {
/> />
) : withActivationBar && barVisible.confirmEmail && tReady ? ( ) : withActivationBar && barVisible.confirmEmail && tReady ? (
<ConfirmEmailBar <ConfirmEmailBar
currentColorScheme={currentColorScheme}
onLoad={onLoad} onLoad={onLoad}
onClick={sendActivationLinkAction} onClick={sendActivationLinkAction}
onClose={onCloseActivationBar} onClose={onCloseActivationBar}
@ -221,6 +219,8 @@ export default inject(({ auth, profileActionsStore }) => {
showStorageQuotaBar, showStorageQuotaBar,
} = auth.currentQuotaStore; } = auth.currentQuotaStore;
const { currentColorScheme } = auth.settingsStore;
return { return {
isAdmin: user?.isAdmin, isAdmin: user?.isAdmin,
withActivationBar, withActivationBar,
@ -236,5 +236,7 @@ export default inject(({ auth, profileActionsStore }) => {
showRoomQuotaBar, showRoomQuotaBar,
showStorageQuotaBar, showStorageQuotaBar,
currentColorScheme,
}; };
})(withTranslation(["Profile", "Common"])(withRouter(observer(Bar)))); })(withTranslation(["Profile", "Common"])(withRouter(observer(Bar))));

View File

@ -11,10 +11,17 @@ const StyledLink = styled(Link)`
line-height: 16px; line-height: 16px;
font-weight: 400; font-weight: 400;
color: #316daa; color: ${(props) => props.currentColorScheme.main.accent};
`; `;
const ConfirmEmailBar = ({ t, tReady, onClick, onClose, onLoad }) => { const ConfirmEmailBar = ({
t,
tReady,
onClick,
onClose,
onLoad,
currentColorScheme,
}) => {
return ( return (
tReady && ( tReady && (
<SnackBar <SnackBar
@ -22,7 +29,12 @@ const ConfirmEmailBar = ({ t, tReady, onClick, onClose, onLoad }) => {
text={ text={
<> <>
{t("ConfirmEmailDescription")}{" "} {t("ConfirmEmailDescription")}{" "}
<StyledLink onClick={onClick}>{t("RequestActivation")}</StyledLink> <StyledLink
currentColorScheme={currentColorScheme}
onClick={onClick}
>
{t("RequestActivation")}
</StyledLink>
</> </>
} }
isCampaigns={false} isCampaigns={false}

View File

@ -11,7 +11,7 @@ const StyledLink = styled(Link)`
line-height: 16px; line-height: 16px;
font-weight: 400; font-weight: 400;
color: #316daa; color: ${(props) => props.currentColorScheme.main.accent};
`; `;
const QuotasBar = ({ const QuotasBar = ({
@ -23,6 +23,7 @@ const QuotasBar = ({
onClick, onClick,
onClose, onClose,
onLoad, onLoad,
currentColorScheme,
}) => { }) => {
const onClickAction = () => { const onClickAction = () => {
onClick && onClick(isRoomQuota); onClick && onClick(isRoomQuota);
@ -37,7 +38,10 @@ const QuotasBar = ({
description: ( description: (
<Trans i18nKey="RoomQuotaDescription" t={t}> <Trans i18nKey="RoomQuotaDescription" t={t}>
You can archived the unnecessary rooms or You can archived the unnecessary rooms or
<StyledLink onClick={onClickAction}> <StyledLink
currentColorScheme={currentColorScheme}
onClick={onClickAction}
>
{{ clickHere: t("ClickHere") }} {{ clickHere: t("ClickHere") }}
</StyledLink>{" "} </StyledLink>{" "}
to find a better pricing plan for your portal. to find a better pricing plan for your portal.
@ -50,7 +54,10 @@ const QuotasBar = ({
description: ( description: (
<Trans i18nKey="StorageQuotaDescription" t={t}> <Trans i18nKey="StorageQuotaDescription" t={t}>
You can remove the unnecessary files or{" "} You can remove the unnecessary files or{" "}
<StyledLink onClick={onClickAction}> <StyledLink
currentColorScheme={currentColorScheme}
onClick={onClickAction}
>
{{ clickHere: t("ClickHere") }} {{ clickHere: t("ClickHere") }}
</StyledLink>{" "} </StyledLink>{" "}
to find a better pricing plan for your portal. to find a better pricing plan for your portal.

View File

@ -85,7 +85,18 @@ const PeopleSelector = ({
}, [isLoading]); }, [isLoading]);
const toListItem = (item) => { const toListItem = (item) => {
const { id, email, avatar, icon, displayName, hasAvatar } = item; const {
id,
email,
avatar,
icon,
displayName,
hasAvatar,
isOwner,
isAdmin,
isVisitor,
isCollaborator,
} = item;
const role = getUserRole(item); const role = getUserRole(item);
@ -98,6 +109,10 @@ const PeopleSelector = ({
icon, icon,
label: displayName || email, label: displayName || email,
role, role,
isOwner,
isAdmin,
isVisitor,
isCollaborator,
}; };
}; };

View File

@ -79,8 +79,6 @@ const AvatarEditorDialog = (props) => {
const avatars = await createThumbnailsAvatar(profile.id, { const avatars = await createThumbnailsAvatar(profile.id, {
x: 0, x: 0,
y: 0, y: 0,
width: 192,
height: 192,
tmpFile: res.data, tmpFile: res.data,
}); });
updateCreatedAvatar(avatars); updateCreatedAvatar(avatars);

View File

@ -14,10 +14,11 @@ class ResetApplicationDialogComponent extends React.Component {
} }
resetApp = async () => { resetApp = async () => {
const { resetTfaApp, id, onClose, history } = this.props; const { t, resetTfaApp, id, onClose, history } = this.props;
onClose && onClose(); onClose && onClose();
try { try {
const res = await resetTfaApp(id); const res = await resetTfaApp(id);
toastr.success(t("SuccessResetApplication"));
if (res) history.push(res.replace(window.location.origin, "")); if (res) history.push(res.replace(window.location.origin, ""));
} catch (e) { } catch (e) {
toastr.error(e); toastr.error(e);

View File

@ -62,12 +62,19 @@ const AddUsersPanel = ({
const currentItem = shareDataItems.find((x) => x.sharedTo.id === item.id); const currentItem = shareDataItems.find((x) => x.sharedTo.id === item.id);
if (!currentItem) { if (!currentItem) {
const currentAccess =
item.isOwner || item.isAdmin
? ShareAccessRights.RoomManager
: access.access;
const newItem = { const newItem = {
access: access.access, access: currentAccess,
email: item.email, email: item.email,
id: item.id, id: item.id,
displayName: item.label, displayName: item.label,
avatar: item.avatar, avatar: item.avatar,
isOwner: item.isOwner,
isAdmin: item.isAdmin,
}; };
items.push(newItem); items.push(newItem);
} }

View File

@ -35,12 +35,15 @@ const InvitePanel = ({
userLink, userLink,
guestLink, guestLink,
adminLink, adminLink,
collaboratorLink,
defaultAccess, defaultAccess,
inviteUsers, inviteUsers,
setInfoPanelIsMobileHidden, setInfoPanelIsMobileHidden,
reloadSelectionParentRoom, reloadSelectionParentRoom,
setUpdateRoomMembers, setUpdateRoomMembers,
roomsView, roomsView,
getUsersList,
filter,
}) => { }) => {
const [selectedRoom, setSelectedRoom] = useState(null); const [selectedRoom, setSelectedRoom] = useState(null);
const [hasErrors, setHasErrors] = useState(false); const [hasErrors, setHasErrors] = useState(false);
@ -85,7 +88,8 @@ const InvitePanel = ({
useEffect(() => { useEffect(() => {
if (roomId === -1) { if (roomId === -1) {
if (!userLink || !guestLink || !adminLink) getPortalInviteLinks(); if (!userLink || !guestLink || !adminLink || !collaboratorLink)
getPortalInviteLinks();
setShareLinks([ setShareLinks([
{ {
@ -106,6 +110,12 @@ const InvitePanel = ({
shareLink: adminLink, shareLink: adminLink,
access: 3, access: 3,
}, },
{
id: "collaborator",
title: "Collaborator",
shareLink: collaboratorLink,
access: 4,
},
]); ]);
return; return;
@ -113,7 +123,7 @@ const InvitePanel = ({
selectRoom(); selectRoom();
getInfo(); getInfo();
}, [roomId, userLink, guestLink, adminLink]); }, [roomId, userLink, guestLink, adminLink, collaboratorLink]);
useEffect(() => { useEffect(() => {
const hasErrors = inviteItems.some((item) => !!item.errors?.length); const hasErrors = inviteItems.some((item) => !!item.errors?.length);
@ -178,6 +188,10 @@ const InvitePanel = ({
} catch (err) { } catch (err) {
toastr.error(err); toastr.error(err);
setIsLoading(false); setIsLoading(false);
} finally {
if (roomId === -1) {
await getUsersList(filter , false);
}
} }
}; };
@ -261,7 +275,8 @@ const InvitePanel = ({
export default inject(({ auth, peopleStore, filesStore, dialogsStore }) => { export default inject(({ auth, peopleStore, filesStore, dialogsStore }) => {
const { theme } = auth.settingsStore; const { theme } = auth.settingsStore;
const { getUsersByQuery, inviteUsers } = peopleStore.usersStore; const { getUsersByQuery, inviteUsers, getUsersList } = peopleStore.usersStore;
const { filter } = peopleStore.filterStore;
const { const {
setIsMobileHidden: setInfoPanelIsMobileHidden, setIsMobileHidden: setInfoPanelIsMobileHidden,
reloadSelectionParentRoom, reloadSelectionParentRoom,
@ -275,6 +290,7 @@ export default inject(({ auth, peopleStore, filesStore, dialogsStore }) => {
userLink, userLink,
guestLink, guestLink,
adminLink, adminLink,
collaboratorLink,
} = peopleStore.inviteLinksStore; } = peopleStore.inviteLinksStore;
const { const {
@ -308,11 +324,14 @@ export default inject(({ auth, peopleStore, filesStore, dialogsStore }) => {
userLink, userLink,
guestLink, guestLink,
adminLink, adminLink,
collaboratorLink,
inviteUsers, inviteUsers,
setInfoPanelIsMobileHidden, setInfoPanelIsMobileHidden,
reloadSelectionParentRoom, reloadSelectionParentRoom,
setUpdateRoomMembers, setUpdateRoomMembers,
roomsView, roomsView,
getUsersList,
filter,
}; };
})( })(
withTranslation([ withTranslation([

View File

@ -16,6 +16,7 @@ import {
StyledDeleteIcon, StyledDeleteIcon,
StyledText, StyledText,
} from "../StyledInvitePanel"; } from "../StyledInvitePanel";
import { filterUserRoleOptions } from "SRC_DIR/helpers/utils";
const Item = ({ const Item = ({
t, t,
@ -38,7 +39,11 @@ const Item = ({
const accesses = getAccessOptions(t, roomType, true, false, isOwner); const accesses = getAccessOptions(t, roomType, true, false, isOwner);
const defaultAccess = accesses.find((option) => option.access === +access); const filteredAccesses = filterUserRoleOptions(accesses, item, true);
const defaultAccess = filteredAccesses.find(
(option) => option.access === +access
);
const errorsInList = () => { const errorsInList = () => {
const hasErrors = inviteItems.some((item) => !!item.errors?.length); const hasErrors = inviteItems.some((item) => !!item.errors?.length);
@ -128,7 +133,7 @@ const Item = ({
<StyledComboBox <StyledComboBox
onSelect={selectItemAccess} onSelect={selectItemAccess}
noBorder noBorder
options={accesses} options={filteredAccesses}
size="content" size="content"
scaled={false} scaled={false}
manualWidth="fit-content" manualWidth="fit-content"

View File

@ -31,6 +31,17 @@ export const getAccessOptions = (
access: access:
roomType === -1 ? EmployeeType.User : ShareAccessRights.RoomManager, roomType === -1 ? EmployeeType.User : ShareAccessRights.RoomManager,
}, },
collaborator: {
key: "collaborator",
label: t("Common:Collaborator"),
description: t("Translations:RoleCollaboratorDescription"),
quota: t("Common:Paid"),
color: "#EDC409",
access:
roomType === -1
? EmployeeType.Collaborator
: ShareAccessRights.Collaborator,
},
user: { user: {
key: "user", key: "user",
label: t("Common:User"), label: t("Common:User"),
@ -73,6 +84,7 @@ export const getAccessOptions = (
case RoomsType.FillingFormsRoom: case RoomsType.FillingFormsRoom:
options = [ options = [
accesses.roomAdmin, accesses.roomAdmin,
accesses.collaborator,
{ key: "s1", isSeparator: withSeparator }, { key: "s1", isSeparator: withSeparator },
accesses.formFiller, accesses.formFiller,
accesses.viewer, accesses.viewer,
@ -81,6 +93,7 @@ export const getAccessOptions = (
case RoomsType.EditingRoom: case RoomsType.EditingRoom:
options = [ options = [
accesses.roomAdmin, accesses.roomAdmin,
accesses.collaborator,
{ key: "s1", isSeparator: withSeparator }, { key: "s1", isSeparator: withSeparator },
accesses.editor, accesses.editor,
accesses.viewer, accesses.viewer,
@ -89,6 +102,7 @@ export const getAccessOptions = (
case RoomsType.ReviewRoom: case RoomsType.ReviewRoom:
options = [ options = [
accesses.roomAdmin, accesses.roomAdmin,
accesses.collaborator,
{ key: "s1", isSeparator: withSeparator }, { key: "s1", isSeparator: withSeparator },
accesses.reviewer, accesses.reviewer,
accesses.commentator, accesses.commentator,
@ -98,6 +112,7 @@ export const getAccessOptions = (
case RoomsType.ReadOnlyRoom: case RoomsType.ReadOnlyRoom:
options = [ options = [
accesses.roomAdmin, accesses.roomAdmin,
accesses.collaborator,
{ key: "s1", isSeparator: withSeparator }, { key: "s1", isSeparator: withSeparator },
accesses.viewer, accesses.viewer,
]; ];
@ -105,6 +120,7 @@ export const getAccessOptions = (
case RoomsType.CustomRoom: case RoomsType.CustomRoom:
options = [ options = [
accesses.roomAdmin, accesses.roomAdmin,
accesses.collaborator,
{ key: "s1", isSeparator: withSeparator }, { key: "s1", isSeparator: withSeparator },
accesses.editor, accesses.editor,
accesses.formFiller, accesses.formFiller,
@ -119,6 +135,7 @@ export const getAccessOptions = (
options = [ options = [
...options, ...options,
accesses.roomAdmin, accesses.roomAdmin,
accesses.collaborator,
{ key: "s1", isSeparator: withSeparator }, { key: "s1", isSeparator: withSeparator },
accesses.user, accesses.user,
]; ];

View File

@ -7,7 +7,6 @@ import toastr from "@docspace/components/toast/toastr";
import SelectFolderDialog from "../SelectFolderDialog"; import SelectFolderDialog from "../SelectFolderDialog";
import SimpleFileInput from "../../SimpleFileInput"; import SimpleFileInput from "../../SimpleFileInput";
import { withTranslation } from "react-i18next"; import { withTranslation } from "react-i18next";
import SelectionPanel from "../SelectionPanel/SelectionPanelBody";
import { FolderType } from "@docspace/common/constants"; import { FolderType } from "@docspace/common/constants";
class SelectFolderInput extends React.PureComponent { class SelectFolderInput extends React.PureComponent {
constructor(props) { constructor(props) {
@ -113,7 +112,7 @@ class SelectFolderInput extends React.PureComponent {
onSelect && onSelect(folderId); onSelect && onSelect(folderId);
}; };
onSetFolderInfo = (folderId) => { onSetFolderInfo = (folderId) => {
const { setExpandedPanelKeys, setParentId } = this.props; const { setExpandedPanelKeys, setParentId, clearLocalStorage } = this.props;
getFolder(folderId) getFolder(folderId)
.then((data) => { .then((data) => {
@ -123,7 +122,10 @@ class SelectFolderInput extends React.PureComponent {
setExpandedPanelKeys(pathParts); setExpandedPanelKeys(pathParts);
setParentId(data.current.parentId); setParentId(data.current.parentId);
}) })
.catch((e) => toastr.error(e)); .catch((e) => {
toastr.error(e);
clearLocalStorage();
});
}; };
render() { render() {
@ -201,7 +203,9 @@ export default inject(
treeFoldersStore, treeFoldersStore,
selectFolderDialogStore, selectFolderDialogStore,
selectedFolderStore, selectedFolderStore,
backup,
}) => { }) => {
const { clearLocalStorage } = backup;
const { setFirstLoad } = filesStore; const { setFirstLoad } = filesStore;
const { setExpandedPanelKeys } = treeFoldersStore; const { setExpandedPanelKeys } = treeFoldersStore;
const { const {
@ -220,6 +224,7 @@ export default inject(
const { setParentId } = selectedFolderStore; const { setParentId } = selectedFolderStore;
return { return {
clearLocalStorage,
setFirstLoad, setFirstLoad,
setExpandedPanelKeys, setExpandedPanelKeys,
setParentId, setParentId,

View File

@ -12,9 +12,6 @@ import ActionsUploadedFile from "./SubComponents/ActionsUploadedFile";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import NoUserSelect from "@docspace/components/utils/commonStyles"; import NoUserSelect from "@docspace/components/utils/commonStyles";
import Button from "@docspace/components/button"; import Button from "@docspace/components/button";
import globalColors from "@docspace/components/utils/globalColors";
const buttonColor = globalColors.blueDisabled;
const StyledFileRow = styled(Row)` const StyledFileRow = styled(Row)`
width: calc(100% - 16px); width: calc(100% - 16px);
@ -145,11 +142,11 @@ class FileRow extends Component {
onCancelCurrentUpload = (e) => { onCancelCurrentUpload = (e) => {
//console.log("cancel upload ", e); //console.log("cancel upload ", e);
const { id, action, fileId } = e.currentTarget.dataset; const { id, action, fileId } = e.currentTarget.dataset;
const { cancelCurrentUpload, cancelCurrentFileConversion } = this.props; const { t, cancelCurrentUpload, cancelCurrentFileConversion } = this.props;
return action === "convert" return action === "convert"
? cancelCurrentFileConversion(fileId) ? cancelCurrentFileConversion(fileId)
: cancelCurrentUpload(id); : cancelCurrentUpload(id, t);
}; };
onMediaClick = (id) => { onMediaClick = (id) => {
@ -228,8 +225,6 @@ class FileRow extends Component {
t, t,
item, item,
uploaded, uploaded,
//onMediaClick,
currentFileUploadProgress,
fileIcon, fileIcon,
isMedia, isMedia,
ext, ext,
@ -314,7 +309,7 @@ class FileRow extends Component {
data-id={item.uniqueId} data-id={item.uniqueId}
onClick={this.onCancelCurrentUpload} onClick={this.onCancelCurrentUpload}
> >
<LoadingButton percent={currentFileUploadProgress} /> <LoadingButton item={item} />
</div> </div>
)} )}
{showPasswordInput && ( {showPasswordInput && (
@ -365,7 +360,6 @@ export default inject(
const { canViewedDocs, getIconSrc, isArchive } = settingsStore; const { canViewedDocs, getIconSrc, isArchive } = settingsStore;
const { const {
uploaded, uploaded,
primaryProgressDataStore,
cancelCurrentUpload, cancelCurrentUpload,
cancelCurrentFileConversion, cancelCurrentFileConversion,
setUploadPanelVisible, setUploadPanelVisible,
@ -379,7 +373,6 @@ export default inject(
setMediaViewerData, setMediaViewerData,
setCurrentItem, setCurrentItem,
} = mediaViewerDataStore; } = mediaViewerDataStore;
const { loadingFile: file } = primaryProgressDataStore;
const isMedia = const isMedia =
item.fileInfo?.viewAccessability?.ImageView || item.fileInfo?.viewAccessability?.ImageView ||
@ -390,25 +383,16 @@ export default inject(
const fileIcon = getIconSrc(ext, 24); const fileIcon = getIconSrc(ext, 24);
const loadingFile = !file || !file.uniqueId ? null : file;
const currentFileUploadProgress =
file && loadingFile.uniqueId === item.uniqueId
? loadingFile.percent
: null;
const downloadInCurrentTab = isArchive(ext) || !canViewedDocs(ext); const downloadInCurrentTab = isArchive(ext) || !canViewedDocs(ext);
return { return {
isPersonal: personal, isPersonal: personal,
theme, theme,
currentFileUploadProgress,
uploaded, uploaded,
isMedia, isMedia: !!isMedia,
fileIcon, fileIcon,
ext, ext,
name, name,
loadingFile,
isMediaActive, isMediaActive,
downloadInCurrentTab, downloadInCurrentTab,
removeFileFromList, removeFileFromList,

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { inject, observer } from "mobx-react";
import { import {
StyledCircle, StyledCircle,
StyledCircleWrap,
StyledLoadingButton, StyledLoadingButton,
} from "@docspace/common/components/StyledLoadingButton"; } from "@docspace/common/components/StyledLoadingButton";
@ -16,7 +16,6 @@ const LoadingButton = (props) => {
onClick, onClick,
isConversion, isConversion,
inConversion, inConversion,
...rest
} = props; } = props;
const [isAnimation, setIsAnimation] = useState(true); const [isAnimation, setIsAnimation] = useState(true);
@ -61,4 +60,22 @@ const LoadingButton = (props) => {
); );
}; };
export default LoadingButton; export default inject(({ uploadDataStore }, { item }) => {
const { primaryProgressDataStore, isParallel } = uploadDataStore;
const { loadingFile: file } = primaryProgressDataStore;
const loadingFile = !file || !file.uniqueId ? null : file;
const currentFileUploadProgress =
file && loadingFile?.uniqueId === item?.uniqueId
? loadingFile.percent
: null;
return {
percent: isParallel
? item?.percent
? item.percent
: null
: currentFileUploadProgress,
};
})(observer(LoadingButton));

View File

@ -18,7 +18,7 @@ class ConfirmRoute extends React.Component {
} }
componentDidMount() { componentDidMount() {
const { forUnauthorized, history, isAuthenticated } = this.props; const { forUnauthorized, isAuthenticated } = this.props;
if (forUnauthorized && isAuthenticated) { if (forUnauthorized && isAuthenticated) {
this.props.logout(); this.props.logout();
@ -42,18 +42,25 @@ class ConfirmRoute extends React.Component {
.then((validationResult) => { .then((validationResult) => {
switch (validationResult) { switch (validationResult) {
case ValidationResult.Ok: case ValidationResult.Ok:
const confirmHeader = `${confirmLinkData}&${search.slice(1)}`; const confirmHeader = search.slice(1);
const linkData = { const linkData = {
...confirmLinkData, ...confirmLinkData,
confirmHeader, confirmHeader,
}; };
console.log("checkConfirmLink", {
confirmLinkData,
validationResult,
linkData,
});
this.setState({ this.setState({
isLoaded: true, isLoaded: true,
linkData, linkData,
}); });
break; break;
case ValidationResult.Invalid: case ValidationResult.Invalid:
console.error("invlid link"); console.error("invlid link", { confirmLinkData, validationResult });
window.location.href = combineUrl( window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url, window.DocSpaceConfig?.proxy?.url,
path, path,
@ -61,7 +68,10 @@ class ConfirmRoute extends React.Component {
); );
break; break;
case ValidationResult.Expired: case ValidationResult.Expired:
console.error("expired link"); console.error("expired link", {
confirmLinkData,
validationResult,
});
window.location.href = combineUrl( window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url, window.DocSpaceConfig?.proxy?.url,
path, path,
@ -69,7 +79,10 @@ class ConfirmRoute extends React.Component {
); );
break; break;
default: default:
console.error("unknown link"); console.error("unknown link", {
confirmLinkData,
validationResult,
});
window.location.href = combineUrl( window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url, window.DocSpaceConfig?.proxy?.url,
path, path,
@ -79,7 +92,7 @@ class ConfirmRoute extends React.Component {
} }
}) })
.catch((error) => { .catch((error) => {
console.error(error); console.error("FAILED checkConfirmLink", { error, confirmLinkData });
window.location.href = combineUrl( window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url, window.DocSpaceConfig?.proxy?.url,
path, path,

View File

@ -51,6 +51,8 @@ export const CategoryType = Object.freeze({
Favorite: 5, Favorite: 5,
Recent: 6, Recent: 6,
Trash: 7, Trash: 7,
Settings: 8,
Accounts: 9,
}); });
/** /**

View File

@ -40,8 +40,9 @@ export const getAccessIcon = (access) => {
}; };
export const getTitleWithoutExst = (item, fromTemplate) => { export const getTitleWithoutExst = (item, fromTemplate) => {
return item.fileExst && !fromTemplate const titleWithoutExst = item.title.split(".").slice(0, -1).join(".");
? item.title.split(".").slice(0, -1).join(".") return titleWithoutExst && item.fileExst && !fromTemplate
? titleWithoutExst
: item.title; : item.title;
}; };

View File

@ -10,26 +10,3 @@ export const thumbnailStatuses = {
}; };
export const ADS_TIMEOUT = 300000; // 5 min export const ADS_TIMEOUT = 300000; // 5 min
export const FilterGroups = Object.freeze({
filterType: "filter-filterType",
filterAuthor: "filter-author",
filterFolders: "filter-folders",
filterContent: "filter-withContent",
roomFilterProviderType: "filter-provider-type",
roomFilterType: "filter-type",
roomFilterSubject: "filter-subject",
roomFilterOwner: "filter-owner",
roomFilterTags: "filter-tags",
roomFilterFolders: "filter-withSubfolders",
roomFilterContent: "filter-content",
});
export const FilterKeys = Object.freeze({
withSubfolders: "withSubfolders",
excludeSubfolders: "excludeSubfolders",
withContent: "withContent",
me: "me",
other: "other",
user: "user",
});

View File

@ -2,7 +2,7 @@ import authStore from "@docspace/common/store/AuthStore";
import { toCommunityHostname, getLanguage } from "@docspace/common/utils"; import { toCommunityHostname, getLanguage } from "@docspace/common/utils";
import history from "@docspace/common/history"; import history from "@docspace/common/history";
import { CategoryType } from "./constants"; import { CategoryType } from "./constants";
import { FolderType } from "@docspace/common/constants"; import { FolderType, ShareAccessRights } from "@docspace/common/constants";
import { translations } from "./autoGeneratedTranslations"; import { translations } from "./autoGeneratedTranslations";
export const setDocumentTitle = (subTitle = null) => { export const setDocumentTitle = (subTitle = null) => {
@ -125,8 +125,12 @@ export const getCategoryType = (location) => {
categoryType = CategoryType.Favorite; categoryType = CategoryType.Favorite;
} else if (pathname.startsWith("/recent")) { } else if (pathname.startsWith("/recent")) {
categoryType = CategoryType.Recent; categoryType = CategoryType.Recent;
} else if (pathname.startsWith("/trash")) { } else if (pathname.startsWith("/files/trash")) {
categoryType = CategoryType.Trash; categoryType = CategoryType.Trash;
} else if (pathname.startsWith("/settings")) {
categoryType = CategoryType.Settings;
} else if (pathname.startsWith("/accounts/filter")) {
categoryType = CategoryType.Accounts;
} }
return categoryType; return categoryType;
@ -186,3 +190,24 @@ export const getCategoryUrl = (categoryType, folderId = null) => {
throw new Error("Unknown category type"); throw new Error("Unknown category type");
} }
}; };
export const filterUserRoleOptions = (
options,
currentUser,
withRemove = false
) => {
let newOptions = [...options];
if (currentUser?.isAdmin || currentUser?.isOwner) {
newOptions = newOptions.filter(
(o) =>
+o.access === ShareAccessRights.RoomManager ||
+o.access === ShareAccessRights.None ||
(withRemove && (o.key === "s2" || o.key === "remove"))
);
return newOptions;
}
return newOptions;
};

View File

@ -20,6 +20,7 @@ const PeopleSection = React.memo(() => {
<PrivateRoute exact path={["/accounts/view/@self"]} component={Profile} /> <PrivateRoute exact path={["/accounts/view/@self"]} component={Profile} />
<PrivateRoute <PrivateRoute
exact exact
withManager
path={["/accounts"]} path={["/accounts"]}
component={HomeRedirectToFilter} component={HomeRedirectToFilter}
/> />

View File

@ -12,6 +12,7 @@ import {
SendInviteDialog, SendInviteDialog,
DeleteUsersDialog, DeleteUsersDialog,
ChangeNameDialog, ChangeNameDialog,
ResetApplicationDialog,
} from "SRC_DIR/components/dialogs"; } from "SRC_DIR/components/dialogs";
const Dialogs = ({ const Dialogs = ({
@ -28,10 +29,12 @@ const Dialogs = ({
disableDialogVisible, disableDialogVisible,
sendInviteDialogVisible, sendInviteDialogVisible,
deleteDialogVisible, deleteDialogVisible,
resetAuthDialogVisible,
changeNameVisible, changeNameVisible,
setChangeNameVisible, setChangeNameVisible,
profile, profile,
resetTfaApp,
}) => { }) => {
return ( return (
<> <>
@ -101,6 +104,15 @@ const Dialogs = ({
fromList fromList
/> />
)} )}
{resetAuthDialogVisible && (
<ResetApplicationDialog
visible={resetAuthDialogVisible}
onClose={closeDialogs}
resetTfaApp={resetTfaApp}
id={data}
/>
)}
</> </>
); );
}; };
@ -121,6 +133,7 @@ export default inject(({ auth, peopleStore }) => {
disableDialogVisible, disableDialogVisible,
sendInviteDialogVisible, sendInviteDialogVisible,
deleteDialogVisible, deleteDialogVisible,
resetAuthDialogVisible,
} = peopleStore.dialogStore; } = peopleStore.dialogStore;
const { user: profile } = auth.userStore; const { user: profile } = auth.userStore;
@ -130,6 +143,10 @@ export default inject(({ auth, peopleStore }) => {
setChangeNameVisible, setChangeNameVisible,
} = peopleStore.targetUserStore; } = peopleStore.targetUserStore;
const { tfaStore } = auth;
const { unlinkApp: resetTfaApp } = tfaStore;
return { return {
changeEmail, changeEmail,
changePassword, changePassword,
@ -145,9 +162,12 @@ export default inject(({ auth, peopleStore }) => {
disableDialogVisible, disableDialogVisible,
sendInviteDialogVisible, sendInviteDialogVisible,
deleteDialogVisible, deleteDialogVisible,
resetAuthDialogVisible,
changeNameVisible, changeNameVisible,
setChangeNameVisible, setChangeNameVisible,
profile, profile,
resetTfaApp,
}; };
})(observer(Dialogs)); })(observer(Dialogs));

View File

@ -40,7 +40,14 @@ const UserContent = ({
t, t,
theme, theme,
}) => { }) => {
const { displayName, email, statusType, role, isVisitor } = item; const {
displayName,
email,
statusType,
role,
isVisitor,
isCollaborator,
} = item;
const nameColor = const nameColor =
statusType === "pending" || statusType === "disabled" statusType === "pending" || statusType === "disabled"
@ -53,6 +60,8 @@ const UserContent = ({
? t("Common:Owner") ? t("Common:Owner")
: role === "admin" : role === "admin"
? t("Common:DocSpaceAdmin") ? t("Common:DocSpaceAdmin")
: isCollaborator
? t("Common:Collaborator")
: isVisitor : isVisitor
? t("Common:User") ? t("Common:User")
: t("Common:RoomAdmin"); : t("Common:RoomAdmin");

View File

@ -142,6 +142,7 @@ const PeopleTableRow = (props) => {
role, role,
isVisitor, isVisitor,
isCollaborator,
} = item; } = item;
const isPending = statusType === "pending" || statusType === "disabled"; const isPending = statusType === "pending" || statusType === "disabled";
@ -168,6 +169,12 @@ const PeopleTableRow = (props) => {
label: t("Common:RoomAdmin"), label: t("Common:RoomAdmin"),
action: "manager", action: "manager",
}; };
const collaboratorOption = {
key: "collaborator",
title: t("Common:Collaborator"),
label: t("Common:Collaborator"),
action: "collaborator",
};
const userOption = { const userOption = {
key: "user", key: "user",
title: t("Common:User"), title: t("Common:User"),
@ -179,10 +186,12 @@ const PeopleTableRow = (props) => {
options.push(managerOption); options.push(managerOption);
if (isCollaborator || isVisitor) options.push(collaboratorOption);
isVisitor && options.push(userOption); isVisitor && options.push(userOption);
return options; return options;
}, [t, isOwner, isVisitor]); }, [t, isOwner, isVisitor, isCollaborator]);
const onAbort = () => { const onAbort = () => {
setIsLoading(false); setIsLoading(false);
@ -227,6 +236,8 @@ const PeopleTableRow = (props) => {
return t("Common:DocSpaceAdmin"); return t("Common:DocSpaceAdmin");
case "manager": case "manager":
return t("Common:RoomAdmin"); return t("Common:RoomAdmin");
case "collaborator":
return t("Common:Collaborator");
case "user": case "user":
return t("Common:User"); return t("Common:User");
} }

View File

@ -11,7 +11,11 @@ import Loaders from "@docspace/common/components/Loaders";
import { withLayoutSize } from "@docspace/common/utils"; import { withLayoutSize } from "@docspace/common/utils";
import withPeopleLoader from "SRC_DIR/HOCs/withPeopleLoader"; import withPeopleLoader from "SRC_DIR/HOCs/withPeopleLoader";
import { EmployeeType, PaymentsType } from "@docspace/common/constants"; import {
EmployeeType,
EmployeeStatus,
PaymentsType,
} from "@docspace/common/constants";
const getStatus = (filterValues) => { const getStatus = (filterValues) => {
const employeeStatus = result( const employeeStatus = result(
@ -69,7 +73,6 @@ const SectionFilterContent = ({
}) => { }) => {
const [selectedFilterValues, setSelectedFilterValues] = React.useState(null); const [selectedFilterValues, setSelectedFilterValues] = React.useState(null);
//TODO: add new options from filter after update backend and fix manager from role
const onFilter = (data) => { const onFilter = (data) => {
const status = getStatus(data); const status = getStatus(data);
@ -80,8 +83,12 @@ const SectionFilterContent = ({
const newFilter = filter.clone(); const newFilter = filter.clone();
if (status === 3) { if (status === 3) {
newFilter.employeeStatus = 2; newFilter.employeeStatus = EmployeeStatus.Disabled;
newFilter.activationStatus = null; newFilter.activationStatus = null;
} else if (status === 2) {
console.log(status);
newFilter.employeeStatus = EmployeeStatus.Active;
newFilter.activationStatus = status;
} else { } else {
newFilter.employeeStatus = null; newFilter.employeeStatus = null;
newFilter.activationStatus = status; newFilter.activationStatus = status;
@ -94,6 +101,7 @@ const SectionFilterContent = ({
newFilter.group = group; newFilter.group = group;
newFilter.payments = payments; newFilter.payments = payments;
//console.log(newFilter);
setIsLoading(true); setIsLoading(true);
fetchPeople(newFilter, true).finally(() => setIsLoading(false)); fetchPeople(newFilter, true).finally(() => setIsLoading(false));
@ -123,7 +131,6 @@ const SectionFilterContent = ({
fetchPeople(newFilter, true).finally(() => setIsLoading(false)); fetchPeople(newFilter, true).finally(() => setIsLoading(false));
}; };
// TODO: change translation keys
const getData = async () => { const getData = async () => {
const statusItems = [ const statusItems = [
{ {
@ -172,6 +179,12 @@ const SectionFilterContent = ({
group: "filter-type", group: "filter-type",
label: t("Common:RoomAdmin"), label: t("Common:RoomAdmin"),
}, },
{
id: "filter_type-room-admin",
key: EmployeeType.Collaborator,
group: "filter-type",
label: t("Common:Collaborator"),
},
{ {
id: "filter_type-user", id: "filter_type-user",
key: EmployeeType.Guest, key: EmployeeType.Guest,
@ -312,6 +325,9 @@ const SectionFilterContent = ({
case EmployeeType.User: case EmployeeType.User:
label = t("Common:RoomAdmin"); label = t("Common:RoomAdmin");
break; break;
case EmployeeType.Collaborator:
label = t("Common:Collaborator");
break;
case EmployeeType.Guest: case EmployeeType.Guest:
label = t("Common:User"); label = t("Common:User");
break; break;

View File

@ -72,6 +72,7 @@ const StyledContainer = styled.div`
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: 33px;
display: grid; display: grid;
align-items: center; align-items: center;

View File

@ -42,11 +42,20 @@ const PureHome = ({
setSelectedNode, setSelectedNode,
withPaging, withPaging,
onClickBack,
}) => { }) => {
const { location } = history; const { location } = history;
const { pathname } = location; const { pathname } = location;
//console.log("People Home render"); //console.log("People Home render");
useEffect(() => {
window.addEventListener("popstate", onClickBack);
return () => {
window.removeEventListener("popstate", onClickBack);
};
}, []);
useEffect(() => { useEffect(() => {
if (pathname.indexOf("/accounts/filter") > -1) { if (pathname.indexOf("/accounts/filter") > -1) {
setSelectedNode(["accounts", "filter"]); setSelectedNode(["accounts", "filter"]);
@ -95,6 +104,7 @@ const PureHome = ({
<Section.SectionBody> <Section.SectionBody>
<SectionBodyContent /> <SectionBodyContent />
</Section.SectionBody> </Section.SectionBody>
<Section.InfoPanelHeader> <Section.InfoPanelHeader>
<InfoPanelHeaderContent /> <InfoPanelHeaderContent />
</Section.InfoPanelHeader> </Section.InfoPanelHeader>
@ -120,36 +130,45 @@ PureHome.propTypes = {
const Home = withTranslation("People")(PureHome); const Home = withTranslation("People")(PureHome);
export default inject(({ auth, peopleStore, treeFoldersStore }) => { export default inject(
const { settingsStore } = auth; ({ auth, peopleStore, treeFoldersStore, filesActionsStore }) => {
const { showCatalog, withPaging } = settingsStore; const { settingsStore } = auth;
const { usersStore, selectedGroupStore, loadingStore, viewAs } = peopleStore; const { showCatalog, withPaging } = settingsStore;
const { getUsersList } = usersStore; const {
const { selectedGroup } = selectedGroupStore; usersStore,
const { setSelectedNode } = treeFoldersStore; selectedGroupStore,
const { loadingStore,
isLoading, viewAs,
setIsLoading, } = peopleStore;
setIsRefresh, const { getUsersList } = usersStore;
firstLoad, const { selectedGroup } = selectedGroupStore;
setFirstLoad, const { setSelectedNode } = treeFoldersStore;
} = loadingStore; const { onClickBack } = filesActionsStore;
const {
isLoading,
setIsLoading,
setIsRefresh,
firstLoad,
setFirstLoad,
} = loadingStore;
return { return {
isAdmin: auth.isAdmin, isAdmin: auth.isAdmin,
isLoading, isLoading,
getUsersList, getUsersList,
setIsLoading, setIsLoading,
setIsRefresh, setIsRefresh,
selectedGroup, selectedGroup,
showCatalog, showCatalog,
firstLoad, firstLoad,
setFirstLoad, setFirstLoad,
viewAs, viewAs,
setSelectedNode, setSelectedNode,
checkedMaintenance: auth.settingsStore.checkedMaintenance, checkedMaintenance: auth.settingsStore.checkedMaintenance,
setMaintenanceExist: auth.settingsStore.setMaintenanceExist, setMaintenanceExist: auth.settingsStore.setMaintenanceExist,
snackbarExist: auth.settingsStore.snackbarExist, snackbarExist: auth.settingsStore.snackbarExist,
withPaging, withPaging,
}; onClickBack,
})(observer(withRouter(Home))); };
}
)(observer(withRouter(Home)));

View File

@ -6,17 +6,18 @@ import Section from "@docspace/common/components/Section";
import { combineUrl } from "@docspace/common/utils"; import { combineUrl } from "@docspace/common/utils";
import tryRedirectTo from "@docspace/common/utils/tryRedirectTo"; import tryRedirectTo from "@docspace/common/utils/tryRedirectTo";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import { EmployeeActivationStatus } from "@docspace/common/constants";
class ActivateEmail extends React.PureComponent { class ActivateEmail extends React.PureComponent {
componentDidMount() { componentDidMount() {
const { logout, changeEmail, linkData } = this.props; const { logout, updateEmailActivationStatus, linkData } = this.props;
const [email, uid, key] = [ const [email, uid, key] = [
linkData.email, linkData.email,
linkData.uid, linkData.uid,
linkData.confirmHeader, linkData.confirmHeader,
]; ];
logout().then(() => logout().then(() =>
changeEmail(uid, email, key) updateEmailActivationStatus(EmployeeActivationStatus.Activated, uid, key)
.then((res) => { .then((res) => {
tryRedirectTo( tryRedirectTo(
combineUrl( combineUrl(
@ -70,6 +71,6 @@ export default inject(({ auth }) => {
const { logout, userStore } = auth; const { logout, userStore } = auth;
return { return {
logout, logout,
changeEmail: userStore.changeEmail, updateEmailActivationStatus: userStore.updateEmailActivationStatus,
}; };
})(withRouter(observer(ActivateEmailForm))); })(withRouter(observer(ActivateEmailForm)));

View File

@ -91,6 +91,7 @@ const FilesSection = React.memo(() => {
<PrivateRoute <PrivateRoute
restricted restricted
withManager withManager
withCollaborator
path={[ path={[
"/rooms/personal", "/rooms/personal",
"/rooms/personal/filter", "/rooms/personal/filter",
@ -144,7 +145,6 @@ const FilesSection = React.memo(() => {
<PrivateRoute <PrivateRoute
exact exact
restricted restricted
withManager
path={"/settings/admin"} path={"/settings/admin"}
component={Settings} component={Settings}
/> />

View File

@ -100,9 +100,8 @@ class Tile extends React.PureComponent {
}; };
onShowTemplateInfo = () => { onShowTemplateInfo = () => {
if (!this.props.isInfoPanelVisible) { this.onSelectForm();
this.props.setIsInfoPanelVisible(true); if (!this.props.isInfoPanelVisible) this.props.setIsInfoPanelVisible(true);
}
}; };
getOptions = () => ["create", "template-info"]; getOptions = () => ["create", "template-info"];

View File

@ -7,6 +7,7 @@ import getCorrectDate from "@docspace/components/utils/getCorrectDate";
import Link from "@docspace/components/link"; import Link from "@docspace/components/link";
import Text from "@docspace/components/text"; import Text from "@docspace/components/text";
import Tag from "@docspace/components/tag"; import Tag from "@docspace/components/tag";
import { decode } from "he";
import { import {
connectedCloudsTypeTitleTranslation as getProviderTranslation, connectedCloudsTypeTitleTranslation as getProviderTranslation,
@ -227,8 +228,8 @@ class DetailsHelper {
const onOpenUser = () => this.openUser(this.item.createdBy, this.history); const onOpenUser = () => this.openUser(this.item.createdBy, this.history);
return this.personal || this.isVisitor return this.personal || this.isVisitor
? text(decodeString(this.item.createdBy?.displayName)) ? text(decode(this.item.createdBy?.displayName))
: link(decodeString(this.item.createdBy?.displayName), onOpenUser); : link(decode(this.item.createdBy?.displayName), onOpenUser);
}; };
getItemLocation = () => { getItemLocation = () => {
@ -279,8 +280,8 @@ class DetailsHelper {
const onOpenUser = () => this.openUser(this.item.updatedBy, this.history); const onOpenUser = () => this.openUser(this.item.updatedBy, this.history);
return this.personal || this.isVisitor return this.personal || this.isVisitor
? text(decodeString(this.item.updatedBy?.displayName)) ? text(decode(this.item.updatedBy?.displayName))
: link(decodeString(this.item.updatedBy?.displayName), onOpenUser); : link(decode(this.item.updatedBy?.displayName), onOpenUser);
}; };
getItemCreationDate = () => { getItemCreationDate = () => {

View File

@ -21,6 +21,11 @@ class MembersHelper {
label: this.t("Common:RoomAdmin"), label: this.t("Common:RoomAdmin"),
access: ShareAccessRights.RoomManager, access: ShareAccessRights.RoomManager,
}, },
collaborator: {
key: "collaborator",
label: this.t("Common:Collaborator"),
access: ShareAccessRights.Collaborator,
},
viewer: { viewer: {
key: "viewer", key: "viewer",
label: this.t("Translations:RoleViewer"), label: this.t("Translations:RoleViewer"),
@ -69,6 +74,7 @@ class MembersHelper {
case RoomsType.FillingFormsRoom: case RoomsType.FillingFormsRoom:
return [ return [
options.roomAdmin, options.roomAdmin,
options.collaborator,
options.formFiller, options.formFiller,
options.viewer, options.viewer,
...deleteOption, ...deleteOption,
@ -76,6 +82,7 @@ class MembersHelper {
case RoomsType.EditingRoom: case RoomsType.EditingRoom:
return [ return [
options.roomAdmin, options.roomAdmin,
options.collaborator,
options.editor, options.editor,
options.viewer, options.viewer,
...deleteOption, ...deleteOption,
@ -83,16 +90,23 @@ class MembersHelper {
case RoomsType.ReviewRoom: case RoomsType.ReviewRoom:
return [ return [
options.roomAdmin, options.roomAdmin,
options.collaborator,
options.reviewer, options.reviewer,
options.commentator, options.commentator,
options.viewer, options.viewer,
...deleteOption, ...deleteOption,
]; ];
case RoomsType.ReadOnlyRoom: case RoomsType.ReadOnlyRoom:
return [options.roomAdmin, options.viewer, ...deleteOption]; return [
options.roomAdmin,
options.collaborator,
options.viewer,
...deleteOption,
];
case RoomsType.CustomRoom: case RoomsType.CustomRoom:
return [ return [
options.roomAdmin, options.roomAdmin,
options.collaborator,
options.editor, options.editor,
options.formFiller, options.formFiller,
options.reviewer, options.reviewer,

View File

@ -1,9 +1,10 @@
import styled, { css } from "styled-components"; import styled, { css } from "styled-components";
import { Base } from "@docspace/components/themes"; import { Base } from "@docspace/components/themes";
import { hugeMobile, tablet } from "@docspace/components/utils/device";
const StyledAccountsItemTitle = styled.div` const StyledAccountsItemTitle = styled.div`
min-height: 104px; min-height: 80px;
height: 104px; height: 80px;
max-height: 104px; max-height: 104px;
display: flex; display: flex;
@ -11,8 +12,29 @@ const StyledAccountsItemTitle = styled.div`
justify-content: start; justify-content: start;
gap: 16px; gap: 16px;
position: fixed;
margin-top: -80px;
margin-left: -20px;
width: calc(100% - 40px);
padding: 24px 0 24px 20px;
background: ${(props) => props.theme.infoPanel.backgroundColor};
z-index: 100;
@media ${tablet} {
width: 440px;
padding: 24px 20px 24px 20px;
}
@media (max-width: 549px) {
width: calc(100vw - 69px - 40px);
}
@media ${hugeMobile} {
width: calc(100vw - 32px);
padding: 24px 0 24px 16px;
}
.avatar { .avatar {
padding-top: 24px;
min-width: 80px; min-width: 80px;
} }

View File

@ -7,9 +7,9 @@ const StyledInfoPanelBody = styled.div`
${({ isAccounts }) => ${({ isAccounts }) =>
isAccounts isAccounts
? css` ? css`
padding: 0px 3px 0 20px; padding: 80px 3px 0 20px;
@media ${hugeMobile} { @media ${hugeMobile} {
padding: 0px 8px 0 16px; padding: 80px 8px 0 16px;
} }
` `
: css` : css`

View File

@ -28,7 +28,6 @@ const StyledNoItemContainer = styled.div`
} }
.no-accounts { .no-accounts {
padding-top: 80px;
} }
`; `;

View File

@ -5,11 +5,11 @@ import { ReactSVG } from "react-svg";
import { Text } from "@docspace/components"; import { Text } from "@docspace/components";
import { StyledTitle } from "../../styles/common"; import { StyledTitle } from "../../styles/common";
const GalleryItemTitle = ({ selection, getIcon }) => { const GalleryItemTitle = ({ gallerySelected, getIcon }) => {
return ( return (
<StyledTitle> <StyledTitle>
<ReactSVG className="icon" src={getIcon(32, ".docxf")} /> <ReactSVG className="icon" src={getIcon(32, ".docxf")} />
<Text className="text">{selection?.attributes?.name_form}</Text> <Text className="text">{gallerySelected?.attributes?.name_form}</Text>
</StyledTitle> </StyledTitle>
); );
}; };

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