Merge branch 'feature/public-room' of github.com:ONLYOFFICE/DocSpace into feature/public-room
This commit is contained in:
commit
bda0d671e9
@ -305,15 +305,6 @@ COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Web.Ap
|
||||
|
||||
CMD ["ASC.Web.Api.dll", "ASC.Web.Api"]
|
||||
|
||||
## ASC.Webhooks.Service ##
|
||||
# FROM dotnetrun AS webhooks-service
|
||||
# WORKDIR ${BUILD_PATH}/services/ASC.Webhooks.Service/
|
||||
|
||||
# COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py
|
||||
# COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Webhooks.Service/service/ .
|
||||
|
||||
# CMD ["ASC.Webhooks.Service.dll", "ASC.Webhooks.Service"]
|
||||
|
||||
## ASC.Web.Studio ##
|
||||
FROM dotnetrun AS studio
|
||||
WORKDIR ${BUILD_PATH}/studio/ASC.Web.Studio/
|
||||
|
@ -113,13 +113,6 @@ services:
|
||||
target: ssoauth
|
||||
image: "${REPO}/${DOCKER_IMAGE_PREFIX}-ssoauth:${DOCKER_TAG}"
|
||||
|
||||
# onlyoffice-webhooks-service:
|
||||
# build:
|
||||
# context: ./
|
||||
# dockerfile: "${DOCKERFILE}"
|
||||
# target: webhooks-service
|
||||
# image: "${REPO}/${DOCKER_IMAGE_PREFIX}-webhooks-service:${DOCKER_TAG}"
|
||||
|
||||
onlyoffice-proxy:
|
||||
build:
|
||||
context: ./
|
||||
|
@ -139,11 +139,6 @@ services:
|
||||
- ${SERVICE_PORT}
|
||||
- "9834"
|
||||
|
||||
# onlyoffice-webhooks-service:
|
||||
# <<: *x-service-base
|
||||
# image: "${REPO}/${DOCKER_IMAGE_PREFIX}-webhooks-service:${DOCKER_TAG}"
|
||||
# container_name: ${WEBHOOKS_SERVICE_HOST}
|
||||
|
||||
onlyoffice-proxy:
|
||||
image: "${REPO}/${DOCKER_IMAGE_PREFIX}-proxy:${DOCKER_TAG}"
|
||||
container_name: ${PROXY_HOST}
|
||||
@ -159,7 +154,6 @@ services:
|
||||
- onlyoffice-backup
|
||||
# - onlyoffice-clear-events
|
||||
# - onlyoffice-migration
|
||||
# - webhooks-service
|
||||
- onlyoffice-files
|
||||
- onlyoffice-files-services
|
||||
- onlyoffice-people-server
|
||||
@ -177,7 +171,6 @@ services:
|
||||
- SERVICE_FILES_SERVICES=${SERVICE_FILES_SERVICES}
|
||||
# - SERVICE_CLEAR_EVENTS=${SERVICE_CLEAR_EVENTS}
|
||||
# - SERVICE_MIGRATION=${SERVICE_MIGRATION}
|
||||
# - SERVICE_WEBHOOKS_SERVICE=${SERVICE_WEBHOOKS_SERVICE}
|
||||
- SERVICE_NOTIFY=${SERVICE_NOTIFY}
|
||||
- SERVICE_PEOPLE_SERVER=${SERVICE_PEOPLE_SERVER}
|
||||
- SERVICE_SOCKET=${SERVICE_SOCKET}
|
||||
|
@ -47,6 +47,7 @@ public class EmployeeFullDto : EmployeeDto
|
||||
public string AvatarMedium { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
public bool IsAdmin { get; set; }
|
||||
public bool IsRoomAdmin { get; set; }
|
||||
public bool IsLDAP { get; set; }
|
||||
public List<string> ListAdminModules { get; set; }
|
||||
public bool IsOwner { get; set; }
|
||||
@ -182,6 +183,8 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper
|
||||
|
||||
public async Task<EmployeeFullDto> GetFullAsync(UserInfo userInfo)
|
||||
{
|
||||
var currentType = await _userManager.GetUserTypeAsync(userInfo.Id);
|
||||
|
||||
var result = new EmployeeFullDto
|
||||
{
|
||||
UserName = userInfo.UserName,
|
||||
@ -194,9 +197,10 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper
|
||||
WorkFrom = _apiDateTimeHelper.Get(userInfo.WorkFromDate),
|
||||
Email = userInfo.Email,
|
||||
IsVisitor = await _userManager.IsUserAsync(userInfo),
|
||||
IsAdmin = await _userManager.IsDocSpaceAdminAsync(userInfo),
|
||||
IsAdmin = currentType is EmployeeType.DocSpaceAdmin,
|
||||
IsRoomAdmin = currentType is EmployeeType.RoomAdmin,
|
||||
IsOwner = userInfo.IsOwner(_context.Tenant),
|
||||
IsCollaborator = await _userManager.IsCollaboratorAsync(userInfo),
|
||||
IsCollaborator = currentType is EmployeeType.Collaborator,
|
||||
IsLDAP = userInfo.IsLDAP(),
|
||||
IsSSO = userInfo.IsSSO()
|
||||
};
|
||||
|
@ -29,7 +29,9 @@ using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
|
||||
namespace System.Web;
|
||||
|
||||
public static class HttpRequestExtensions
|
||||
{
|
||||
{
|
||||
public static readonly string RequestTokenHeader = "Request-Token";
|
||||
|
||||
public static Uri Url(this HttpRequest request)
|
||||
{
|
||||
return request != null ? new Uri(request.GetDisplayUrl()) : null;
|
||||
|
@ -187,6 +187,7 @@ public class CachedUserService : IUserService, ICachedService
|
||||
EmployeeStatus? employeeStatus,
|
||||
List<List<Guid>> includeGroups,
|
||||
List<Guid> excludeGroups,
|
||||
List<Tuple<List<List<Guid>>, List<Guid>>> combinedGroups,
|
||||
EmployeeActivationStatus? activationStatus,
|
||||
AccountLoginType? accountLoginType,
|
||||
string text,
|
||||
@ -197,7 +198,7 @@ public class CachedUserService : IUserService, ICachedService
|
||||
out int total,
|
||||
out int count)
|
||||
{
|
||||
return Service.GetUsers(tenant, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, accountLoginType, text, sortBy, sortOrderAsc, limit, offset, out total, out count);
|
||||
return Service.GetUsers(tenant, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, combinedGroups, activationStatus, accountLoginType, text, sortBy, sortOrderAsc, limit, offset, out total, out count);
|
||||
}
|
||||
|
||||
public async Task<UserInfo> GetUserAsync(int tenant, Guid id)
|
||||
|
@ -186,6 +186,7 @@ public class UserManager
|
||||
EmployeeStatus? employeeStatus,
|
||||
List<List<Guid>> includeGroups,
|
||||
List<Guid> excludeGroups,
|
||||
List<Tuple<List<List<Guid>>, List<Guid>>> combinedGroups,
|
||||
EmployeeActivationStatus? activationStatus,
|
||||
AccountLoginType? accountLoginType,
|
||||
string text,
|
||||
@ -196,7 +197,7 @@ public class UserManager
|
||||
out int total,
|
||||
out int count)
|
||||
{
|
||||
return _userService.GetUsers(Tenant.Id, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, accountLoginType, text, sortBy, sortOrderAsc, limit, offset, out total, out count);
|
||||
return _userService.GetUsers(Tenant.Id, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, combinedGroups, activationStatus, accountLoginType, text, sortBy, sortOrderAsc, limit, offset, out total, out count);
|
||||
}
|
||||
|
||||
public async Task<string[]> GetUserNamesAsync(EmployeeStatus status)
|
||||
|
@ -34,6 +34,7 @@ public interface IUserService
|
||||
EmployeeStatus? employeeStatus,
|
||||
List<List<Guid>> includeGroups,
|
||||
List<Guid> excludeGroups,
|
||||
List<Tuple<List<List<Guid>>, List<Guid>>> combinedGroups,
|
||||
EmployeeActivationStatus? activationStatus,
|
||||
AccountLoginType? accountLoginType,
|
||||
string text,
|
||||
|
@ -232,16 +232,31 @@ public class EFUserService : IUserService
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public IQueryable<UserInfo> GetUsers(int tenant, bool isDocSpaceAdmin, EmployeeStatus? employeeStatus, List<List<Guid>> includeGroups, List<Guid> excludeGroups, EmployeeActivationStatus? activationStatus, AccountLoginType? accountLoginType, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total, out int count)
|
||||
public IQueryable<UserInfo> GetUsers(
|
||||
int tenant,
|
||||
bool isDocSpaceAdmin,
|
||||
EmployeeStatus? employeeStatus,
|
||||
List<List<Guid>> includeGroups,
|
||||
List<Guid> excludeGroups,
|
||||
List<Tuple<List<List<Guid>>, List<Guid>>> combinedGroups,
|
||||
EmployeeActivationStatus? activationStatus,
|
||||
AccountLoginType? accountLoginType,
|
||||
string text,
|
||||
string sortBy,
|
||||
bool sortOrderAsc,
|
||||
long limit,
|
||||
long offset,
|
||||
out int total,
|
||||
out int count)
|
||||
{
|
||||
using var userDbContext = _dbContextFactory.CreateDbContext();
|
||||
var userDbContext = _dbContextFactory.CreateDbContext();
|
||||
var totalQuery = GetUserQuery(userDbContext, tenant);
|
||||
totalQuery = GetUserQueryForFilter(userDbContext, totalQuery, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, accountLoginType, text);
|
||||
totalQuery = GetUserQueryForFilter(userDbContext, totalQuery, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, combinedGroups, activationStatus, accountLoginType, text);
|
||||
total = totalQuery.Count();
|
||||
|
||||
var q = GetUserQuery(userDbContext, tenant);
|
||||
|
||||
q = GetUserQueryForFilter(userDbContext, q, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, accountLoginType, text);
|
||||
q = GetUserQueryForFilter(userDbContext, q, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, combinedGroups, activationStatus, accountLoginType, text);
|
||||
|
||||
var orderedQuery = q.OrderBy(r => r.ActivationStatus == EmployeeActivationStatus.Pending);
|
||||
q = orderedQuery;
|
||||
@ -596,26 +611,61 @@ public class EFUserService : IUserService
|
||||
EmployeeStatus? employeeStatus,
|
||||
List<List<Guid>> includeGroups,
|
||||
List<Guid> excludeGroups,
|
||||
List<Tuple<List<List<Guid>>, List<Guid>>> combinedGroups,
|
||||
EmployeeActivationStatus? activationStatus,
|
||||
AccountLoginType? accountLoginType,
|
||||
string text)
|
||||
{
|
||||
q = q.Where(r => !r.Removed);
|
||||
|
||||
if (includeGroups != null && includeGroups.Count > 0)
|
||||
if (includeGroups != null && includeGroups.Count > 0 || excludeGroups != null && excludeGroups.Count > 0)
|
||||
{
|
||||
foreach (var ig in includeGroups)
|
||||
if (includeGroups != null && includeGroups.Count > 0)
|
||||
{
|
||||
q = q.Where(r => userDbContext.UserGroups.Any(a => !a.Removed && a.TenantId == r.TenantId && a.Userid == r.Id && ig.Any(r => r == a.UserGroupId)));
|
||||
foreach (var ig in includeGroups)
|
||||
{
|
||||
q = q.Where(r => userDbContext.UserGroups.Any(a => !a.Removed && a.TenantId == r.TenantId && a.Userid == r.Id && ig.Any(r => r == a.UserGroupId)));
|
||||
}
|
||||
}
|
||||
|
||||
if (excludeGroups != null && excludeGroups.Count > 0)
|
||||
{
|
||||
foreach (var eg in excludeGroups)
|
||||
{
|
||||
q = q.Where(r => !userDbContext.UserGroups.Any(a => !a.Removed && a.TenantId == r.TenantId && a.Userid == r.Id && a.UserGroupId == eg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (excludeGroups != null && excludeGroups.Count > 0)
|
||||
else if (combinedGroups != null && combinedGroups.Any())
|
||||
{
|
||||
foreach (var eg in excludeGroups)
|
||||
Expression<Func<User, bool>> a = r => false;
|
||||
|
||||
foreach (var cg in combinedGroups)
|
||||
{
|
||||
q = q.Where(r => !userDbContext.UserGroups.Any(a => !a.Removed && a.TenantId == r.TenantId && a.Userid == r.Id && a.UserGroupId == eg));
|
||||
Expression<Func<User, bool>> b = r => true;
|
||||
|
||||
var cgIncludeGroups = cg.Item1;
|
||||
var cgExcludeGroups = cg.Item2;
|
||||
if (cgIncludeGroups != null && cgIncludeGroups.Count > 0)
|
||||
{
|
||||
foreach (var ig in cgIncludeGroups)
|
||||
{
|
||||
b = b.And(r => userDbContext.UserGroups.Any(a => !a.Removed && a.TenantId == r.TenantId && a.Userid == r.Id && ig.Any(r => r == a.UserGroupId)));
|
||||
}
|
||||
}
|
||||
|
||||
if (cgExcludeGroups != null && cgExcludeGroups.Count > 0)
|
||||
{
|
||||
foreach (var eg in cgExcludeGroups)
|
||||
{
|
||||
b = b.And(r => !userDbContext.UserGroups.Any(a => !a.Removed && a.TenantId == r.TenantId && a.Userid == r.Id && a.UserGroupId == eg));
|
||||
}
|
||||
}
|
||||
|
||||
a = a.Or(b);
|
||||
}
|
||||
|
||||
q = q.Where(a);
|
||||
}
|
||||
|
||||
if (!isDocSpaceAdmin && employeeStatus == null)
|
||||
|
66
common/ASC.Core.Common/EF/PredicateBuilder.cs
Normal file
66
common/ASC.Core.Common/EF/PredicateBuilder.cs
Normal file
@ -0,0 +1,66 @@
|
||||
// (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.Core.Common.EF;
|
||||
public static class PredicateBuilder
|
||||
{
|
||||
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b)
|
||||
{
|
||||
|
||||
var p = a.Parameters[0];
|
||||
|
||||
var visitor = new SubstExpressionVisitor();
|
||||
visitor.Subst[b.Parameters[0]] = p;
|
||||
|
||||
Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body));
|
||||
return Expression.Lambda<Func<T, bool>>(body, p);
|
||||
}
|
||||
|
||||
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b)
|
||||
{
|
||||
var p = a.Parameters[0];
|
||||
|
||||
var visitor = new SubstExpressionVisitor();
|
||||
visitor.Subst[b.Parameters[0]] = p;
|
||||
|
||||
Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
|
||||
return Expression.Lambda<Func<T, bool>>(body, p);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SubstExpressionVisitor : ExpressionVisitor
|
||||
{
|
||||
internal Dictionary<Expression, Expression> Subst = new Dictionary<Expression, Expression>();
|
||||
|
||||
protected override Expression VisitParameter(ParameterExpression node)
|
||||
{
|
||||
if (Subst.TryGetValue(node, out var newValue))
|
||||
{
|
||||
return newValue;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
@ -203,9 +203,9 @@ public enum MessageAction
|
||||
RoomUpdateAccessForUser = 5075,
|
||||
RoomRemoveUser = 5084,
|
||||
RoomCreateUser = 5085,// last
|
||||
RoomLinkUpdated = 5082,
|
||||
RoomLinkCreated = 5086,
|
||||
RoomLinkDeleted = 5087,
|
||||
RoomInvitationLinkUpdated = 5082,
|
||||
RoomInvitationLinkCreated = 5086,
|
||||
RoomInvitationLinkDeleted = 5087,
|
||||
|
||||
TagCreated = 5076,
|
||||
TagsDeleted = 5077,
|
||||
@ -214,6 +214,10 @@ public enum MessageAction
|
||||
|
||||
RoomLogoCreated = 5080,
|
||||
RoomLogoDeleted = 5081,
|
||||
|
||||
ExternalLinkCreated = 5088,
|
||||
ExternalLinkUpdated = 5089,
|
||||
ExternalLinkDeleted = 5090,
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -33,4 +33,5 @@ public static class Constants
|
||||
public const string QueryAuth = "auth";
|
||||
public const string QueryExpire = "expire";
|
||||
public const string QueryHeader = "headers";
|
||||
public const string SecureKeyHeader = "secure-key";
|
||||
}
|
||||
|
@ -111,6 +111,11 @@ public class S3Storage : BaseStorage
|
||||
|
||||
foreach (var h in headers)
|
||||
{
|
||||
if (h.StartsWith(Constants.SecureKeyHeader))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (h.StartsWith("Content-Disposition"))
|
||||
{
|
||||
headersOverrides.ContentDisposition = (h.Substring("Content-Disposition".Length + 1));
|
||||
|
@ -41,4 +41,39 @@ public static class SecureHelper
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GenerateSecureKeyHeader(string path, EmailValidationKeyProvider keyProvider)
|
||||
{
|
||||
var ticks = DateTime.UtcNow.Ticks;
|
||||
var data = path.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar) + '.' + ticks;
|
||||
var key = keyProvider.GetEmailKey(data);
|
||||
|
||||
return Constants.SecureKeyHeader + ':' + ticks + '-' + key;
|
||||
}
|
||||
|
||||
public static async Task<bool> CheckSecureKeyHeader(string queryHeaders, string path, EmailValidationKeyProvider keyProvider)
|
||||
{
|
||||
if (string.IsNullOrEmpty(queryHeaders))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var headers = queryHeaders.Length > 0 ? queryHeaders.Split('&').Select(HttpUtility.UrlDecode) : Array.Empty<string>();
|
||||
|
||||
var headerKey = headers.FirstOrDefault(h => h.StartsWith(Constants.SecureKeyHeader))?.
|
||||
Replace(Constants.SecureKeyHeader + ':', string.Empty);
|
||||
|
||||
if (string.IsNullOrEmpty(headerKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var separatorPosition = headerKey.IndexOf('-');
|
||||
var ticks = headerKey[..separatorPosition];
|
||||
var key = headerKey[(separatorPosition + 1)..];
|
||||
|
||||
var result = await keyProvider.ValidateEmailKeyAsync(path + '.' + ticks, key);
|
||||
|
||||
return result == EmailValidationKeyProvider.ValidationResult.Ok;
|
||||
}
|
||||
}
|
||||
|
@ -51,12 +51,12 @@ public class StorageHandler
|
||||
public async ValueTask InvokeAsync(HttpContext context, TenantManager tenantManager, SecurityContext securityContext, StorageFactory storageFactory, EmailValidationKeyProvider emailValidationKeyProvider)
|
||||
{
|
||||
var storage = await storageFactory.GetStorageAsync((await tenantManager.GetCurrentTenantAsync()).Id, _module);
|
||||
var path = CrossPlatform.PathCombine(_path, GetRouteValue("pathInfo", context).Replace('/', Path.DirectorySeparatorChar));
|
||||
var path = CrossPlatform.PathCombine(_path, GetRouteValue("pathInfo", context).Replace('/', Path.DirectorySeparatorChar));
|
||||
var header = context.Request.Query[Constants.QueryHeader].FirstOrDefault() ?? "";
|
||||
var auth = context.Request.Query[Constants.QueryAuth].FirstOrDefault() ?? "";
|
||||
var storageExpire = storage.GetExpire(_domain);
|
||||
|
||||
if (_checkAuth && !securityContext.IsAuthenticated && String.IsNullOrEmpty(auth))
|
||||
if (_checkAuth && !securityContext.IsAuthenticated && !await SecureHelper.CheckSecureKeyHeader(header, path, emailValidationKeyProvider))
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return;
|
||||
@ -74,31 +74,31 @@ public class StorageHandler
|
||||
if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok)
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!await storage.IsFileAsync(_domain, path))
|
||||
{
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
var headers = header.Length > 0 ? header.Split('&').Select(HttpUtility.UrlDecode) : Array.Empty<string>();
|
||||
var headers = header.Length > 0 ? header.Split('&').Select(HttpUtility.UrlDecode) : Array.Empty<string>();
|
||||
|
||||
const int bigSize = 5 * 1024 * 1024;
|
||||
var fileSize = await storage.GetFileSizeAsync(_domain, path);
|
||||
|
||||
if (storage.IsSupportInternalUri && bigSize < fileSize)
|
||||
{
|
||||
var uri = await storage.GetInternalUriAsync(_domain, path, TimeSpan.FromMinutes(15), headers);
|
||||
var uri = await storage.GetInternalUriAsync(_domain, path, TimeSpan.FromMinutes(15), headers);
|
||||
|
||||
//TODO
|
||||
//context.Response.Cache.SetAllowResponseInBrowserHistory(false);
|
||||
//context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
|
||||
|
||||
context.Response.Redirect(uri.ToString());
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
//if (!String.IsNullOrEmpty(context.Request.Query["_"]))
|
||||
@ -119,7 +119,7 @@ public class StorageHandler
|
||||
|
||||
string encoding = null;
|
||||
|
||||
if (storage is DiscDataStore && await storage.IsFileAsync(_domain, path + ".gz"))
|
||||
if (storage is DiscDataStore && await storage.IsFileAsync(_domain, path + ".gz"))
|
||||
{
|
||||
path += ".gz";
|
||||
encoding = "gzip";
|
||||
@ -162,7 +162,7 @@ public class StorageHandler
|
||||
{
|
||||
var responseBufferingFeature = context.Features.Get<IHttpResponseBodyFeature>();
|
||||
responseBufferingFeature?.DisableBuffering();
|
||||
|
||||
|
||||
await stream.CopyToAsync(context.Response.Body);
|
||||
}
|
||||
|
||||
@ -233,4 +233,4 @@ public static class StorageHandlerExtensions
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session.user) {
|
||||
if (!session.user && !session.anonymous) {
|
||||
logger.error("invalid session: unknown user");
|
||||
return;
|
||||
}
|
||||
@ -45,14 +45,18 @@
|
||||
return `${tenantId}-${roomPart}`;
|
||||
};
|
||||
|
||||
logger.info(
|
||||
`connect user='${userId}' on tenant='${tenantId}' socketId='${socket.id}'`
|
||||
);
|
||||
const connectMessage = !session.anonymous ?
|
||||
`connect user='${userId}' on tenant='${tenantId}' socketId='${socket.id}'` :
|
||||
`connect anonymous user by share key on tenant='${tenantId}' socketId='${socket.id}'`;
|
||||
|
||||
logger.info(connectMessage);
|
||||
|
||||
socket.on("disconnect", (reason) => {
|
||||
logger.info(
|
||||
`disconnect user='${userId}' on tenant='${tenantId}' socketId='${socket.id}' due to ${reason}`
|
||||
);
|
||||
const disconnectMessage = !session.anonymous ?
|
||||
`disconnect user='${userId}' on tenant='${tenantId}' socketId='${socket.id}' due to ${reason}` :
|
||||
`disconnect anonymous user by share key on tenant='${tenantId}' socketId='${socket.id}' due to ${reason}`;
|
||||
|
||||
logger.info(disconnectMessage)
|
||||
});
|
||||
|
||||
socket.on("subscribe", ({ roomParts, individual }) => {
|
||||
@ -80,7 +84,7 @@
|
||||
|
||||
changeFunc(roomParts);
|
||||
|
||||
if (individual) {
|
||||
if (individual && !session.anonymous) {
|
||||
if (Array.isArray(roomParts)) {
|
||||
changeFunc(roomParts.map((p) => `${p}-${userId}`));
|
||||
} else {
|
||||
|
@ -9,9 +9,10 @@ module.exports = (socket, next) => {
|
||||
|
||||
const cookie = req?.cookies?.authorization || req?.cookies?.asc_auth_key;
|
||||
const token = req?.headers?.authorization;
|
||||
const share = socket.handshake.query?.share;
|
||||
|
||||
if (!cookie && !token) {
|
||||
const err = new Error("Authentication error (not token or cookie)");
|
||||
if (!cookie && !token && !share) {
|
||||
const err = new Error("Authentication error (not token or cookie or share key)");
|
||||
logger.error(err);
|
||||
socket.disconnect("unauthorized");
|
||||
next(err);
|
||||
@ -31,15 +32,13 @@ module.exports = (socket, next) => {
|
||||
return;
|
||||
}
|
||||
|
||||
let headers;
|
||||
if (cookie)
|
||||
headers = {
|
||||
Authorization: cookie,
|
||||
};
|
||||
|
||||
const basePath = portalManager(req)?.replace(/\/$/g, "");
|
||||
let headers = {};
|
||||
|
||||
logger.info(`API basePath='${basePath}' Authorization='${cookie}'`);
|
||||
if (cookie) {
|
||||
headers.Authorization = cookie;
|
||||
|
||||
logger.info(`API basePath='${basePath}' Authorization='${cookie}'`);
|
||||
|
||||
const getUser = () => {
|
||||
return request({
|
||||
@ -59,17 +58,53 @@ module.exports = (socket, next) => {
|
||||
});
|
||||
};
|
||||
|
||||
return Promise.all([getUser(), getPortal()])
|
||||
.then(([user, portal]) => {
|
||||
logger.info("Get account info", { user, portal });
|
||||
session.user = user;
|
||||
session.portal = portal;
|
||||
session.save();
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error("Error of getting account info", err);
|
||||
return Promise.all([getUser(), getPortal()])
|
||||
.then(([user, portal]) => {
|
||||
logger.info("Get account info", { user, portal });
|
||||
session.user = user;
|
||||
session.portal = portal;
|
||||
session.save();
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error("Error of getting account info", err);
|
||||
socket.disconnect("Unauthorized");
|
||||
next(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (share) {
|
||||
if (req?.cookies) {
|
||||
const pairs = Object.entries(req.cookies).map(([key, value]) => `${key}=${value}`);
|
||||
|
||||
if (pairs.length > 0) {
|
||||
let cookie = pairs.join(';');
|
||||
cookie += ';';
|
||||
headers.Cookie = cookie;
|
||||
}
|
||||
}
|
||||
|
||||
return request({
|
||||
method: "get",
|
||||
url: `/files/share/${share}`,
|
||||
headers,
|
||||
basePath,
|
||||
}).then(validation => {
|
||||
if (validation.status !== 0) {
|
||||
const err = new Error("Invalid share key");
|
||||
logger.error("Share key validation failure:", err);
|
||||
next(err);
|
||||
} else {
|
||||
logger.info(`Share key validation successful: key=${share}`)
|
||||
session.anonymous = true;
|
||||
session.portal = { tenantId: validation.tenantId }
|
||||
session.save();
|
||||
next();
|
||||
}
|
||||
}).catch(err => {
|
||||
logger.error(err);
|
||||
socket.disconnect("Unauthorized");
|
||||
next(err);
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
};
|
@ -66,7 +66,8 @@ const options = {
|
||||
const token =
|
||||
req?.headers?.authorization ||
|
||||
req?.cookies?.authorization ||
|
||||
req?.cookies?.asc_auth_key;
|
||||
req?.cookies?.asc_auth_key ||
|
||||
req?._query?.share;
|
||||
|
||||
if (!token) {
|
||||
winston.info(`not allowed request: empty token`);
|
||||
|
@ -90,18 +90,28 @@ public class DbWorker
|
||||
return toAdd;
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<WebhooksConfigWithStatus> GetTenantWebhooksWithStatus()
|
||||
public async IAsyncEnumerable<WebhooksConfigWithStatus> GetTenantWebhooksWithStatus()
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
return Queries.WebhooksConfigWithStatusAsync(webhooksDbContext, Tenant);
|
||||
var q = Queries.WebhooksConfigWithStatusAsync(webhooksDbContext, Tenant);
|
||||
|
||||
await foreach (var webhook in q)
|
||||
{
|
||||
yield return webhook;
|
||||
}
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<WebhooksConfig> GetWebhookConfigs()
|
||||
public async IAsyncEnumerable<WebhooksConfig> GetWebhookConfigs()
|
||||
{
|
||||
var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
return Queries.WebhooksConfigsAsync(webhooksDbContext, Tenant);
|
||||
var q = Queries.WebhooksConfigsAsync(webhooksDbContext, Tenant);
|
||||
|
||||
await foreach (var webhook in q)
|
||||
{
|
||||
yield return webhook;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<WebhooksConfig> UpdateWebhookConfig(int id, string name, string uri, string key, bool? enabled, bool? ssl)
|
||||
|
@ -20,6 +20,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\ASC.Core.Common\ASC.Core.Common.csproj" />
|
||||
<ProjectReference Include="..\..\ASC.MessagingSystem\ASC.MessagingSystem.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -154,9 +154,9 @@ internal class RoomsActionMapper : IModuleActionMapper
|
||||
MessageAction.RoomCreateUser,
|
||||
MessageAction.RoomUpdateAccessForUser,
|
||||
MessageAction.RoomRemoveUser,
|
||||
MessageAction.RoomLinkCreated,
|
||||
MessageAction.RoomLinkUpdated,
|
||||
MessageAction.RoomLinkDeleted
|
||||
MessageAction.RoomInvitationLinkCreated,
|
||||
MessageAction.RoomInvitationLinkUpdated,
|
||||
MessageAction.RoomInvitationLinkDeleted
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -24,6 +24,8 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using ASC.Core.Common.EF;
|
||||
|
||||
namespace ASC.AuditTrail.Repositories;
|
||||
|
||||
[Scope(Additional = typeof(AuditEventsRepositoryExtensions))]
|
||||
@ -57,20 +59,20 @@ public class AuditEventsRepository
|
||||
DateTime? from = null,
|
||||
DateTime? to = null,
|
||||
int startIndex = 0,
|
||||
int limit = 0,
|
||||
int limit = 0,
|
||||
Guid? withoutUserId = null)
|
||||
{
|
||||
return await GetByFilterWithActionsAsync(
|
||||
userId,
|
||||
productType,
|
||||
productType,
|
||||
moduleType,
|
||||
actionType,
|
||||
new List<MessageAction?> { action },
|
||||
entry,
|
||||
target,
|
||||
from,
|
||||
target,
|
||||
from,
|
||||
to,
|
||||
startIndex,
|
||||
startIndex,
|
||||
limit,
|
||||
withoutUserId);
|
||||
}
|
||||
@ -108,7 +110,7 @@ public class AuditEventsRepository
|
||||
if (userId.HasValue && userId.Value != Guid.Empty)
|
||||
{
|
||||
query = query.Where(r => r.Event.UserId == userId.Value);
|
||||
}
|
||||
}
|
||||
else if (withoutUserId.HasValue && withoutUserId.Value != Guid.Empty)
|
||||
{
|
||||
query = query.Where(r => r.Event.UserId != withoutUserId.Value);
|
||||
@ -228,34 +230,6 @@ public class AuditEventsRepository
|
||||
}
|
||||
}
|
||||
|
||||
internal static class PredicateBuilder
|
||||
{
|
||||
internal static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b)
|
||||
{
|
||||
var p = a.Parameters[0];
|
||||
|
||||
var visitor = new SubstExpressionVisitor();
|
||||
visitor.Subst[b.Parameters[0]] = p;
|
||||
|
||||
Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
|
||||
return Expression.Lambda<Func<T, bool>>(body, p);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SubstExpressionVisitor : ExpressionVisitor
|
||||
{
|
||||
internal Dictionary<Expression, Expression> Subst = new Dictionary<Expression, Expression>();
|
||||
|
||||
protected override Expression VisitParameter(ParameterExpression node)
|
||||
{
|
||||
if (Subst.TryGetValue(node, out var newValue))
|
||||
{
|
||||
return newValue;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AuditEventsRepositoryExtensions
|
||||
{
|
||||
public static void Register(DIHelper services)
|
||||
|
@ -248,23 +248,43 @@ const RootFolderContainer = (props) => {
|
||||
<span>
|
||||
<div className="empty-folder_container-links">
|
||||
<StyledPlusIcon
|
||||
className="empty-folder_container-image"
|
||||
className="plus-document empty-folder_container-image"
|
||||
data-format="docx"
|
||||
onClick={onCreate}
|
||||
alt="plus_icon"
|
||||
/>
|
||||
|
||||
<Box className="flex-wrapper_container">
|
||||
<Link data-format="docx" onClick={onCreate} {...linkStyles}>
|
||||
<Link
|
||||
id="document"
|
||||
data-format="docx"
|
||||
onClick={onCreate}
|
||||
{...linkStyles}
|
||||
>
|
||||
{t("Document")},
|
||||
</Link>
|
||||
<Link data-format="xlsx" onClick={onCreate} {...linkStyles}>
|
||||
<Link
|
||||
id="spreadsheet"
|
||||
data-format="xlsx"
|
||||
onClick={onCreate}
|
||||
{...linkStyles}
|
||||
>
|
||||
{t("Spreadsheet")},
|
||||
</Link>
|
||||
<Link data-format="pptx" onClick={onCreate} {...linkStyles}>
|
||||
<Link
|
||||
id="presentation"
|
||||
data-format="pptx"
|
||||
onClick={onCreate}
|
||||
{...linkStyles}
|
||||
>
|
||||
{t("Presentation")},
|
||||
</Link>
|
||||
<Link data-format="docxf" onClick={onCreate} {...linkStyles}>
|
||||
<Link
|
||||
id="form-template"
|
||||
data-format="docxf"
|
||||
onClick={onCreate}
|
||||
{...linkStyles}
|
||||
>
|
||||
{t("Translations:NewForm")}
|
||||
</Link>
|
||||
</Box>
|
||||
@ -272,11 +292,11 @@ const RootFolderContainer = (props) => {
|
||||
|
||||
<div className="empty-folder_container-links">
|
||||
<StyledPlusIcon
|
||||
className="empty-folder_container-image"
|
||||
className="plus-folder empty-folder_container-image"
|
||||
onClick={onCreate}
|
||||
alt="plus_icon"
|
||||
/>
|
||||
<Link {...linkStyles} onClick={onCreate}>
|
||||
<Link id="folder" {...linkStyles} onClick={onCreate}>
|
||||
{t("Folder")}
|
||||
</Link>
|
||||
</div>
|
||||
|
@ -132,6 +132,7 @@ const Dialog = ({
|
||||
{isCreateDialog && extension && (
|
||||
<Box displayProp="flex" alignItems="center" paddingProp="16px 0 0">
|
||||
<Checkbox
|
||||
className="dont-ask-again"
|
||||
label={t("Common:DontAskAgain")}
|
||||
isChecked={isChecked}
|
||||
onChange={onChangeCheckbox}
|
||||
@ -150,6 +151,7 @@ const Dialog = ({
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="submit"
|
||||
key="GlobalSendBtn"
|
||||
label={isCreateDialog ? t("Common:Create") : t("Common:SaveButton")}
|
||||
size="normal"
|
||||
@ -160,6 +162,7 @@ const Dialog = ({
|
||||
onClick={onSaveAction}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
key="CloseBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
|
@ -36,7 +36,8 @@ const AvatarEditorDialog = (props) => {
|
||||
"CreateEditRoomDialog",
|
||||
]);
|
||||
|
||||
const { visible, onClose, profile, updateCreatedAvatar, setHasAvatar } = props;
|
||||
const { visible, onClose, profile, updateCreatedAvatar, setHasAvatar } =
|
||||
props;
|
||||
const [avatar, setAvatar] = useState({
|
||||
uploadedFile: profile.hasAvatar ? profile.avatarMax : DefaultUserAvatarMax,
|
||||
x: 0.5,
|
||||
@ -54,7 +55,7 @@ const AvatarEditorDialog = (props) => {
|
||||
if (!avatar.uploadedFile) {
|
||||
const res = await deleteAvatar(profile.id);
|
||||
updateCreatedAvatar(res);
|
||||
setHasAvatar(false)
|
||||
setHasAvatar(false);
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
@ -70,7 +71,7 @@ const AvatarEditorDialog = (props) => {
|
||||
|
||||
if (res.success) {
|
||||
res.data && updateCreatedAvatar(res.data);
|
||||
setHasAvatar(true)
|
||||
setHasAvatar(true);
|
||||
toastr.success(t("Common:ChangesSavedSuccessfully"));
|
||||
} else {
|
||||
throw new Error(t("Common:ErrorInternalServer"));
|
||||
@ -113,6 +114,7 @@ const AvatarEditorDialog = (props) => {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="save"
|
||||
key="AvatarEditorSaveBtn"
|
||||
label={t("Common:SaveButton")}
|
||||
size="normal"
|
||||
@ -122,6 +124,7 @@ const AvatarEditorDialog = (props) => {
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
key="AvatarEditorCloseBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
@ -136,7 +139,11 @@ const AvatarEditorDialog = (props) => {
|
||||
export default inject(({ peopleStore }) => {
|
||||
const { targetUserStore } = peopleStore;
|
||||
|
||||
const { targetUser: profile, updateCreatedAvatar, setHasAvatar } = targetUserStore;
|
||||
const {
|
||||
targetUser: profile,
|
||||
updateCreatedAvatar,
|
||||
setHasAvatar,
|
||||
} = targetUserStore;
|
||||
|
||||
return {
|
||||
profile,
|
||||
|
@ -175,6 +175,7 @@ class ChangeEmailDialogComponent extends React.Component {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="send"
|
||||
key="ChangeEmailSendBtn"
|
||||
label={t("Common:SendButton")}
|
||||
size="normal"
|
||||
@ -184,6 +185,7 @@ class ChangeEmailDialogComponent extends React.Component {
|
||||
isLoading={isRequestRunning}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
key="CloseBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
|
@ -74,6 +74,7 @@ const ChangeNameDialog = (props) => {
|
||||
className="field"
|
||||
>
|
||||
<TextInput
|
||||
className="first-name"
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
value={firstName}
|
||||
@ -91,6 +92,7 @@ const ChangeNameDialog = (props) => {
|
||||
className="field"
|
||||
>
|
||||
<TextInput
|
||||
className="last-name"
|
||||
scale={true}
|
||||
value={lastName}
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
@ -103,6 +105,7 @@ const ChangeNameDialog = (props) => {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="save"
|
||||
key="ChangeNameSaveBtn"
|
||||
label={t("Common:SaveButton")}
|
||||
size="normal"
|
||||
@ -113,6 +116,7 @@ const ChangeNameDialog = (props) => {
|
||||
tabIndex={3}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
key="CloseBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
|
@ -57,14 +57,8 @@ class ChangePasswordDialogComponent extends React.Component {
|
||||
|
||||
render() {
|
||||
// console.log("ChangePasswordDialog render");
|
||||
const {
|
||||
t,
|
||||
tReady,
|
||||
visible,
|
||||
email,
|
||||
onClose,
|
||||
currentColorScheme,
|
||||
} = this.props;
|
||||
const { t, tReady, visible, email, onClose, currentColorScheme } =
|
||||
this.props;
|
||||
const { isRequestRunning } = this.state;
|
||||
|
||||
return (
|
||||
@ -84,6 +78,7 @@ class ChangePasswordDialogComponent extends React.Component {
|
||||
>
|
||||
Send the password change instructions to the
|
||||
<Link
|
||||
className="email-link"
|
||||
type="page"
|
||||
href={`mailto:${email}`}
|
||||
noHover
|
||||
@ -98,6 +93,7 @@ class ChangePasswordDialogComponent extends React.Component {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="send"
|
||||
key="ChangePasswordSendBtn"
|
||||
label={t("Common:SendButton")}
|
||||
size="normal"
|
||||
@ -107,6 +103,7 @@ class ChangePasswordDialogComponent extends React.Component {
|
||||
isLoading={isRequestRunning}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
key="CloseBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
|
@ -87,6 +87,7 @@ const ChangePricingPlanDialog = ({
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="ok-button"
|
||||
label={t("Common:OKButton")}
|
||||
size="normal"
|
||||
primary={true}
|
||||
|
@ -273,6 +273,7 @@ const PureConnectDialogContainer = (props) => {
|
||||
errorMessage={t("Common:RequiredField")}
|
||||
>
|
||||
<TextInput
|
||||
id="connection-url-input"
|
||||
isAutoFocussed={true}
|
||||
hasError={!isUrlValid}
|
||||
isDisabled={isLoading}
|
||||
@ -292,6 +293,7 @@ const PureConnectDialogContainer = (props) => {
|
||||
errorMessage={t("Common:RequiredField")}
|
||||
>
|
||||
<TextInput
|
||||
id="login-input"
|
||||
isAutoFocussed={!showUrlField}
|
||||
hasError={!isLoginValid}
|
||||
isDisabled={isLoading}
|
||||
@ -310,6 +312,7 @@ const PureConnectDialogContainer = (props) => {
|
||||
style={roomCreation ? { margin: "0" } : {}}
|
||||
>
|
||||
<PasswordInput
|
||||
id="password-input"
|
||||
hasError={!isPasswordValid}
|
||||
isDisabled={isLoading}
|
||||
tabIndex={3}
|
||||
@ -342,6 +345,7 @@ const PureConnectDialogContainer = (props) => {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
id="save"
|
||||
tabIndex={5}
|
||||
label={t("Common:SaveButton")}
|
||||
size="normal"
|
||||
@ -352,6 +356,7 @@ const PureConnectDialogContainer = (props) => {
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<Button
|
||||
id="cancel"
|
||||
tabIndex={5}
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
|
@ -29,8 +29,6 @@ export const getRoomTypeDescriptionTranslation = (roomType = 1, t) => {
|
||||
return t("CreateEditRoomDialog:ViewOnlyRoomDescription");
|
||||
case RoomsType.CustomRoom:
|
||||
return t("CreateEditRoomDialog:CustomRoomDescription");
|
||||
case RoomsType.PublicRoom:
|
||||
return t("CreateEditRoomDialog:PublicRoomDescription");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -38,6 +38,7 @@ const DeletePortalDialog = (props) => {
|
||||
Before you delete the portal, please make sure that automatic billing
|
||||
is turned off. You may check the status of automatic billing in
|
||||
<ColorTheme
|
||||
className="stripe-url-link"
|
||||
tag="a"
|
||||
themeId={ThemeType.Link}
|
||||
fontSize="13px"
|
||||
@ -51,6 +52,7 @@ const DeletePortalDialog = (props) => {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="delete-button"
|
||||
key="DeletePortalBtn"
|
||||
label={t("Common:Delete")}
|
||||
size="normal"
|
||||
@ -59,6 +61,7 @@ const DeletePortalDialog = (props) => {
|
||||
onClick={onDeleteClick}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
key="CancelDeleteBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
|
@ -366,6 +366,7 @@ class DownloadDialogComponent extends React.Component {
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="DownloadButton"
|
||||
className="download-button"
|
||||
label={t("Common:Download")}
|
||||
size="normal"
|
||||
primary
|
||||
@ -375,6 +376,7 @@ class DownloadDialogComponent extends React.Component {
|
||||
/>
|
||||
<Button
|
||||
key="CancelButton"
|
||||
className="cancel-button"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
onClick={this.onClose}
|
||||
|
@ -36,12 +36,17 @@ const LogoutAllConnectionDialog = ({
|
||||
{t("Profile:DescriptionForSecurity")}
|
||||
</Text>
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<Checkbox isChecked={isChecked} onChange={onChangeCheckbox} />
|
||||
<Checkbox
|
||||
className="change-password"
|
||||
isChecked={isChecked}
|
||||
onChange={onChangeCheckbox}
|
||||
/>
|
||||
{t("Profile:ChangePasswordAfterLoggingOut")}
|
||||
</Box>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="logout"
|
||||
key="LogoutBtn"
|
||||
label={t("Profile:LogoutBtn")}
|
||||
size="normal"
|
||||
@ -53,6 +58,7 @@ const LogoutAllConnectionDialog = ({
|
||||
isLoading={loading}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
key="CloseBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
|
@ -110,6 +110,7 @@ const SalesDepartmentRequestDialog = ({
|
||||
errorMessage={t("Common:RequiredField")}
|
||||
>
|
||||
<TextInput
|
||||
id="your-name"
|
||||
hasError={!isValidName}
|
||||
name="name"
|
||||
type="text"
|
||||
@ -134,7 +135,7 @@ const SalesDepartmentRequestDialog = ({
|
||||
>
|
||||
<TextInput
|
||||
hasError={!isValidEmail}
|
||||
id="e-mail"
|
||||
id="registration-email"
|
||||
name="e-mail"
|
||||
type="text"
|
||||
size="base"
|
||||
@ -156,6 +157,7 @@ const SalesDepartmentRequestDialog = ({
|
||||
errorMessage={t("Common:RequiredField")}
|
||||
>
|
||||
<Textarea
|
||||
id="request-details"
|
||||
heightScale={false}
|
||||
hasError={!isValidDescription}
|
||||
placeholder={t("RequestDetails")}
|
||||
@ -168,6 +170,7 @@ const SalesDepartmentRequestDialog = ({
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="send-button"
|
||||
label={isLoading ? t("Common:Sending") : t("Common:SendButton")}
|
||||
size="normal"
|
||||
primary={true}
|
||||
@ -177,6 +180,7 @@ const SalesDepartmentRequestDialog = ({
|
||||
tabIndex={3}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
onClick={onCloseModal}
|
||||
|
@ -302,6 +302,7 @@ const InvitePanel = ({
|
||||
{hasInvitedUsers && (
|
||||
<StyledButtons>
|
||||
<Button
|
||||
className="send-invitation"
|
||||
scale={true}
|
||||
size={"normal"}
|
||||
isDisabled={hasErrors}
|
||||
@ -311,6 +312,7 @@ const InvitePanel = ({
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
scale={true}
|
||||
size={"normal"}
|
||||
onClick={onClose}
|
||||
@ -366,12 +368,8 @@ export default inject(({ auth, peopleStore, filesStore, dialogsStore }) => {
|
||||
setInvitePanelOptions,
|
||||
} = dialogsStore;
|
||||
|
||||
const {
|
||||
getFolderInfo,
|
||||
setRoomSecurity,
|
||||
getRoomSecurityInfo,
|
||||
folders,
|
||||
} = filesStore;
|
||||
const { getFolderInfo, setRoomSecurity, getRoomSecurityInfo, folders } =
|
||||
filesStore;
|
||||
|
||||
return {
|
||||
folders,
|
||||
|
@ -15,6 +15,7 @@ const AccessSelector = ({
|
||||
withRemove = false,
|
||||
filteredAccesses,
|
||||
setIsOpenItemAccess,
|
||||
className,
|
||||
}) => {
|
||||
const [horizontalOrientation, setHorizontalOrientation] = useState(false);
|
||||
const width = containerRef?.current?.offsetWidth - 32;
|
||||
@ -53,6 +54,7 @@ const AccessSelector = ({
|
||||
<StyledAccessSelector className="invite-panel_access-selector">
|
||||
{!(isMobileOnly && !isMobileHorizontalOrientation) && (
|
||||
<AccessRightSelect
|
||||
className={className}
|
||||
selectedOption={selectedOption}
|
||||
onSelect={onSelectAccess}
|
||||
accessOptions={filteredAccesses ? filteredAccesses : accessOptions}
|
||||
@ -70,6 +72,7 @@ const AccessSelector = ({
|
||||
|
||||
{isMobileOnly && !isMobileHorizontalOrientation && (
|
||||
<AccessRightSelect
|
||||
className={className}
|
||||
selectedOption={selectedOption}
|
||||
onSelect={onSelectAccess}
|
||||
accessOptions={filteredAccesses ? filteredAccesses : accessOptions}
|
||||
|
@ -188,6 +188,7 @@ const ExternalLinks = ({
|
||||
</div>
|
||||
)}
|
||||
<StyledToggleButton
|
||||
className="invite-via-link"
|
||||
isChecked={externalLinksVisible}
|
||||
onChange={toggleLinks}
|
||||
/>
|
||||
@ -212,6 +213,7 @@ const ExternalLinks = ({
|
||||
/>
|
||||
</StyledInviteInput>
|
||||
<AccessSelector
|
||||
className="invite-via-link-access"
|
||||
t={t}
|
||||
roomType={roomType}
|
||||
defaultAccess={activeLink.access}
|
||||
|
@ -242,6 +242,7 @@ const InviteInput = ({
|
||||
{t("AddManually")}
|
||||
{!hideSelector && (
|
||||
<StyledLink
|
||||
className="link-list"
|
||||
fontWeight="600"
|
||||
type="action"
|
||||
isHovered
|
||||
@ -260,6 +261,7 @@ const InviteInput = ({
|
||||
<StyledInviteInputContainer ref={inputsRef}>
|
||||
<StyledInviteInput ref={searchRef}>
|
||||
<TextInput
|
||||
className="invite-input"
|
||||
scale
|
||||
onChange={onChange}
|
||||
placeholder={
|
||||
@ -288,6 +290,7 @@ const InviteInput = ({
|
||||
foundUsers
|
||||
) : (
|
||||
<DropDownItem
|
||||
className="add-item"
|
||||
style={{ width: "inherit" }}
|
||||
textOverflow
|
||||
onClick={addEmail}
|
||||
@ -300,6 +303,7 @@ const InviteInput = ({
|
||||
)}
|
||||
|
||||
<AccessSelector
|
||||
className="add-manually-access"
|
||||
t={t}
|
||||
roomType={roomType}
|
||||
defaultAccess={selectedAccess}
|
||||
|
@ -130,10 +130,15 @@ const Item = ({
|
||||
size={16}
|
||||
color="#F21C0E"
|
||||
/>
|
||||
<StyledDeleteIcon size="medium" onClick={removeItem} />
|
||||
<StyledDeleteIcon
|
||||
className="delete-icon"
|
||||
size="medium"
|
||||
onClick={removeItem}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<AccessSelector
|
||||
className="user-access"
|
||||
t={t}
|
||||
roomType={roomType}
|
||||
defaultAccess={defaultAccess?.access}
|
||||
|
138
packages/client/src/components/panels/SharingPanel/linkRow.js
Normal file
138
packages/client/src/components/panels/SharingPanel/linkRow.js
Normal file
@ -0,0 +1,138 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import Row from "@docspace/components/row";
|
||||
import LinkWithDropdown from "@docspace/components/link-with-dropdown";
|
||||
import ToggleButton from "@docspace/components/toggle-button";
|
||||
import { StyledLinkRow } from "../StyledPanels";
|
||||
import AccessComboBox from "./AccessComboBox";
|
||||
import { ShareAccessRights } from "@docspace/common/constants";
|
||||
import AccessEditIcon from "PUBLIC_DIR/images/access.edit.react.svg";
|
||||
import CopyIcon from "PUBLIC_DIR/images/copy.react.svg";
|
||||
import commonIconsStyles from "@docspace/components/utils/common-icons-style";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledAccessEditIcon = styled(AccessEditIcon)`
|
||||
${commonIconsStyles}
|
||||
path {
|
||||
fill: ${(props) => props.theme.filesPanels.sharing.fill};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledAccessEditIcon.defaultProps = { theme: Base };
|
||||
|
||||
const StyledCopyIcon = styled(CopyIcon)`
|
||||
${commonIconsStyles}
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
${(props) =>
|
||||
props.isDisabled &&
|
||||
css`
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
`}
|
||||
`;
|
||||
|
||||
class LinkRow extends React.Component {
|
||||
onToggleButtonChange = () => {
|
||||
const { onToggleLink, item } = this.props;
|
||||
|
||||
onToggleLink(item);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
linkText,
|
||||
options,
|
||||
index,
|
||||
t,
|
||||
item,
|
||||
withToggle,
|
||||
externalAccessOptions,
|
||||
onChangeItemAccess,
|
||||
isLoading,
|
||||
theme,
|
||||
onCopyLink,
|
||||
} = this.props;
|
||||
|
||||
const isChecked = item.access !== ShareAccessRights.DenyAccess;
|
||||
const disableLink = withToggle ? !isChecked : false;
|
||||
const isDisabled = isLoading || disableLink;
|
||||
|
||||
return (
|
||||
<StyledLinkRow
|
||||
theme={theme}
|
||||
withToggle={withToggle}
|
||||
isDisabled={isDisabled}
|
||||
className="link-row__container"
|
||||
>
|
||||
<Row
|
||||
theme={theme}
|
||||
className="link-row"
|
||||
key={`${linkText.replace(" ", "-")}-key_${index}`}
|
||||
element={
|
||||
withToggle ? (
|
||||
<AccessComboBox
|
||||
theme={theme}
|
||||
t={t}
|
||||
access={item.access}
|
||||
directionX="left"
|
||||
accessOptions={externalAccessOptions}
|
||||
onAccessChange={onChangeItemAccess}
|
||||
itemId={item.sharedTo.id}
|
||||
isDisabled={isDisabled}
|
||||
disableLink={disableLink}
|
||||
/>
|
||||
) : (
|
||||
<StyledAccessEditIcon
|
||||
theme={theme}
|
||||
size="medium"
|
||||
className="sharing_panel-owner-icon"
|
||||
/>
|
||||
)
|
||||
}
|
||||
contextButtonSpacerWidth="0px"
|
||||
>
|
||||
<>
|
||||
<div className="sharing_panel-link-container">
|
||||
<LinkWithDropdown
|
||||
theme={theme}
|
||||
className="sharing_panel-link"
|
||||
color={theme.filesPanels.sharing.dropdownColor}
|
||||
dropdownType="alwaysDashed"
|
||||
data={options}
|
||||
fontSize="13px"
|
||||
fontWeight={600}
|
||||
isDisabled={isDisabled}
|
||||
>
|
||||
{linkText}
|
||||
</LinkWithDropdown>
|
||||
{onCopyLink && (
|
||||
<StyledCopyIcon
|
||||
theme={theme}
|
||||
isDisabled={isDisabled}
|
||||
size="medium"
|
||||
onClick={onCopyLink}
|
||||
title={t("CopyExternalLink")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{withToggle && (
|
||||
<div>
|
||||
<ToggleButton
|
||||
theme={theme}
|
||||
isChecked={isChecked}
|
||||
onChange={this.onToggleButtonChange}
|
||||
isDisabled={isLoading}
|
||||
className="sharing-row__toggle-button"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</Row>
|
||||
</StyledLinkRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default LinkRow;
|
@ -196,7 +196,7 @@ export const getCategoryUrl = (categoryType, folderId = null) => {
|
||||
return "/accounts/filter";
|
||||
|
||||
case CategoryType.Settings:
|
||||
return "/settings/common";
|
||||
return "/settings/personal";
|
||||
|
||||
default:
|
||||
throw new Error("Unknown category type");
|
||||
|
@ -144,7 +144,10 @@ const AboutContent = (props) => {
|
||||
</ColorTheme>
|
||||
|
||||
<Text className="row-el select-el" fontSize="13px" fontWeight="600">
|
||||
v.{buildVersionInfo.docspace}
|
||||
v.
|
||||
<span className="version-document-management">
|
||||
{buildVersionInfo.docspace}
|
||||
</span>
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
@ -166,7 +169,10 @@ const AboutContent = (props) => {
|
||||
ONLYOFFICE Docs
|
||||
</ColorTheme>
|
||||
<Text className="row-el select-el" fontSize="13px" fontWeight="600">
|
||||
v.{buildVersionInfo.documentServer}
|
||||
v.
|
||||
<span className="version-online-editors">
|
||||
{buildVersionInfo.documentServer}
|
||||
</span>
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
|
@ -53,7 +53,9 @@ const DebugInfoDialog = (props) => {
|
||||
<ModalDialog.Header>Debug Info</ModalDialog.Header>
|
||||
<ModalDialog.Body className="debug-info-body">
|
||||
{/* <Text>{`# Build version: ${BUILD_VERSION}`}</Text> */}
|
||||
<Text>{`# Version: ${VERSION}`}</Text>
|
||||
<Text>
|
||||
# Version: <span className="version">{VERSION}</span>
|
||||
</Text>
|
||||
<Text>{`# Build date: ${BUILD_AT}`}</Text>
|
||||
{user && (
|
||||
<Text>{`# Current User: ${user?.displayName} (id:${user?.id})`}</Text>
|
||||
|
@ -2,6 +2,7 @@ import React, { useState, useEffect, useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import { FolderType } from "@docspace/common/constants";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
|
||||
import PersonPlusReactSvgUrl from "PUBLIC_DIR/images/person+.react.svg?url";
|
||||
@ -195,6 +196,7 @@ const Members = ({
|
||||
/>
|
||||
)}
|
||||
</StyledUserTypeHeader>
|
||||
|
||||
<StyledUserList>
|
||||
{Object.values(members.inRoom).map((user) => (
|
||||
<User
|
||||
@ -215,6 +217,7 @@ const Members = ({
|
||||
/>
|
||||
))}
|
||||
</StyledUserList>
|
||||
|
||||
{!!members.expected.length && (
|
||||
<StyledUserTypeHeader isExpect>
|
||||
<Text className="title">{t("PendingInvitations")}</Text>
|
||||
@ -230,6 +233,7 @@ const Members = ({
|
||||
)}
|
||||
</StyledUserTypeHeader>
|
||||
)}
|
||||
|
||||
<StyledUserList>
|
||||
{Object.values(members.expected).map((user, i) => (
|
||||
<User
|
||||
|
@ -1,10 +1,13 @@
|
||||
import CrossReactSvgUrl from "PUBLIC_DIR/images/cross.react.svg?url";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { inject } from "mobx-react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { isMobile as isMobileRDD } from "react-device-detect";
|
||||
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import Text from "@docspace/components/text";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import withLoader from "@docspace/client/src/HOCs/withLoader";
|
||||
import Submenu from "@docspace/components/submenu";
|
||||
import {
|
||||
isDesktop as isDesktopUtils,
|
||||
@ -15,7 +18,7 @@ import {
|
||||
import { ColorTheme, ThemeType } from "@docspace/components/ColorTheme";
|
||||
|
||||
import { StyledInfoPanelHeader } from "./styles/common";
|
||||
import { RoomsType } from "@docspace/common/constants";
|
||||
import { FolderType } from "@docspace/common/constants";
|
||||
|
||||
const InfoPanelHeaderContent = (props) => {
|
||||
const {
|
||||
@ -64,6 +67,8 @@ const InfoPanelHeaderContent = (props) => {
|
||||
const setHistory = () => setView("info_history");
|
||||
const setDetails = () => setView("info_details");
|
||||
|
||||
//const isArchiveRoot = rootFolderType === FolderType.Archive;
|
||||
|
||||
const submenuData = [
|
||||
{
|
||||
id: "info_members",
|
||||
@ -84,6 +89,9 @@ const InfoPanelHeaderContent = (props) => {
|
||||
content: null,
|
||||
},
|
||||
];
|
||||
// const selectionRoomRights = selectionParentRoom
|
||||
// ? selectionParentRoom.security?.Read
|
||||
// : selection?.security?.Read;
|
||||
|
||||
const roomsSubmenu = [...submenuData];
|
||||
|
||||
@ -144,8 +152,12 @@ export default inject(({ auth, selectedFolderStore }) => {
|
||||
getIsGallery,
|
||||
getIsAccounts,
|
||||
getIsTrash,
|
||||
//selectionParentRoom,
|
||||
} = auth.infoPanelStore;
|
||||
const { isRootFolder, roomType } = selectedFolderStore;
|
||||
const {
|
||||
isRootFolder,
|
||||
// rootFolderType
|
||||
} = selectedFolderStore;
|
||||
|
||||
return {
|
||||
selection,
|
||||
@ -158,6 +170,14 @@ export default inject(({ auth, selectedFolderStore }) => {
|
||||
getIsGallery,
|
||||
getIsAccounts,
|
||||
getIsTrash,
|
||||
|
||||
isRootFolder,
|
||||
};
|
||||
})(withTranslation(["Common", "InfoPanel"])(InfoPanelHeaderContent));
|
||||
})(
|
||||
withTranslation(["Common", "InfoPanel"])(
|
||||
InfoPanelHeaderContent
|
||||
// withLoader(observer(InfoPanelHeaderContent))(
|
||||
// <Loaders.InfoPanelHeaderLoader />
|
||||
// )
|
||||
)
|
||||
);
|
||||
|
@ -27,7 +27,7 @@ const GeneralSettings = ({
|
||||
{t("StoringFileVersion")}
|
||||
</Heading>
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="intermediate-version toggle-btn"
|
||||
label={t("IntermediateVersion")}
|
||||
onChange={onChangeStoreForceSave}
|
||||
isChecked={storeForceSave}
|
||||
|
@ -104,21 +104,21 @@ const PersonalSettings = ({
|
||||
/>
|
||||
{!isVisitor && (
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="ask-again toggle-btn"
|
||||
label={t("Common:DontAskAgain")}
|
||||
onChange={onChangeKeepNewFileName}
|
||||
isChecked={keepNewFileName}
|
||||
/>
|
||||
)}
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="save-copy-original toggle-btn"
|
||||
label={t("OriginalCopy")}
|
||||
onChange={onChangeOriginalCopy}
|
||||
isChecked={storeOriginalFiles}
|
||||
/>
|
||||
{!isVisitor && (
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="display-notification toggle-btn"
|
||||
label={t("DisplayNotification")}
|
||||
onChange={onChangeDeleteConfirm}
|
||||
isChecked={confirmDelete}
|
||||
@ -161,7 +161,7 @@ const PersonalSettings = ({
|
||||
</Heading>
|
||||
{!isVisitor && (
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="update-or-create toggle-btn"
|
||||
label={t("UpdateOrCreate")}
|
||||
onChange={onChangeUpdateIfExist}
|
||||
isChecked={updateIfExist}
|
||||
@ -169,7 +169,7 @@ const PersonalSettings = ({
|
||||
)}
|
||||
{!isVisitor && (
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="keep-intermediate-version toggle-btn"
|
||||
label={t("KeepIntermediateVersion")}
|
||||
onChange={onChangeForceSave}
|
||||
isChecked={forceSave}
|
||||
|
@ -30,18 +30,18 @@ const SectionBodyContent = ({ isErrorSettings, user }) => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const setting = window.location.pathname.endsWith("/settings/common")
|
||||
? "common"
|
||||
: "admin";
|
||||
const setting = window.location.pathname.endsWith("/settings/personal")
|
||||
? "personal"
|
||||
: "general";
|
||||
|
||||
const commonSettings = {
|
||||
id: "common",
|
||||
id: "personal",
|
||||
name: t("Common:SettingsPersonal"),
|
||||
content: <PersonalSettings t={t} />,
|
||||
};
|
||||
|
||||
const adminSettings = {
|
||||
id: "admin",
|
||||
id: "general",
|
||||
name: t("Common:SettingsGeneral"),
|
||||
content: <GeneralSettings t={t} />,
|
||||
};
|
||||
@ -80,7 +80,7 @@ const SectionBodyContent = ({ isErrorSettings, user }) => {
|
||||
) : (
|
||||
<Submenu
|
||||
data={data}
|
||||
startSelect={setting === "common" ? commonSettings : adminSettings}
|
||||
startSelect={setting === "personal" ? commonSettings : adminSettings}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
)}
|
||||
|
@ -16,13 +16,13 @@ const SettingsView = ({
|
||||
|
||||
const inLoad = (!isLoadedSettingsTree && isLoading) || isLoading;
|
||||
|
||||
const setting = location.pathname.includes("/settings/common")
|
||||
? "common"
|
||||
: "admin";
|
||||
const setting = location.pathname.includes("/settings/general")
|
||||
? "general"
|
||||
: "personal";
|
||||
return (
|
||||
<>
|
||||
{inLoad ? (
|
||||
setting === "common" ? (
|
||||
setting === "personal" ? (
|
||||
<Loaders.SettingsCommon isAdmin={isAdmin} />
|
||||
) : (
|
||||
<Loaders.SettingsAdmin />
|
||||
|
@ -31,7 +31,7 @@ const DailyFeedContainer = ({
|
||||
<Text {...textDescriptionsProps}>{t("DailyFeedDescription")}</Text>
|
||||
</div>
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="daily-feed toggle-btn"
|
||||
onChange={onChangeEmailSubscription}
|
||||
isChecked={dailyFeedSubscriptions}
|
||||
/>
|
||||
|
@ -38,7 +38,7 @@ const RoomsActionsContainer = ({
|
||||
</Text>
|
||||
</div>
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="rooms-actions toggle-btn"
|
||||
onChange={onChangeBadgeSubscription}
|
||||
isChecked={badgesSubscription}
|
||||
/>
|
||||
|
@ -31,7 +31,7 @@ const RoomsActivityContainer = ({
|
||||
<Text {...textDescriptionsProps}>{t("RoomsActivityDescription")}</Text>
|
||||
</div>
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="rooms-activity toggle-btn"
|
||||
onChange={onChangeEmailSubscription}
|
||||
isChecked={roomsActivitySubscription}
|
||||
/>
|
||||
|
@ -30,7 +30,7 @@ const UsefulTipsContainer = ({
|
||||
<Text {...textDescriptionsProps}>{t("UsefulTipsDescription")}</Text>
|
||||
</div>
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
className="useful-tips toggle-btn"
|
||||
onChange={onChangeEmailSubscription}
|
||||
isChecked={usefulTipsSubscription}
|
||||
/>
|
||||
|
@ -56,11 +56,8 @@ const AdditionalResources = (props) => {
|
||||
const [hasChange, setHasChange] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const {
|
||||
feedbackAndSupportEnabled,
|
||||
videoGuidesEnabled,
|
||||
helpCenterEnabled,
|
||||
} = additionalSettings;
|
||||
const { feedbackAndSupportEnabled, videoGuidesEnabled, helpCenterEnabled } =
|
||||
additionalSettings;
|
||||
|
||||
const getSettings = () => {
|
||||
const additionalSettings = getFromSessionStorage("additionalSettings");
|
||||
@ -213,7 +210,7 @@ const AdditionalResources = (props) => {
|
||||
<div className="branding-checkbox">
|
||||
<Checkbox
|
||||
tabIndex={12}
|
||||
className="checkbox"
|
||||
className="show-feedback-support checkbox"
|
||||
isDisabled={!isSettingPaid}
|
||||
label={t("ShowFeedbackAndSupport")}
|
||||
isChecked={feedbackAndSupportEnabled}
|
||||
@ -222,7 +219,7 @@ const AdditionalResources = (props) => {
|
||||
|
||||
<Checkbox
|
||||
tabIndex={13}
|
||||
className="checkbox"
|
||||
className="show-video-guides checkbox"
|
||||
isDisabled={!isSettingPaid}
|
||||
label={t("ShowVideoGuides")}
|
||||
isChecked={videoGuidesEnabled}
|
||||
@ -230,7 +227,7 @@ const AdditionalResources = (props) => {
|
||||
/>
|
||||
<Checkbox
|
||||
tabIndex={14}
|
||||
className="checkbox"
|
||||
className="show-help-center checkbox"
|
||||
isDisabled={!isSettingPaid}
|
||||
label={t("ShowHelpCenter")}
|
||||
isChecked={helpCenterEnabled}
|
||||
@ -247,6 +244,8 @@ const AdditionalResources = (props) => {
|
||||
reminderTest={t("YouHaveUnsavedChanges")}
|
||||
showReminder={(isSettingPaid && hasChange) || isLoading}
|
||||
disableRestoreToDefault={additionalResourcesIsDefault || isLoading}
|
||||
additionalClassSaveButton="additional-resources-save"
|
||||
additionalClassCancelButton="additional-resources-cancel"
|
||||
/>
|
||||
</StyledComponent>
|
||||
</>
|
||||
@ -256,10 +255,8 @@ const AdditionalResources = (props) => {
|
||||
export default inject(({ auth, common }) => {
|
||||
const { settingsStore } = auth;
|
||||
|
||||
const {
|
||||
setIsLoadedAdditionalResources,
|
||||
isLoadedAdditionalResources,
|
||||
} = common;
|
||||
const { setIsLoadedAdditionalResources, isLoadedAdditionalResources } =
|
||||
common;
|
||||
|
||||
const {
|
||||
getAdditionalResources,
|
||||
|
@ -59,7 +59,9 @@ const CompanyInfoSettings = (props) => {
|
||||
};
|
||||
|
||||
const [companySettings, setCompanySettings] = useState({});
|
||||
const [companySettingsError, setCompanySettingsError] = useState(defaultCompanySettingsError);
|
||||
const [companySettingsError, setCompanySettingsError] = useState(
|
||||
defaultCompanySettingsError
|
||||
);
|
||||
const [showReminder, setShowReminder] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
@ -112,7 +114,9 @@ const CompanyInfoSettings = (props) => {
|
||||
}, [isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
const defaultCompanySettings = getFromSessionStorage("defaultCompanySettings");
|
||||
const defaultCompanySettings = getFromSessionStorage(
|
||||
"defaultCompanySettings"
|
||||
);
|
||||
|
||||
const newSettings = {
|
||||
address: companySettings.address,
|
||||
@ -190,7 +194,10 @@ const CompanyInfoSettings = (props) => {
|
||||
const companyName = e.target.value;
|
||||
validateEmpty(companyName, "companyName");
|
||||
setCompanySettings({ ...companySettings, companyName });
|
||||
saveToSessionStorage("companySettings", {...companySettings, companyName });
|
||||
saveToSessionStorage("companySettings", {
|
||||
...companySettings,
|
||||
companyName,
|
||||
});
|
||||
};
|
||||
|
||||
const onChangePhone = (e) => {
|
||||
@ -411,6 +418,8 @@ const CompanyInfoSettings = (props) => {
|
||||
displaySettings={true}
|
||||
showReminder={(isSettingPaid && showReminder) || isLoading}
|
||||
disableRestoreToDefault={companyInfoSettingsIsDefault || isLoading}
|
||||
additionalClassSaveButton="company-info-save"
|
||||
additionalClassCancelButton="company-info-cancel"
|
||||
/>
|
||||
</StyledComponent>
|
||||
</>
|
||||
|
@ -12,6 +12,7 @@ const Logo = (props) => {
|
||||
isSettingPaid,
|
||||
onChangeText,
|
||||
inputId,
|
||||
linkId,
|
||||
imageClass,
|
||||
isEditor,
|
||||
} = props;
|
||||
@ -58,6 +59,7 @@ const Logo = (props) => {
|
||||
disabled={!isSettingPaid}
|
||||
/>
|
||||
<Link
|
||||
id={linkId}
|
||||
fontWeight="600"
|
||||
isHovered
|
||||
type="action"
|
||||
|
@ -37,9 +37,8 @@ const WhiteLabel = (props) => {
|
||||
} = props;
|
||||
const [isLoadedData, setIsLoadedData] = useState(false);
|
||||
const [logoTextWhiteLabel, setLogoTextWhiteLabel] = useState("");
|
||||
const [defaultLogoTextWhiteLabel, setDefaultLogoTextWhiteLabel] = useState(
|
||||
""
|
||||
);
|
||||
const [defaultLogoTextWhiteLabel, setDefaultLogoTextWhiteLabel] =
|
||||
useState("");
|
||||
|
||||
const [logoUrlsWhiteLabel, setLogoUrlsWhiteLabel] = useState(null);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
@ -234,7 +233,7 @@ const WhiteLabel = (props) => {
|
||||
className="settings_unavailable"
|
||||
>
|
||||
<TextInput
|
||||
className="input"
|
||||
className="company-name input"
|
||||
value={logoTextWhiteLabel}
|
||||
onChange={onChangeCompanyName}
|
||||
isDisabled={!isSettingPaid}
|
||||
@ -272,6 +271,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[0].path.light}
|
||||
imageClass="logo-header background-light"
|
||||
inputId="logoUploader_1_light"
|
||||
linkId="link-space-header-light"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -281,6 +281,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[0].path.dark}
|
||||
imageClass="logo-header background-dark"
|
||||
inputId="logoUploader_1_dark"
|
||||
linkId="link-space-header-dark"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -303,6 +304,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[5].path.light}
|
||||
imageClass="border-img logo-compact background-light"
|
||||
inputId="logoUploader_6_light"
|
||||
linkId="link-compact-left-menu-light"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -312,6 +314,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[5].path.dark}
|
||||
imageClass="border-img logo-compact background-dark"
|
||||
inputId="logoUploader_6_dark"
|
||||
linkId="link-compact-left-menu-dark"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -334,6 +337,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[1].path.light}
|
||||
imageClass="border-img logo-big background-white"
|
||||
inputId="logoUploader_2_light"
|
||||
linkId="link-login-emails-light"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -343,6 +347,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[1].path.dark}
|
||||
imageClass="border-img logo-big background-dark"
|
||||
inputId="logoUploader_2_dark"
|
||||
linkId="link-login-emails-dark"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -365,6 +370,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[6].path.light}
|
||||
imageClass="border-img logo-about background-white"
|
||||
inputId="logoUploader_7_light"
|
||||
linkId="link-about-light"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -374,6 +380,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[6].path.dark}
|
||||
imageClass="border-img logo-about background-dark"
|
||||
inputId="logoUploader_7_dark"
|
||||
linkId="link-about-dark"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -394,6 +401,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[2].path.light}
|
||||
imageClass="border-img logo-favicon"
|
||||
inputId="logoUploader_3_light"
|
||||
linkId="link-favicon"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -413,6 +421,7 @@ const WhiteLabel = (props) => {
|
||||
isEditor={true}
|
||||
src={logoUrlsWhiteLabel[3].path.light}
|
||||
inputId="logoUploader_4_light"
|
||||
linkId="link-editors-header"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -432,6 +441,7 @@ const WhiteLabel = (props) => {
|
||||
src={logoUrlsWhiteLabel[4].path.light}
|
||||
imageClass="border-img logo-embedded-editor background-white"
|
||||
inputId="logoUploader_5_light"
|
||||
linkId="link-embedded-editor"
|
||||
onChangeText={t("ChangeLogoButton")}
|
||||
onChange={onChangeLogo}
|
||||
isSettingPaid={isSettingPaid}
|
||||
@ -450,6 +460,8 @@ const WhiteLabel = (props) => {
|
||||
showReminder={isSettingPaid}
|
||||
saveButtonDisabled={isEqualLogo && isEqualText}
|
||||
isSaving={isSaving}
|
||||
additionalClassSaveButton="white-label-save"
|
||||
additionalClassCancelButton="white-label-cancel"
|
||||
/>
|
||||
</WhiteLabelWrapper>
|
||||
);
|
||||
|
@ -83,13 +83,11 @@ const LanguageAndTimeZone = (props) => {
|
||||
|
||||
React.useEffect(() => {
|
||||
languageFromSessionStorage = getFromSessionStorage("language");
|
||||
languageDefaultFromSessionStorage = getFromSessionStorage(
|
||||
"languageDefault"
|
||||
);
|
||||
languageDefaultFromSessionStorage =
|
||||
getFromSessionStorage("languageDefault");
|
||||
timezoneFromSessionStorage = getFromSessionStorage("timezone");
|
||||
timezoneDefaultFromSessionStorage = getFromSessionStorage(
|
||||
"timezoneDefault"
|
||||
);
|
||||
timezoneDefaultFromSessionStorage =
|
||||
getFromSessionStorage("timezoneDefault");
|
||||
|
||||
setDocumentTitle(t("StudioTimeLanguageSettings"));
|
||||
|
||||
@ -240,9 +238,8 @@ const LanguageAndTimeZone = (props) => {
|
||||
}
|
||||
|
||||
// TODO: Remove div with height 64 and remove settings-mobile class
|
||||
const settingsMobile = document.getElementsByClassName(
|
||||
"settings-mobile"
|
||||
)[0];
|
||||
const settingsMobile =
|
||||
document.getElementsByClassName("settings-mobile")[0];
|
||||
|
||||
if (settingsMobile) {
|
||||
settingsMobile.style.display = "none";
|
||||
@ -499,6 +496,7 @@ const LanguageAndTimeZone = (props) => {
|
||||
{t("StudioTimeLanguageSettings")}
|
||||
</div>
|
||||
<HelpButton
|
||||
className="language-time-zone-help-button"
|
||||
offsetRight={0}
|
||||
iconName={CombinedShapeSvgUrl}
|
||||
size={12}
|
||||
@ -522,6 +520,8 @@ const LanguageAndTimeZone = (props) => {
|
||||
cancelButtonLabel={t("Common:CancelButton")}
|
||||
displaySettings={true}
|
||||
hasScroll={hasScroll}
|
||||
additionalClassSaveButton="language-time-zone-save"
|
||||
additionalClassCancelButton="language-time-zone-cancel"
|
||||
/>
|
||||
</StyledSettingsComponent>
|
||||
);
|
||||
@ -544,12 +544,8 @@ export default inject(({ auth, setup, common }) => {
|
||||
const { user } = auth.userStore;
|
||||
|
||||
const { setLanguageAndTime } = setup;
|
||||
const {
|
||||
isLoaded,
|
||||
setIsLoadedLngTZSettings,
|
||||
initSettings,
|
||||
setIsLoaded,
|
||||
} = common;
|
||||
const { isLoaded, setIsLoadedLngTZSettings, initSettings, setIsLoaded } =
|
||||
common;
|
||||
return {
|
||||
theme: auth.settingsStore.theme,
|
||||
user,
|
||||
|
@ -313,6 +313,7 @@ const PortalRenaming = (props) => {
|
||||
<div className="category-item-heading">
|
||||
<div className="category-item-title">{t("PortalRenaming")}</div>
|
||||
<HelpButton
|
||||
className="portal-renaming-help-button"
|
||||
offsetRight={0}
|
||||
iconName={CombinedShapeSvgUrl}
|
||||
size={12}
|
||||
@ -337,6 +338,8 @@ const PortalRenaming = (props) => {
|
||||
reminderTest={t("YouHaveUnsavedChanges")}
|
||||
displaySettings={true}
|
||||
hasScroll={hasScroll}
|
||||
additionalClassSaveButton="portal-renaming-save"
|
||||
additionalClassCancelButton="portal-renaming-cancel"
|
||||
/>
|
||||
<PortalRenamingDialog
|
||||
visible={isShowModal}
|
||||
|
@ -326,6 +326,7 @@ const WelcomePageSettings = (props) => {
|
||||
<div className="category-item-heading">
|
||||
<div className="category-item-title">{t("CustomTitlesWelcome")}</div>
|
||||
<HelpButton
|
||||
className="welcome-page-help-button"
|
||||
offsetRight={0}
|
||||
iconName={CombinedShapeSvgUrl}
|
||||
size={12}
|
||||
@ -351,6 +352,8 @@ const WelcomePageSettings = (props) => {
|
||||
displaySettings={true}
|
||||
hasScroll={state.hasScroll}
|
||||
disableRestoreToDefault={greetingSettingsIsDefault}
|
||||
additionalClassSaveButton="welcome-page-save"
|
||||
additionalClassCancelButton="welcome-page-cancel"
|
||||
/>
|
||||
</StyledSettingsComponent>
|
||||
);
|
||||
|
@ -107,6 +107,7 @@ const Appearance = (props) => {
|
||||
const array_items = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "light-theme",
|
||||
key: "0",
|
||||
title: t("Profile:LightTheme"),
|
||||
content: (
|
||||
@ -120,6 +121,7 @@ const Appearance = (props) => {
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "dark-theme",
|
||||
key: "1",
|
||||
title: t("Profile:DarkTheme"),
|
||||
content: (
|
||||
@ -733,7 +735,7 @@ const Appearance = (props) => {
|
||||
|
||||
<div className="buttons-container">
|
||||
<Button
|
||||
className="button"
|
||||
className="save button"
|
||||
label={t("Common:SaveButton")}
|
||||
onClick={onSave}
|
||||
primary
|
||||
@ -742,7 +744,7 @@ const Appearance = (props) => {
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="button"
|
||||
className="edit-current-theme button"
|
||||
label={t("Settings:EditCurrentTheme")}
|
||||
onClick={onClickEdit}
|
||||
size="small"
|
||||
@ -750,7 +752,7 @@ const Appearance = (props) => {
|
||||
/>
|
||||
{isShowDeleteButton && (
|
||||
<Button
|
||||
className="button"
|
||||
className="delete-theme button"
|
||||
label={t("Settings:DeleteTheme")}
|
||||
onClick={onOpenDialogDelete}
|
||||
size="small"
|
||||
|
@ -123,6 +123,7 @@ const ColorSchemeDialog = (props) => {
|
||||
{showSaveButtonDialog && (
|
||||
<>
|
||||
<Button
|
||||
className="save"
|
||||
label={t("Common:SaveButton")}
|
||||
size="normal"
|
||||
primary
|
||||
@ -130,6 +131,7 @@ const ColorSchemeDialog = (props) => {
|
||||
onClick={onSaveColorSchemeDialog}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
scale
|
||||
|
@ -49,7 +49,7 @@ export const LanguageTimeSettingsTooltip = ({
|
||||
button at the bottom of the section.
|
||||
<Link
|
||||
color={currentColorScheme.main.accent}
|
||||
className="display-block font-size"
|
||||
className="tooltip-link display-block font-size"
|
||||
isHovered={true}
|
||||
target="_blank"
|
||||
href={`${helpLink}/administration/docspace-settings.aspx#DocSpacelanguage`}
|
||||
@ -120,7 +120,7 @@ export const DNSSettingsTooltip = ({
|
||||
</Trans>
|
||||
<Link
|
||||
color={currentColorScheme.main.accent}
|
||||
className="display-block font-size"
|
||||
className="tooltip-link display-block font-size"
|
||||
isHovered={true}
|
||||
target="_blank"
|
||||
href={`${helpLink}/administration/docspace-settings.aspx#alternativeurl`}
|
||||
|
@ -119,7 +119,7 @@ const HexColorPickerComponent = (props) => {
|
||||
/>
|
||||
<Button
|
||||
label={t("Common:CancelButton")}
|
||||
className="button"
|
||||
className="cancel-button button"
|
||||
size="small"
|
||||
scale={true}
|
||||
onClick={onCloseHexColorPicker}
|
||||
|
@ -33,14 +33,14 @@ const ModalDialogDelete = (props) => {
|
||||
<ModalDialog.Body>{t("Settings:DeleteThemeNotice")}</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="button-modal"
|
||||
className="delete button-modal"
|
||||
label={t("Common:Delete")}
|
||||
onClick={onClickDelete}
|
||||
primary
|
||||
size="normal"
|
||||
/>
|
||||
<Button
|
||||
className="button-modal"
|
||||
className="cancel-button button-modal"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
onClick={onClose}
|
||||
|
@ -441,7 +441,8 @@ class AutomaticBackup extends React.PureComponent {
|
||||
{renderTooltip(
|
||||
t("AutoBackupHelp") +
|
||||
" " +
|
||||
t("AutoBackupHelpNote", { organizationName })
|
||||
t("AutoBackupHelpNote", { organizationName }),
|
||||
"automatic-backup"
|
||||
)}
|
||||
{!isEnableAuto && (
|
||||
<Badge
|
||||
@ -457,7 +458,7 @@ class AutomaticBackup extends React.PureComponent {
|
||||
</Text>
|
||||
<div className="backup_toggle-wrapper">
|
||||
<ToggleButton
|
||||
className="backup_toggle-btn"
|
||||
className="enable-automatic-backup backup_toggle-btn"
|
||||
label={t("EnableAutomaticBackup")}
|
||||
onChange={this.onClickPermissions}
|
||||
isChecked={selectedEnableSchedule}
|
||||
@ -472,6 +473,7 @@ class AutomaticBackup extends React.PureComponent {
|
||||
<StyledModules>
|
||||
<RadioButton
|
||||
{...commonRadioButtonProps}
|
||||
id="backup-room"
|
||||
label={t("RoomsModule")}
|
||||
name={`${DocumentModuleType}`}
|
||||
key={0}
|
||||
@ -493,6 +495,7 @@ class AutomaticBackup extends React.PureComponent {
|
||||
>
|
||||
<RadioButton
|
||||
{...commonRadioButtonProps}
|
||||
id="third-party-resource"
|
||||
label={t("ThirdPartyResource")}
|
||||
name={`${ResourcesModuleType}`}
|
||||
isChecked={isCheckedThirdParty}
|
||||
@ -515,6 +518,7 @@ class AutomaticBackup extends React.PureComponent {
|
||||
<StyledModules>
|
||||
<RadioButton
|
||||
{...commonRadioButtonProps}
|
||||
id="third-party-storage"
|
||||
label={t("Common:ThirdPartyStorage")}
|
||||
name={`${StorageModuleType}`}
|
||||
isChecked={isCheckedThirdPartyStorage}
|
||||
|
@ -13,8 +13,6 @@ const ButtonContainer = ({
|
||||
}) => {
|
||||
const prevChange = useRef();
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
prevChange.current = isChanged;
|
||||
}, [isChanged]);
|
||||
@ -32,6 +30,7 @@ const ButtonContainer = ({
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="cancel-button"
|
||||
label={t("Common:CancelButton")}
|
||||
isDisabled={isLoadingData}
|
||||
onClick={onCancelModuleSettings}
|
||||
|
@ -364,6 +364,7 @@ const DirectThirdPartyConnection = (props) => {
|
||||
|
||||
{!connectedThirdPartyAccount?.id || !isTheSameThirdPartyAccount ? (
|
||||
<Button
|
||||
id="connect-button"
|
||||
primary
|
||||
label={t("Common:Connect")}
|
||||
onClick={onConnect}
|
||||
|
@ -254,10 +254,11 @@ class AmazonSettings extends React.Component {
|
||||
} = this.props;
|
||||
const { region } = this.state;
|
||||
|
||||
const renderTooltip = (helpInfo) => {
|
||||
const renderTooltip = (helpInfo, className) => {
|
||||
return (
|
||||
<>
|
||||
<HelpButton
|
||||
className={className}
|
||||
offsetRight={0}
|
||||
iconName={HelpReactSvgUrl}
|
||||
tooltipContent={
|
||||
@ -291,9 +292,10 @@ class AmazonSettings extends React.Component {
|
||||
<StyledBody>
|
||||
<div className="backup_storage-tooltip">
|
||||
<Text isBold>{this.bucketPlaceholder}</Text>
|
||||
{renderTooltip(t("AmazonBucketTip"))}
|
||||
{renderTooltip(t("AmazonBucketTip"), "bucket-tooltip")}
|
||||
</div>
|
||||
<TextInput
|
||||
id="bucket-input"
|
||||
name={bucket}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
@ -307,10 +309,10 @@ class AmazonSettings extends React.Component {
|
||||
<StyledBody>
|
||||
<div className="backup_storage-tooltip">
|
||||
<Text isBold>{this.regionPlaceholder}</Text>
|
||||
{renderTooltip(t("AmazonRegionTip"))}
|
||||
{renderTooltip(t("AmazonRegionTip"), "region-tooltip")}
|
||||
</div>
|
||||
<ComboBox
|
||||
className="backup_text-input"
|
||||
className="region-combo-box backup_text-input"
|
||||
options={this.regions}
|
||||
selectedOption={{
|
||||
key: 0,
|
||||
@ -330,9 +332,10 @@ class AmazonSettings extends React.Component {
|
||||
<StyledBody>
|
||||
<div className="backup_storage-tooltip">
|
||||
<Text isBold>{this.serviceUrlPlaceholder}</Text>
|
||||
{renderTooltip(t("AmazonServiceTip"))}
|
||||
{renderTooltip(t("AmazonServiceTip"), "service-tooltip")}
|
||||
</div>
|
||||
<TextInput
|
||||
id="service-url-input"
|
||||
name={serviceurl}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
@ -346,6 +349,7 @@ class AmazonSettings extends React.Component {
|
||||
|
||||
<StyledBody theme={theme}>
|
||||
<Checkbox
|
||||
id="force-path-style"
|
||||
name={forcepathstyle}
|
||||
label={this.forcePathStylePlaceholder}
|
||||
isChecked={formSettings[forcepathstyle] === "false" ? false : true}
|
||||
@ -355,13 +359,17 @@ class AmazonSettings extends React.Component {
|
||||
tabIndex={4}
|
||||
helpButton={
|
||||
<div className="backup_storage-tooltip">
|
||||
{renderTooltip(t("AmazonForcePathStyleTip"))}
|
||||
{renderTooltip(
|
||||
t("AmazonForcePathStyleTip"),
|
||||
"force-path-style-tooltip"
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</StyledBody>
|
||||
<StyledBody theme={theme}>
|
||||
<Checkbox
|
||||
id="use-http"
|
||||
className="backup_checkbox"
|
||||
name={usehttp}
|
||||
label={this.useHttpPlaceholder}
|
||||
@ -372,7 +380,7 @@ class AmazonSettings extends React.Component {
|
||||
tabIndex={5}
|
||||
helpButton={
|
||||
<div className="backup_storage-tooltip">
|
||||
{renderTooltip(t("AmazonHTTPTip"))}
|
||||
{renderTooltip(t("AmazonHTTPTip"), "http-tooltip")}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@ -380,10 +388,10 @@ class AmazonSettings extends React.Component {
|
||||
<StyledBody>
|
||||
<div className="backup_storage-tooltip">
|
||||
<Text isBold>{this.SSEPlaceholder}</Text>
|
||||
{renderTooltip(t("AmazonSSETip"))}
|
||||
{renderTooltip(t("AmazonSSETip"), "sse-method-tooltip")}
|
||||
</div>
|
||||
<ComboBox
|
||||
className="backup_text-input"
|
||||
className="sse-method-combo-box backup_text-input"
|
||||
options={this.availableEncryptions}
|
||||
selectedOption={{
|
||||
key: 0,
|
||||
@ -403,6 +411,7 @@ class AmazonSettings extends React.Component {
|
||||
{selectedEncryption === this.serverSideEncryption && (
|
||||
<>
|
||||
<RadioButton
|
||||
id="sse-s3"
|
||||
className="backup_radio-button-settings"
|
||||
value=""
|
||||
label={this.sse_s3}
|
||||
@ -413,6 +422,7 @@ class AmazonSettings extends React.Component {
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
id="sse-kms"
|
||||
className="backup_radio-button-settings"
|
||||
value=""
|
||||
label={this.sse_kms}
|
||||
@ -426,7 +436,7 @@ class AmazonSettings extends React.Component {
|
||||
<>
|
||||
<Text isBold>{"Managed CMK"}</Text>
|
||||
<ComboBox
|
||||
className="backup_text-input"
|
||||
className="managed-cmk-combo-box backup_text-input"
|
||||
options={this.managedKeys}
|
||||
selectedOption={{
|
||||
key: 0,
|
||||
@ -447,6 +457,7 @@ class AmazonSettings extends React.Component {
|
||||
<>
|
||||
<Text isBold>{"KMS Key Id:"}</Text>
|
||||
<TextInput
|
||||
id="customer-manager-kms-key-id"
|
||||
name={sse_key}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
@ -467,6 +478,7 @@ class AmazonSettings extends React.Component {
|
||||
<>
|
||||
<Text isBold>{"KMS Key Id:"}</Text>
|
||||
<TextInput
|
||||
id="client-side-encryption-kms-key-id"
|
||||
name={sse_key}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
@ -481,6 +493,7 @@ class AmazonSettings extends React.Component {
|
||||
|
||||
{isNeedFilePath && (
|
||||
<TextInput
|
||||
id="file-path-input"
|
||||
name="filePath"
|
||||
className="backup_text-input"
|
||||
scale
|
||||
|
@ -47,6 +47,7 @@ class GoogleCloudSettings extends React.Component {
|
||||
return (
|
||||
<>
|
||||
<TextInput
|
||||
id="bucket-input"
|
||||
name={bucket}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
@ -60,6 +61,7 @@ class GoogleCloudSettings extends React.Component {
|
||||
|
||||
{isNeedFilePath && (
|
||||
<TextInput
|
||||
id="file-path-input"
|
||||
name={filePath}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
|
@ -61,6 +61,7 @@ class RackspaceSettings extends React.Component {
|
||||
return (
|
||||
<>
|
||||
<TextInput
|
||||
id="private-container-input"
|
||||
name={private_container}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
@ -72,6 +73,7 @@ class RackspaceSettings extends React.Component {
|
||||
tabIndex={1}
|
||||
/>
|
||||
<TextInput
|
||||
id="public-container-input"
|
||||
name={public_container}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
@ -83,6 +85,7 @@ class RackspaceSettings extends React.Component {
|
||||
tabIndex={2}
|
||||
/>
|
||||
<TextInput
|
||||
id="region-input"
|
||||
name={region}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
@ -95,6 +98,7 @@ class RackspaceSettings extends React.Component {
|
||||
/>
|
||||
{isNeedFilePath && (
|
||||
<TextInput
|
||||
id="file-path-input"
|
||||
name={filePath}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
|
@ -57,6 +57,7 @@ class SelectelSettings extends React.Component {
|
||||
return (
|
||||
<>
|
||||
<TextInput
|
||||
id="private-container-input"
|
||||
name={private_container}
|
||||
className="backup_text-input"
|
||||
scale={true}
|
||||
@ -68,6 +69,7 @@ class SelectelSettings extends React.Component {
|
||||
tabIndex={1}
|
||||
/>
|
||||
<TextInput
|
||||
id="public-container-input"
|
||||
name={public_container}
|
||||
className="backup_text-input"
|
||||
scale={true}
|
||||
@ -81,6 +83,7 @@ class SelectelSettings extends React.Component {
|
||||
|
||||
{isNeedFilePath && (
|
||||
<TextInput
|
||||
id="file-path-input"
|
||||
name={filePath}
|
||||
className="backup_text-input"
|
||||
scale
|
||||
|
@ -35,6 +35,7 @@ const Backup = ({
|
||||
</Trans>
|
||||
<div>
|
||||
<Link
|
||||
id="link-tooltip"
|
||||
as="a"
|
||||
href={automaticBackupUrl}
|
||||
target="_blank"
|
||||
@ -90,11 +91,8 @@ export default inject(({ auth }) => {
|
||||
const { settingsStore, currentTariffStatusStore } = auth;
|
||||
const { isNotPaidPeriod } = currentTariffStatusStore;
|
||||
|
||||
const {
|
||||
automaticBackupUrl,
|
||||
isTabletView,
|
||||
currentColorScheme,
|
||||
} = settingsStore;
|
||||
const { automaticBackupUrl, isTabletView, currentColorScheme } =
|
||||
settingsStore;
|
||||
|
||||
const buttonSize = isTabletView ? "normal" : "small";
|
||||
|
||||
|
@ -278,13 +278,14 @@ class ManualBackup extends React.Component {
|
||||
<Text isBold fontSize="16px">
|
||||
{t("DataBackup")}
|
||||
</Text>
|
||||
{renderTooltip(t("ManualBackupHelp"))}
|
||||
{renderTooltip(t("ManualBackupHelp"), "data-backup")}
|
||||
</div>
|
||||
<Text className="backup_modules-description">
|
||||
{t("ManualBackupDescription")}
|
||||
</Text>
|
||||
<StyledModules>
|
||||
<RadioButton
|
||||
id="temporary-storage"
|
||||
label={t("TemporaryStorage")}
|
||||
name={"isCheckedTemporaryStorage"}
|
||||
key={0}
|
||||
@ -298,6 +299,7 @@ class ManualBackup extends React.Component {
|
||||
{isCheckedTemporaryStorage && (
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
id="create-button"
|
||||
label={t("Common:Create")}
|
||||
onClick={this.onMakeTemporaryBackup}
|
||||
primary
|
||||
@ -306,6 +308,7 @@ class ManualBackup extends React.Component {
|
||||
/>
|
||||
{temporaryLink?.length > 0 && isMaxProgress && (
|
||||
<Button
|
||||
id="download-copy"
|
||||
label={t("DownloadCopy")}
|
||||
onClick={this.onClickDownloadBackup}
|
||||
isDisabled={false}
|
||||
@ -326,6 +329,7 @@ class ManualBackup extends React.Component {
|
||||
</StyledModules>
|
||||
<StyledModules isDisabled={isNotPaidPeriod}>
|
||||
<RadioButton
|
||||
id="backup-room"
|
||||
label={t("RoomsModule")}
|
||||
name={"isCheckedDocuments"}
|
||||
key={1}
|
||||
@ -348,6 +352,7 @@ class ManualBackup extends React.Component {
|
||||
|
||||
<StyledModules isDisabled={isNotPaidPeriod}>
|
||||
<RadioButton
|
||||
id="third-party-resource"
|
||||
label={t("ThirdPartyResource")}
|
||||
name={"isCheckedThirdParty"}
|
||||
key={2}
|
||||
@ -362,6 +367,7 @@ class ManualBackup extends React.Component {
|
||||
</StyledModules>
|
||||
<StyledModules isDisabled={isNotPaidPeriod}>
|
||||
<RadioButton
|
||||
id="third-party-storage"
|
||||
label={t("Common:ThirdPartyStorage")}
|
||||
name={"isCheckedThirdPartyStorage"}
|
||||
key={3}
|
||||
|
@ -87,6 +87,7 @@ class RoomsModule extends React.Component {
|
||||
</div>
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
id="create-copy"
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={this.onMakeCopy}
|
||||
primary
|
||||
|
@ -8,11 +8,8 @@ import { ThirdPartyStorages } from "@docspace/common/constants";
|
||||
class AmazonStorage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const {
|
||||
selectedStorage,
|
||||
setCompletedFormFields,
|
||||
storageRegions,
|
||||
} = this.props;
|
||||
const { selectedStorage, setCompletedFormFields, storageRegions } =
|
||||
this.props;
|
||||
|
||||
const basicValues = AmazonSettings.formNames(storageRegions[0].systemName);
|
||||
|
||||
@ -50,6 +47,7 @@ class AmazonStorage extends React.Component {
|
||||
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
id="create-copy"
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={onMakeCopyIntoStorage}
|
||||
primary
|
||||
|
@ -47,6 +47,7 @@ class GoogleCloudStorage extends React.Component {
|
||||
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
id="create-copy"
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={onMakeCopyIntoStorage}
|
||||
primary
|
||||
|
@ -48,6 +48,7 @@ class RackspaceStorage extends React.Component {
|
||||
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
id="create-copy"
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={onMakeCopyIntoStorage}
|
||||
primary
|
||||
|
@ -47,6 +47,7 @@ class SelectelStorage extends React.Component {
|
||||
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
id="create-copy"
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={onMakeCopyIntoStorage}
|
||||
primary
|
||||
|
@ -142,10 +142,18 @@ const RestoreBackup = (props) => {
|
||||
fontWeight="400"
|
||||
className="backup_radio-button"
|
||||
options={[
|
||||
{ value: LOCAL_FILE, label: t("LocalFile") },
|
||||
{ value: BACKUP_ROOM, label: t("RoomsModule") },
|
||||
{ value: DISK_SPACE, label: t("ThirdPartyResource") },
|
||||
{ value: STORAGE_SPACE, label: t("Common:ThirdPartyStorage") },
|
||||
{ id: "local-file", value: LOCAL_FILE, label: t("LocalFile") },
|
||||
{ id: "backup-room", value: BACKUP_ROOM, label: t("RoomsModule") },
|
||||
{
|
||||
id: "third-party-resource",
|
||||
value: DISK_SPACE,
|
||||
label: t("ThirdPartyResource"),
|
||||
},
|
||||
{
|
||||
id: "third-party-storage",
|
||||
value: STORAGE_SPACE,
|
||||
label: t("Common:ThirdPartyStorage"),
|
||||
},
|
||||
]}
|
||||
onClick={onChangeRadioButton}
|
||||
selected={radioButtonState}
|
||||
|
@ -209,6 +209,7 @@ const BackupListModalDialog = (props) => {
|
||||
{t("BackupListWarningText")}
|
||||
</Text>
|
||||
<Link
|
||||
id="delete-backups"
|
||||
onClick={this.onCleanBackupList}
|
||||
fontWeight={600}
|
||||
style={{ textDecoration: "underline dotted" }}
|
||||
@ -269,6 +270,7 @@ const BackupListModalDialog = (props) => {
|
||||
|
||||
<div className="restore_dialog-button">
|
||||
<Button
|
||||
className="restore"
|
||||
primary
|
||||
size="normal"
|
||||
label={t("Common:Restore")}
|
||||
@ -276,6 +278,7 @@ const BackupListModalDialog = (props) => {
|
||||
isDisabled={isCopyingToLocal || !isChecked}
|
||||
/>
|
||||
<Button
|
||||
className="close"
|
||||
size="normal"
|
||||
label={t("Common:CloseButton")}
|
||||
onClick={onModalClose}
|
||||
|
@ -39,13 +39,14 @@ const DataManagementWrapper = (props) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const renderTooltip = (helpInfo) => {
|
||||
const renderTooltip = (helpInfo, className) => {
|
||||
const isAutoBackupPage = window.location.pathname.includes(
|
||||
"portal-settings/backup/auto-backup"
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<HelpButton
|
||||
className={className}
|
||||
place="bottom"
|
||||
iconName={HelpReactSvgUrl}
|
||||
tooltipContent={
|
||||
@ -55,6 +56,7 @@ const DataManagementWrapper = (props) => {
|
||||
</Trans>
|
||||
<div>
|
||||
<Link
|
||||
id="link-tooltip"
|
||||
as="a"
|
||||
href={isAutoBackupPage ? automaticBackupUrl : dataBackupUrl}
|
||||
target="_blank"
|
||||
|
@ -63,7 +63,7 @@ const PortalDeactivation = (props) => {
|
||||
<Text className="helper">{t("PortalDeactivationHelper")}</Text>
|
||||
<ButtonWrapper>
|
||||
<Button
|
||||
className="button"
|
||||
className="deactivate-button button"
|
||||
label={t("Deactivate")}
|
||||
primary
|
||||
size={isDesktopView ? "small" : "normal"}
|
||||
|
@ -75,7 +75,7 @@ const PortalDeletion = (props) => {
|
||||
<Text className="helper">{t("PortalDeletionHelper")}</Text>
|
||||
<ButtonWrapper>
|
||||
<Button
|
||||
className="button"
|
||||
className="delete-button button"
|
||||
label={t("Common:Delete")}
|
||||
primary
|
||||
size={isDesktopView ? "small" : "normal"}
|
||||
|
@ -261,6 +261,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("FrameId")} />
|
||||
<TextInput
|
||||
id="frame-id-input"
|
||||
scale={true}
|
||||
onChange={onChangeFrameId}
|
||||
placeholder={t("EnterId")}
|
||||
@ -270,6 +271,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("EmbeddingPanel:Width")} />
|
||||
<TextInput
|
||||
id="width-input"
|
||||
scale={true}
|
||||
onChange={onChangeWidth}
|
||||
placeholder={t("EnterWidth")}
|
||||
@ -279,6 +281,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("EmbeddingPanel:Height")} />
|
||||
<TextInput
|
||||
id="height-input"
|
||||
scale={true}
|
||||
onChange={onChangeHeight}
|
||||
placeholder={t("EnterHeight")}
|
||||
@ -286,21 +289,25 @@ const PortalIntegration = (props) => {
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<Checkbox
|
||||
id="header-checkbox"
|
||||
label={t("Header")}
|
||||
onChange={onChangeShowHeader}
|
||||
isChecked={config.showHeader}
|
||||
/>
|
||||
<Checkbox
|
||||
id="title-checkbox"
|
||||
label={t("Common:Title")}
|
||||
onChange={onChangeShowTitle}
|
||||
isChecked={config.showTitle}
|
||||
/>
|
||||
<Checkbox
|
||||
id="menu-checkbox"
|
||||
label={t("Menu")}
|
||||
onChange={onChangeShowArticle}
|
||||
isChecked={config.showArticle}
|
||||
/>
|
||||
<Checkbox
|
||||
id="filter-checkbox"
|
||||
label={t("Files:Filter")}
|
||||
onChange={onChangeShowFilter}
|
||||
isChecked={config.showFilter}
|
||||
@ -311,6 +318,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("FolderId")} />
|
||||
<TextInput
|
||||
id="folder-id-input"
|
||||
scale={true}
|
||||
onChange={onChangeFolderId}
|
||||
placeholder={t("EnterId")}
|
||||
@ -320,6 +328,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("ItemsCount")} />
|
||||
<TextInput
|
||||
id="items-count-input"
|
||||
scale={true}
|
||||
onChange={onChangeCount}
|
||||
placeholder={t("EnterCount")}
|
||||
@ -329,6 +338,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Page")} />
|
||||
<TextInput
|
||||
id="enter-page-input"
|
||||
scale={true}
|
||||
onChange={onChangePage}
|
||||
placeholder={t("EnterPage")}
|
||||
@ -341,12 +351,14 @@ const PortalIntegration = (props) => {
|
||||
style={{ flexDirection: "row", display: "flex", gap: "16px" }}
|
||||
>
|
||||
<TextInput
|
||||
id="search-term-input"
|
||||
scale={true}
|
||||
onChange={onChangeSearch}
|
||||
placeholder={t("Common:Search")}
|
||||
value={config.search}
|
||||
/>
|
||||
<Checkbox
|
||||
id="with-subfolders-checkbox"
|
||||
label={t("Files:WithSubfolders")}
|
||||
onChange={onChangeWithSubfolders}
|
||||
isChecked={withSubfolders}
|
||||
@ -356,6 +368,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Files:ByAuthor")} />
|
||||
<TextInput
|
||||
id="author-input"
|
||||
scale={true}
|
||||
onChange={onChangeAuthor}
|
||||
placeholder={t("Common:EnterName")}
|
||||
@ -365,6 +378,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Common:SortBy")} />
|
||||
<ComboBox
|
||||
id="sort-by-combo-box"
|
||||
onSelect={onChangeSortBy}
|
||||
options={dataSortBy}
|
||||
scaled={true}
|
||||
@ -376,6 +390,7 @@ const PortalIntegration = (props) => {
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("SortOrder")} />
|
||||
<ComboBox
|
||||
id="sort-order-combo-box"
|
||||
onSelect={onChangeSortOrder}
|
||||
options={dataSortOrder}
|
||||
scaled={true}
|
||||
@ -394,12 +409,14 @@ const PortalIntegration = (props) => {
|
||||
|
||||
<Buttons>
|
||||
<Button
|
||||
id="preview-button"
|
||||
primary
|
||||
size="normal"
|
||||
label={t("Common:Preview")}
|
||||
onClick={loadFrame}
|
||||
/>
|
||||
<Button
|
||||
id="destroy-button"
|
||||
primary
|
||||
size="normal"
|
||||
label={t("Destroy")}
|
||||
|
@ -69,7 +69,13 @@ const DetailsNavigationHeader = (props) => {
|
||||
<Headline type="content" truncate={true} className="headline">
|
||||
{t("WebhookDetails")}
|
||||
</Headline>
|
||||
<IconButton iconName={RetryIcon} size="17" isFill={true} onClick={handleRetryEvent} />
|
||||
<IconButton
|
||||
className="retry"
|
||||
iconName={RetryIcon}
|
||||
size="17"
|
||||
isFill={true}
|
||||
onClick={handleRetryEvent}
|
||||
/>
|
||||
|
||||
<NoBoxShadowToast />
|
||||
</HeaderContainer>
|
||||
|
@ -75,6 +75,7 @@ const RequestDetails = ({ eventDetails }) => {
|
||||
<Textarea isDisabled />
|
||||
) : (
|
||||
<Textarea
|
||||
classNameCopyIcon="request-header-copy"
|
||||
value={eventDetails.requestHeaders}
|
||||
enableCopy
|
||||
hasNumeration
|
||||
@ -89,6 +90,7 @@ const RequestDetails = ({ eventDetails }) => {
|
||||
</Text>
|
||||
{isJSON(eventDetails.requestPayload) ? (
|
||||
<Textarea
|
||||
classNameCopyIcon="request-body-copy"
|
||||
value={eventDetails.requestPayload}
|
||||
isJSONField
|
||||
enableCopy
|
||||
@ -97,7 +99,11 @@ const RequestDetails = ({ eventDetails }) => {
|
||||
copyInfoText={t("RequestBodyCopied")}
|
||||
/>
|
||||
) : (
|
||||
<Textarea value={eventDetails.requestPayload} heightScale className="textareaBody" />
|
||||
<Textarea
|
||||
value={eventDetails.requestPayload}
|
||||
heightScale
|
||||
className="textareaBody"
|
||||
/>
|
||||
)}
|
||||
</DetailsWrapper>
|
||||
);
|
||||
|
@ -79,7 +79,9 @@ const ResponseDetails = ({ eventDetails }) => {
|
||||
const openRawPayload = () => {
|
||||
const rawPayload = window.open("");
|
||||
isJSON(responsePayload)
|
||||
? rawPayload.document.write(beautifiedJSON.replace(/(?:\r\n|\r|\n)/g, "<br/>"))
|
||||
? rawPayload.document.write(
|
||||
beautifiedJSON.replace(/(?:\r\n|\r|\n)/g, "<br/>")
|
||||
)
|
||||
: rawPayload.document.write(responsePayload);
|
||||
rawPayload.focus();
|
||||
};
|
||||
@ -91,6 +93,7 @@ const ResponseDetails = ({ eventDetails }) => {
|
||||
</Text>
|
||||
{isJSON(eventDetails.responseHeaders) ? (
|
||||
<Textarea
|
||||
classNameCopyIcon="response-header-copy"
|
||||
value={eventDetails.responseHeaders}
|
||||
enableCopy
|
||||
hasNumeration
|
||||
@ -99,7 +102,11 @@ const ResponseDetails = ({ eventDetails }) => {
|
||||
copyInfoText={t("ResponseHeaderCopied")}
|
||||
/>
|
||||
) : (
|
||||
<Textarea value={eventDetails.responseHeaders} heightScale className="textareaBody" />
|
||||
<Textarea
|
||||
value={eventDetails.responseHeaders}
|
||||
heightScale
|
||||
className="textareaBody"
|
||||
/>
|
||||
)}
|
||||
<Text as="h3" fontWeight={600} className="mb-4 mt-16">
|
||||
{t("ResponsePostBody")}
|
||||
@ -110,6 +117,7 @@ const ResponseDetails = ({ eventDetails }) => {
|
||||
{t("PayloadIsTooLarge")}
|
||||
</Text>
|
||||
<Button
|
||||
className="view-raw-payload"
|
||||
size="small"
|
||||
onClick={openRawPayload}
|
||||
label={t("ViewRawPayload")}
|
||||
@ -120,6 +128,7 @@ const ResponseDetails = ({ eventDetails }) => {
|
||||
<Textarea isDisabled />
|
||||
) : isJSON(responsePayload) ? (
|
||||
<Textarea
|
||||
classNameCopyIcon="response-body-copy"
|
||||
value={responsePayload}
|
||||
isJSONField
|
||||
enableCopy
|
||||
@ -129,6 +138,7 @@ const ResponseDetails = ({ eventDetails }) => {
|
||||
/>
|
||||
) : (
|
||||
<Textarea
|
||||
classNameCopyIcon="response-body-copy"
|
||||
value={responsePayload}
|
||||
enableCopy
|
||||
heightScale
|
||||
|
@ -119,7 +119,7 @@ const DeliveryDatePicker = ({ filters, setFilters, isApplied, setIsApplied }) =>
|
||||
return (
|
||||
<div>
|
||||
<SelectedItem
|
||||
className="selectedItem"
|
||||
className="selectedItem delete-delivery-date-button"
|
||||
onClose={deleteSelectedDate}
|
||||
label={filters.deliveryDate.format("DD MMM YYYY") + formattedTime}
|
||||
onClick={toggleCalendar}
|
||||
@ -134,7 +134,6 @@ const DeliveryDatePicker = ({ filters, setFilters, isApplied, setIsApplied }) =>
|
||||
!calendarRef?.current?.contains(e.target) &&
|
||||
setIsCalendarOpen(false);
|
||||
};
|
||||
|
||||
const isEqualDates = (firstDate, secondDate) => {
|
||||
return firstDate.format("YYYY-MM-D HH:mm") === secondDate.format("YYYY-MM-D HH:mm");
|
||||
};
|
||||
@ -176,13 +175,19 @@ const DeliveryDatePicker = ({ filters, setFilters, isApplied, setIsApplied }) =>
|
||||
<Text isInline fontWeight={600} color="#A3A9AE" className="mr-8">
|
||||
{t("From")}
|
||||
</Text>
|
||||
<TimePicker onChange={setDeliveryFrom} hasError={!isTimeValid} tabIndex={1} />
|
||||
<TimePicker
|
||||
classNameInput="from-time"
|
||||
onChange={setDeliveryFrom}
|
||||
hasError={!isTimeValid}
|
||||
tabIndex={1}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<Text isInline fontWeight={600} color="#A3A9AE" className="mr-8">
|
||||
{t("Before")}
|
||||
</Text>
|
||||
<TimePicker
|
||||
classNameInput="before-time"
|
||||
date={filters.deliveryTo}
|
||||
setDate={setDeliveryTo}
|
||||
hasError={!isTimeValid}
|
||||
@ -191,7 +196,11 @@ const DeliveryDatePicker = ({ filters, setFilters, isApplied, setIsApplied }) =>
|
||||
</TimePickerCell>
|
||||
) : (
|
||||
<TimePickerCell>
|
||||
<SelectorAddButton title={t("Add")} onClick={showTimePicker} className="mr-8" />
|
||||
<SelectorAddButton
|
||||
title={t("Add")}
|
||||
onClick={showTimePicker}
|
||||
className="mr-8 add-delivery-time-button"
|
||||
/>
|
||||
<Text isInline fontWeight={600} color="#A3A9AE">
|
||||
{t("SelectDeliveryTime")}
|
||||
</Text>
|
||||
|
@ -23,10 +23,15 @@ const Selectors = styled.div`
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
const StatusBadgeSelector = ({ label, statusCode, isStatusSelected, handleStatusClick }) => {
|
||||
const StatusBadgeSelector = ({ label, statusCode, isStatusSelected, handleStatusClick, id }) => {
|
||||
const handleOnClick = () => handleStatusClick(statusCode);
|
||||
return (
|
||||
<RoundedButton label={label} onClick={handleOnClick} primary={isStatusSelected(statusCode)} />
|
||||
<RoundedButton
|
||||
id={id}
|
||||
label={label}
|
||||
onClick={handleOnClick}
|
||||
primary={isStatusSelected(statusCode)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -50,6 +55,7 @@ const StatusPicker = ({ filters, setFilters }) => {
|
||||
const StatusBadgeElements = StatusCodes.map((code) =>
|
||||
code === "Not sent" ? (
|
||||
<StatusBadgeSelector
|
||||
id="not-sent"
|
||||
label={t("NotSent")}
|
||||
statusCode={code}
|
||||
isStatusSelected={isStatusSelected}
|
||||
@ -58,13 +64,14 @@ const StatusPicker = ({ filters, setFilters }) => {
|
||||
/>
|
||||
) : (
|
||||
<StatusBadgeSelector
|
||||
id={code}
|
||||
label={code}
|
||||
statusCode={code}
|
||||
isStatusSelected={isStatusSelected}
|
||||
handleStatusClick={handleStatusClick}
|
||||
key={code}
|
||||
/>
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -54,8 +54,14 @@ function areArraysEqual(array1, array2) {
|
||||
}
|
||||
|
||||
const FilterDialog = (props) => {
|
||||
const { visible, closeModal, applyFilters, formatFilters, setHistoryFilters, historyFilters } =
|
||||
props;
|
||||
const {
|
||||
visible,
|
||||
closeModal,
|
||||
applyFilters,
|
||||
formatFilters,
|
||||
setHistoryFilters,
|
||||
historyFilters,
|
||||
} = props;
|
||||
const { t } = useTranslation(["Webhooks", "Files", "Common"]);
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
@ -130,12 +136,18 @@ const FilterDialog = (props) => {
|
||||
<ModalDialog.Footer>
|
||||
<Footer>
|
||||
<Button
|
||||
className="apply-button"
|
||||
label={t("Common:ApplyButton")}
|
||||
size="normal"
|
||||
primary={true}
|
||||
onClick={handleApplyFilters}
|
||||
/>
|
||||
<Button label={t("Common:CancelButton")} size="normal" onClick={closeModal} />
|
||||
<Button
|
||||
className="cancel-button"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
onClick={closeModal}
|
||||
/>
|
||||
</Footer>
|
||||
</ModalDialog.Footer>
|
||||
)}
|
||||
|
@ -54,7 +54,8 @@ const FilterButton = styled.div`
|
||||
z-index: ${(props) => (props.isGroupMenuVisible ? 199 : 201)};
|
||||
|
||||
border: 1px solid;
|
||||
border-color: ${(props) => (props.theme.isBase ? "#d0d5da" : "rgb(71, 71, 71)")};
|
||||
border-color: ${(props) =>
|
||||
props.theme.isBase ? "#d0d5da" : "rgb(71, 71, 71)"};
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
@ -107,7 +108,11 @@ const HistoryFilterHeader = (props) => {
|
||||
{t("Webhook")} {id}
|
||||
</ListHeading>
|
||||
|
||||
<FilterButton onClick={openFiltersModal} isGroupMenuVisible={isGroupMenuVisible}>
|
||||
<FilterButton
|
||||
id="filter-button"
|
||||
onClick={openFiltersModal}
|
||||
isGroupMenuVisible={isGroupMenuVisible}
|
||||
>
|
||||
<IconButton iconName={FilterReactSvrUrl} size={16} />
|
||||
<span hidden={historyFilters === null}></span>
|
||||
</FilterButton>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { inject, observer } from "mobx-react";
|
||||
@ -8,7 +8,7 @@ import RetryIcon from "PUBLIC_DIR/images/refresh.react.svg?url";
|
||||
|
||||
import Headline from "@docspace/common/components/Headline";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import { Hint } from "../../styled-components";
|
||||
// import { Hint } from "../../styled-components";
|
||||
|
||||
import { tablet } from "@docspace/components/utils/device";
|
||||
|
||||
@ -20,7 +20,7 @@ import toastr from "@docspace/components/toast/toastr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { showLoader, hideLoader } from "@docspace/common/utils";
|
||||
import FloatingButton from "@docspace/components/floating-button";
|
||||
|
||||
const HeaderContainer = styled.div`
|
||||
position: sticky;
|
||||
@ -78,6 +78,14 @@ const HeaderContainer = styled.div`
|
||||
width: calc(100% + 40px);
|
||||
height: 69px;
|
||||
|
||||
.combo-button_selected-icon {
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => (props.isDisabled ? "#d0d5da" : "#333")};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${() =>
|
||||
isMobile &&
|
||||
css`
|
||||
@ -109,6 +117,9 @@ const HistoryHeader = (props) => {
|
||||
theme,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
setRetryPendingFalse,
|
||||
setRetryPendingTrue,
|
||||
} = props;
|
||||
const navigate = useNavigate();
|
||||
const onBack = () => {
|
||||
@ -117,28 +128,37 @@ const HistoryHeader = (props) => {
|
||||
const { t } = useTranslation(["Webhooks", "Common", "InfoPanel"]);
|
||||
const { id } = useParams();
|
||||
|
||||
const [isPendingVisible, setIsPendingVisible] = useState(false);
|
||||
|
||||
const handleGroupSelection = (isChecked) => {
|
||||
isChecked ? checkAllIds() : emptyCheckedIds();
|
||||
};
|
||||
|
||||
const handleRetryAll = async () => {
|
||||
try {
|
||||
setRetryPendingTrue();
|
||||
const timeout = setTimeout(() => {
|
||||
setIsPendingVisible(true);
|
||||
}, 300);
|
||||
await retryWebhookEvents(checkedEventIds);
|
||||
await emptyCheckedIds();
|
||||
const tempIds = checkedEventIds;
|
||||
showLoader();
|
||||
await retryWebhookEvents(tempIds);
|
||||
hideLoader();
|
||||
clearTimeout(timeout);
|
||||
setRetryPendingFalse();
|
||||
setIsPendingVisible(false);
|
||||
await fetchHistoryItems({
|
||||
...(historyFilters ? formatFilters(historyFilters) : {}),
|
||||
configId: id,
|
||||
});
|
||||
toastr.success(
|
||||
`${t("WebhookRedilivered")}: ${checkedEventIds.length}`,
|
||||
<b>{t("Common:Done")}</b>,
|
||||
<b>{t("Common:Done")}</b>
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toastr.error(error);
|
||||
} finally {
|
||||
setRetryPendingFalse();
|
||||
setIsPendingVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -151,7 +171,8 @@ const HistoryHeader = (props) => {
|
||||
},
|
||||
];
|
||||
|
||||
const onKeyPress = (e) => (e.key === "Esc" || e.key === "Escape") && emptyCheckedIds();
|
||||
const onKeyPress = (e) =>
|
||||
(e.key === "Esc" || e.key === "Escape") && emptyCheckedIds();
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keyup", onKeyPress);
|
||||
@ -161,12 +182,14 @@ const HistoryHeader = (props) => {
|
||||
const menuItems = (
|
||||
<>
|
||||
<DropDownItem
|
||||
id="select-all"
|
||||
key="select-all-event-ids"
|
||||
label={t("Common:SelectAll")}
|
||||
data-index={0}
|
||||
onClick={checkAllIds}
|
||||
/>
|
||||
<DropDownItem
|
||||
id="unselect-all"
|
||||
key="unselect-all-event-ids"
|
||||
label={t("UnselectAll")}
|
||||
data-index={1}
|
||||
@ -203,6 +226,7 @@ const HistoryHeader = (props) => {
|
||||
isChecked={areAllIdsChecked}
|
||||
isIndeterminate={isIndeterminate}
|
||||
withoutInfoPanelToggler
|
||||
isBlocked={isRetryPending}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -211,7 +235,7 @@ const HistoryHeader = (props) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<HeaderContainer>
|
||||
<HeaderContainer isDisabled={isRetryPending}>
|
||||
{isMobileOnly ? (
|
||||
<>
|
||||
{isGroupMenuVisible && <GroupMenu />}
|
||||
@ -222,6 +246,8 @@ const HistoryHeader = (props) => {
|
||||
) : (
|
||||
<NavigationHeader />
|
||||
)}
|
||||
|
||||
{isPendingVisible && <FloatingButton icon="refresh" />}
|
||||
</HeaderContainer>
|
||||
);
|
||||
};
|
||||
@ -238,6 +264,9 @@ export default inject(({ webhooksStore, auth }) => {
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
setRetryPendingFalse,
|
||||
setRetryPendingTrue,
|
||||
} = webhooksStore;
|
||||
|
||||
const { settingsStore } = auth;
|
||||
@ -256,5 +285,8 @@ export default inject(({ webhooksStore, auth }) => {
|
||||
theme,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
setRetryPendingFalse,
|
||||
setRetryPendingTrue,
|
||||
};
|
||||
})(observer(HistoryHeader));
|
||||
|
@ -23,12 +23,14 @@ const HistoryRow = (props) => {
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
} = props;
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
|
||||
const redirectToDetails = () => navigate(window.location.pathname + `/${historyItem.id}`);
|
||||
const redirectToDetails = () =>
|
||||
navigate(window.location.pathname + `/${historyItem.id}`);
|
||||
const handleRetryEvent = async () => {
|
||||
await retryWebhookEvent(historyItem.id);
|
||||
await fetchHistoryItems({
|
||||
@ -54,16 +56,19 @@ const HistoryRow = (props) => {
|
||||
|
||||
const contextOptions = [
|
||||
{
|
||||
id: "webhook-details",
|
||||
key: "Webhook details dropdownItem",
|
||||
label: t("WebhookDetails"),
|
||||
icon: InfoIcon,
|
||||
onClick: redirectToDetails,
|
||||
},
|
||||
{
|
||||
id: "retry",
|
||||
key: "Retry dropdownItem",
|
||||
label: t("Retry"),
|
||||
icon: RetryIcon,
|
||||
onClick: handleRetryEvent,
|
||||
disabled: isRetryPending,
|
||||
},
|
||||
];
|
||||
|
||||
@ -75,9 +80,15 @@ const HistoryRow = (props) => {
|
||||
checkbox
|
||||
checked={isIdChecked(historyItem.id)}
|
||||
onSelect={handleOnSelect}
|
||||
className={isIdChecked(historyItem.id) ? "row-item selected-row-item" : "row-item "}
|
||||
onClick={handleRowClick}>
|
||||
<HistoryRowContent sectionWidth={sectionWidth} historyItem={historyItem} />
|
||||
className={
|
||||
isIdChecked(historyItem.id) ? "row-item selected-row-item" : "row-item "
|
||||
}
|
||||
onClick={handleRowClick}
|
||||
>
|
||||
<HistoryRowContent
|
||||
sectionWidth={sectionWidth}
|
||||
historyItem={historyItem}
|
||||
/>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
@ -90,6 +101,7 @@ export default inject(({ webhooksStore }) => {
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
} = webhooksStore;
|
||||
|
||||
return {
|
||||
@ -99,5 +111,6 @@ export default inject(({ webhooksStore }) => {
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
};
|
||||
})(observer(HistoryRow));
|
||||
|
@ -25,6 +25,17 @@ const StyledTableRow = styled(TableRow)`
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.p-menuitem-icon {
|
||||
svg {
|
||||
path {
|
||||
fill: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
.p-menuitem-text {
|
||||
color: red;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isHighlight &&
|
||||
css`
|
||||
@ -48,13 +59,18 @@ const HistoryTableRow = (props) => {
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
} = props;
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
|
||||
const redirectToDetails = () => navigate(window.location.pathname + `/${item.id}`);
|
||||
const redirectToDetails = () =>
|
||||
navigate(window.location.pathname + `/${item.id}`);
|
||||
const handleRetryEvent = async () => {
|
||||
if (isRetryPending) {
|
||||
return;
|
||||
}
|
||||
await retryWebhookEvent(item.id);
|
||||
await fetchHistoryItems({
|
||||
...(historyFilters ? formatFilters(historyFilters) : {}),
|
||||
@ -65,20 +81,24 @@ const HistoryTableRow = (props) => {
|
||||
|
||||
const contextOptions = [
|
||||
{
|
||||
id: "webhook-details",
|
||||
key: "Webhook details dropdownItem",
|
||||
label: t("WebhookDetails"),
|
||||
icon: InfoIcon,
|
||||
onClick: redirectToDetails,
|
||||
},
|
||||
{
|
||||
id: "retry",
|
||||
key: "Retry dropdownItem",
|
||||
label: t("Retry"),
|
||||
icon: RetryIcon,
|
||||
onClick: handleRetryEvent,
|
||||
disabled: isRetryPending,
|
||||
},
|
||||
];
|
||||
|
||||
const formattedDelivery = moment(item.delivery).format("MMM D, YYYY, h:mm:ss A") + " UTC";
|
||||
const formattedDelivery =
|
||||
moment(item.delivery).format("MMM D, YYYY, h:mm:ss A") + " UTC";
|
||||
|
||||
const onChange = (e) => {
|
||||
if (
|
||||
@ -96,11 +116,22 @@ const HistoryTableRow = (props) => {
|
||||
const isChecked = isIdChecked(item.id);
|
||||
|
||||
return (
|
||||
<StyledWrapper className={isChecked ? "selected-table-row" : ""} onClick={onChange}>
|
||||
<StyledTableRow contextOptions={contextOptions} checked={isChecked} hideColumns={hideColumns}>
|
||||
<StyledWrapper
|
||||
className={isChecked ? "selected-table-row" : ""}
|
||||
onClick={onChange}
|
||||
>
|
||||
<StyledTableRow
|
||||
contextOptions={contextOptions}
|
||||
checked={isChecked}
|
||||
hideColumns={hideColumns}
|
||||
>
|
||||
<TableCell>
|
||||
<TableCell checked={isChecked} className="checkboxWrapper">
|
||||
<Checkbox onChange={onChange} isChecked={isChecked} />
|
||||
<Checkbox
|
||||
className="checkbox"
|
||||
onChange={onChange}
|
||||
isChecked={isChecked}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<Text fontWeight={600}>{item.id}</Text>
|
||||
@ -126,6 +157,7 @@ export default inject(({ webhooksStore }) => {
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
} = webhooksStore;
|
||||
|
||||
return {
|
||||
@ -135,5 +167,6 @@ export default inject(({ webhooksStore }) => {
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
isRetryPending,
|
||||
};
|
||||
})(observer(HistoryTableRow));
|
||||
|
@ -111,11 +111,20 @@ const Webhooks = (props) => {
|
||||
/>
|
||||
</ButtonSeating>
|
||||
) : (
|
||||
<Button label={t("CreateWebhook")} primary size={"small"} onClick={openCreateModal} />
|
||||
<Button
|
||||
id="create-webhook-button"
|
||||
label={t("CreateWebhook")}
|
||||
primary
|
||||
size={"small"}
|
||||
onClick={openCreateModal}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isWebhooksEmpty && (
|
||||
<WebhooksTable openSettingsModal={openSettingsModal} openDeleteModal={openDeleteModal} />
|
||||
<WebhooksTable
|
||||
openSettingsModal={openSettingsModal}
|
||||
openDeleteModal={openDeleteModal}
|
||||
/>
|
||||
)}
|
||||
<WebhookDialog
|
||||
visible={isCreateOpened}
|
||||
|
@ -20,8 +20,14 @@ const Footer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
export const DeleteWebhookDialog = ({ visible, onClose, header, handleSubmit }) => {
|
||||
const onKeyPress = (e) => (e.key === "Esc" || e.key === "Escape") && onClose();
|
||||
export const DeleteWebhookDialog = ({
|
||||
visible,
|
||||
onClose,
|
||||
header,
|
||||
handleSubmit,
|
||||
}) => {
|
||||
const onKeyPress = (e) =>
|
||||
(e.key === "Esc" || e.key === "Escape") && onClose();
|
||||
|
||||
const { t } = useTranslation(["Webhooks", "Common", "EmptyTrashDialog"]);
|
||||
|
||||
@ -47,12 +53,18 @@ export const DeleteWebhookDialog = ({ visible, onClose, header, handleSubmit })
|
||||
<ModalDialog.Footer>
|
||||
<Footer>
|
||||
<Button
|
||||
id="delete-forever-button"
|
||||
label={t("EmptyTrashDialog:DeleteForeverButton")}
|
||||
size="normal"
|
||||
primary={true}
|
||||
onClick={handleDeleteClick}
|
||||
/>
|
||||
<Button label={t("Common:CancelButton")} size="normal" onClick={onClose} />
|
||||
<Button
|
||||
id="cancel-button"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
onClick={onClose}
|
||||
/>
|
||||
</Footer>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
|
@ -26,10 +26,12 @@ export const LabledInput = ({
|
||||
hasError,
|
||||
className,
|
||||
required = false,
|
||||
id,
|
||||
}) => {
|
||||
return (
|
||||
<StyledLabel text={label} className={className}>
|
||||
<TextInput
|
||||
id={id}
|
||||
name={name}
|
||||
placeholder={placeholder}
|
||||
tabIndex={1}
|
||||
|
@ -33,18 +33,27 @@ export const SSLVerification = ({ onChange, value }) => {
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
|
||||
const handleOnChange = (e) => {
|
||||
onChange({ target: { name: e.target.name, value: e.target.value === "true" } });
|
||||
onChange({
|
||||
target: { name: e.target.name, value: e.target.value === "true" },
|
||||
});
|
||||
};
|
||||
|
||||
const toggleHint = () => setIsHintVisible((prevIsHintVisible) => !prevIsHintVisible);
|
||||
const toggleHint = () =>
|
||||
setIsHintVisible((prevIsHintVisible) => !prevIsHintVisible);
|
||||
return (
|
||||
<Label
|
||||
text={
|
||||
<Header>
|
||||
{t("SSLVerification")}{" "}
|
||||
<StyledInfoIcon src={InfoIcon} alt="infoIcon" onClick={toggleHint} />
|
||||
<StyledInfoIcon
|
||||
className="ssl-verification-tooltip"
|
||||
src={InfoIcon}
|
||||
alt="infoIcon"
|
||||
onClick={toggleHint}
|
||||
/>
|
||||
</Header>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<Hint isTooltip hidden={!isHintVisible} onClick={toggleHint}>
|
||||
{t("SSLHint")}
|
||||
</Hint>
|
||||
@ -55,10 +64,12 @@ export const SSLVerification = ({ onChange, value }) => {
|
||||
onClick={handleOnChange}
|
||||
options={[
|
||||
{
|
||||
id: "enable-ssl",
|
||||
label: t("EnableSSL"),
|
||||
value: "true",
|
||||
},
|
||||
{
|
||||
id: "disable-ssl",
|
||||
label: t("DisableSSL"),
|
||||
value: "false",
|
||||
},
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user