This commit is contained in:
@@ -61,7 +61,7 @@ namespace AyaNova.Api.Controllers
|
||||
{
|
||||
if (serverState.IsClosed)
|
||||
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
||||
if (!Authorized.HasAnyRole(HttpContext.Items, AuthorizationRoles.OpsAdminFull))
|
||||
if (!Authorized.HasModifyRole(HttpContext.Items, AyaType.Backup))//technically maybe this could be wider open, but for now keeping as locked down
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
var JobName = $"Backup (on demand)";
|
||||
OpsJob j = new OpsJob();
|
||||
@@ -87,7 +87,7 @@ namespace AyaNova.Api.Controllers
|
||||
//Need size and more info
|
||||
if (serverState.IsClosed)
|
||||
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
||||
if (!Authorized.HasAnyRole(HttpContext.Items, AuthorizationRoles.OpsAdminFull | AuthorizationRoles.OpsAdminLimited))
|
||||
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Backup))
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
return Ok(ApiOkResponse.Response(FileUtil.BackupStatusReport()));
|
||||
}
|
||||
@@ -122,7 +122,7 @@ namespace AyaNova.Api.Controllers
|
||||
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
|
||||
}
|
||||
|
||||
if (!Authorized.HasAnyRole(DownloadUser.Roles, AuthorizationRoles.OpsAdminFull))
|
||||
if (!Authorized.HasModifyRole(HttpContext.Items, AyaType.Backup))//not technically modify but treating as such as a backup is very sensitive data
|
||||
{
|
||||
await Task.Delay(nFailedAuthDelay);//DOS protection
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace AyaNova.Api.Controllers
|
||||
//Note: the date and times are nullable and required so that the regular modelstate code kicks in to ensure they are present
|
||||
if (serverState.IsClosed)
|
||||
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
||||
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Metrics))
|
||||
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.ServerMetrics))
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
if (!ModelState.IsValid)
|
||||
return BadRequest(new ApiErrorResponse(ModelState));
|
||||
@@ -127,7 +127,7 @@ namespace AyaNova.Api.Controllers
|
||||
privateBytes = dsPrivateBytes.Select(z => new MetricLong(DateTime.FromOADate(z.Item1), z.Item2 / MB)).ToArray()
|
||||
};
|
||||
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.Metrics, AyaEvent.Retrieved), ct);
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.ServerMetrics, AyaEvent.Retrieved), ct);
|
||||
return Ok(ApiOkResponse.Response(ret));
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace AyaNova.Api.Controllers
|
||||
//Note: the date and times are nullable and required so that the regular modelstate code kicks in to ensure they are present
|
||||
if (serverState.IsClosed)
|
||||
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
||||
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Metrics))
|
||||
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.ServerMetrics))
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
if (!ModelState.IsValid)
|
||||
return BadRequest(new ApiErrorResponse(ModelState));
|
||||
@@ -189,7 +189,7 @@ namespace AyaNova.Api.Controllers
|
||||
|
||||
|
||||
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.Metrics, AyaEvent.Retrieved), ct);
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.ServerMetrics, AyaEvent.Retrieved), ct);
|
||||
return Ok(ApiOkResponse.Response(ret));
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace AyaNova.Api.Controllers
|
||||
//Note: the date and times are nullable and required so that the regular modelstate code kicks in to ensure they are present
|
||||
if (serverState.IsClosed)
|
||||
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
||||
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Metrics))
|
||||
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.ServerMetrics))
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
if (!ModelState.IsValid)
|
||||
return BadRequest(new ApiErrorResponse(ModelState));
|
||||
@@ -296,7 +296,7 @@ namespace AyaNova.Api.Controllers
|
||||
|
||||
|
||||
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.Metrics, AyaEvent.Retrieved), ct);
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.ServerMetrics, AyaEvent.Retrieved), ct);
|
||||
return Ok(ApiOkResponse.Response(ret));
|
||||
}
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ namespace AyaNova
|
||||
var utcNow = new DateTimeOffset(DateTime.Now.ToUniversalTime(), TimeSpan.Zero);
|
||||
if (u.DlKeyExpire > utcNow.DateTime)
|
||||
{
|
||||
if (AyaNova.Api.ControllerHelpers.Authorized.HasReadFullRole(u.Roles, AyaType.Metrics))
|
||||
if (AyaNova.Api.ControllerHelpers.Authorized.HasReadFullRole(u.Roles, AyaType.ServerMetrics))
|
||||
context.Request.HttpContext.Items["AY_PROFILER_ALLOWED"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace AyaNova.Biz
|
||||
InventoryFull | AccountingFull | TechLimited | TechFull | SubContractorLimited |
|
||||
SubContractorFull | OpsAdminLimited | OpsAdminFull | SalesFull | SalesLimited
|
||||
|
||||
|
||||
|
||||
}//end AuthorizationRoles
|
||||
//, 65536, 131072, 262144, 524288, 1,048,576
|
||||
}//end namespace GZTW.AyaNova.BLL
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace AyaNova.Biz
|
||||
[CoreBizObject]
|
||||
Contract = 10,
|
||||
TrialSeeder = 11,
|
||||
Metrics = 12,
|
||||
ServerMetrics = 12,
|
||||
Translation = 13,
|
||||
UserOptions = 14,
|
||||
[CoreBizObject]
|
||||
@@ -103,7 +103,11 @@ namespace AyaNova.Biz
|
||||
WorkOrderTemplate = 45,
|
||||
[CoreBizObject]
|
||||
WorkOrderTemplateItem = 46,
|
||||
GlobalOps=47
|
||||
GlobalOps=47,
|
||||
BizMetrics=48,
|
||||
Backup=49,
|
||||
Notification=50,
|
||||
NotificationSubscription=51
|
||||
|
||||
|
||||
//NOTE: New objects added here need to also be added to the following classes:
|
||||
|
||||
@@ -410,6 +410,15 @@ namespace AyaNova.Biz
|
||||
});
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//BACKUP
|
||||
//Only opsfull can change Backup
|
||||
//ops and biz admin can view Backup
|
||||
roles.Add(AyaType.Backup, new BizRoleSet()
|
||||
{
|
||||
Change = AuthorizationRoles.OpsAdminFull,
|
||||
ReadFullRecord = AuthorizationRoles.OpsAdminLimited | AuthorizationRoles.BizAdminFull | AuthorizationRoles.BizAdminLimited
|
||||
});
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
@@ -424,9 +433,9 @@ namespace AyaNova.Biz
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//METRICS
|
||||
//SERVERMETRICS
|
||||
//
|
||||
roles.Add(AyaType.Metrics, new BizRoleSet()
|
||||
roles.Add(AyaType.ServerMetrics, new BizRoleSet()
|
||||
{
|
||||
Change = AuthorizationRoles.OpsAdminFull,//this is to turn on extra metrics (profiler)
|
||||
ReadFullRecord = AuthorizationRoles.OpsAdminFull | AuthorizationRoles.OpsAdminLimited
|
||||
@@ -477,6 +486,38 @@ namespace AyaNova.Biz
|
||||
ReadFullRecord = AuthorizationRoles.All
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//BIZMETRICS
|
||||
//
|
||||
roles.Add(AyaType.BizMetrics, new BizRoleSet()
|
||||
{
|
||||
Change = AuthorizationRoles.BizAdminFull,
|
||||
ReadFullRecord = AuthorizationRoles.BizAdminLimited |
|
||||
AuthorizationRoles.SalesFull |
|
||||
AuthorizationRoles.SalesLimited |
|
||||
AuthorizationRoles.AccountingFull
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//NOTIFICATION
|
||||
//
|
||||
roles.Add(AyaType.Notification, new BizRoleSet()
|
||||
{
|
||||
Change = AuthorizationRoles.All,
|
||||
ReadFullRecord = AuthorizationRoles.All
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//NOTIFICATION_SUBSCRIPTION
|
||||
//
|
||||
roles.Add(AyaType.NotificationSubscription, new BizRoleSet()
|
||||
{
|
||||
Change = AuthorizationRoles.All,
|
||||
ReadFullRecord = AuthorizationRoles.All
|
||||
});
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#endregion all roles init
|
||||
|
||||
@@ -497,7 +538,7 @@ namespace AyaNova.Biz
|
||||
|
||||
//ONGOING VALIDATION TO CATCH MISMATCH WHEN NEW ROLES ADDED (wont' catch changes to existing unfortunately)
|
||||
//var lastRoles = "{\"User\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":131071},\"UserOptions\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"Widget\":{\"Change\":34,\"ReadFullRecord\":17,\"Select\":131071},\"ServerState\":{\"Change\":16384,\"ReadFullRecord\":131071,\"Select\":0},\"License\":{\"Change\":16386,\"ReadFullRecord\":8193,\"Select\":0},\"LogFile\":{\"Change\":0,\"ReadFullRecord\":24576,\"Select\":0},\"ServerJob\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"AyaNova7Import\":{\"Change\":16384,\"ReadFullRecord\":0,\"Select\":0},\"Metrics\":{\"Change\":0,\"ReadFullRecord\":24576,\"Select\":0},\"Translation\":{\"Change\":16386,\"ReadFullRecord\":131071,\"Select\":0},\"DataListView\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"FormCustom\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"PickListTemplate\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":131071}}";
|
||||
var lastRoles = "{\"Customer\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Contract\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"HeadOffice\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"LoanUnit\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Part\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PM\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PMItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PMTemplate\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PMTemplateItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Project\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PurchaseOrder\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Quote\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"QuoteItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"QuoteTemplate\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"QuoteTemplateItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Unit\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"UnitModel\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Vendor\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrder\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemExpense\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemLabor\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemLoan\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemPart\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemPartRequest\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemScheduledUser\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemTask\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemTravel\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemUnit\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderTemplate\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderTemplateItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Global\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"GlobalOps\":{\"Change\":16384,\"ReadFullRecord\":8192,\"Select\":0},\"User\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":131071},\"UserOptions\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"Widget\":{\"Change\":34,\"ReadFullRecord\":17,\"Select\":131071},\"ServerState\":{\"Change\":16384,\"ReadFullRecord\":131071,\"Select\":0},\"License\":{\"Change\":16386,\"ReadFullRecord\":8193,\"Select\":0},\"LogFile\":{\"Change\":0,\"ReadFullRecord\":24576,\"Select\":0},\"ServerJob\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"Metrics\":{\"Change\":0,\"ReadFullRecord\":24576,\"Select\":0},\"Translation\":{\"Change\":16386,\"ReadFullRecord\":131071,\"Select\":0},\"DataListView\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"FormCustom\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"PickListTemplate\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0}}";
|
||||
var lastRoles = "{\"Customer\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Contract\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"HeadOffice\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"LoanUnit\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Part\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PM\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PMItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PMTemplate\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PMTemplateItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Project\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"PurchaseOrder\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Quote\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"QuoteItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"QuoteTemplate\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"QuoteTemplateItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Unit\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"UnitModel\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Vendor\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrder\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemExpense\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemLabor\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemLoan\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemPart\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemPartRequest\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemScheduledUser\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemTask\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemTravel\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderItemUnit\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderTemplate\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"WorkOrderTemplateItem\":{\"Change\":33098,\"ReadFullRecord\":65669,\"Select\":131071},\"Global\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"GlobalOps\":{\"Change\":16384,\"ReadFullRecord\":8192,\"Select\":0},\"User\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":131071},\"UserOptions\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"Widget\":{\"Change\":34,\"ReadFullRecord\":17,\"Select\":131071},\"ServerState\":{\"Change\":16384,\"ReadFullRecord\":131071,\"Select\":0},\"License\":{\"Change\":16386,\"ReadFullRecord\":8193,\"Select\":0},\"LogFile\":{\"Change\":0,\"ReadFullRecord\":24576,\"Select\":0},\"ServerJob\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"ServerMetrics\":{\"Change\":0,\"ReadFullRecord\":24576,\"Select\":0},\"Translation\":{\"Change\":16386,\"ReadFullRecord\":131071,\"Select\":0},\"DataListView\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"FormCustom\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"PickListTemplate\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0}}";
|
||||
|
||||
|
||||
Dictionary<AyaType, BizRoleSet> lastRolesDeserialized = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<AyaType, BizRoleSet>>(lastRoles);
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace AyaNova.Biz
|
||||
UserId = currentUserId;
|
||||
UserTranslationId = userTranslationId;
|
||||
CurrentUserRoles = UserRoles;
|
||||
BizType = AyaType.GlobalOps;
|
||||
BizType = AyaType.Backup;
|
||||
}
|
||||
|
||||
internal static GlobalOpsBackupSettingsBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||
|
||||
@@ -1658,7 +1658,7 @@
|
||||
"ServerJob": "Serverjob",
|
||||
"AyaNova7Import": "AyaNova 7 importieren",
|
||||
"TrialSeeder": "Probesaatgutdaten",
|
||||
"Metrics": "Metrik",
|
||||
"BizMetrics": "Metrik",
|
||||
"UserOptions": "Benutzeroptionen",
|
||||
"FileAttachment": "Dateianhang",
|
||||
"FormCustom": "Formularanpassung",
|
||||
|
||||
@@ -1658,7 +1658,7 @@
|
||||
"ServerJob": "Server job",
|
||||
"AyaNova7Import": "AyaNova 7 import",
|
||||
"TrialSeeder": "Trial seed data",
|
||||
"Metrics": "Metrics",
|
||||
"BizMetrics": "Metrics",
|
||||
"UserOptions": "User options",
|
||||
"FileAttachment": "Attachment",
|
||||
"FormCustom": "Form customization",
|
||||
|
||||
@@ -1658,7 +1658,7 @@
|
||||
"ServerJob": "Trabajo del servidor",
|
||||
"AyaNova7Import": "Importación de AyaNova 7",
|
||||
"TrialSeeder": "Datos iniciales de prueba",
|
||||
"Metrics": "Métricas",
|
||||
"BizMetrics": "Métricas",
|
||||
"UserOptions": "Opciones de usuario",
|
||||
"FileAttachment": "Archivo adjunto",
|
||||
"FormCustom": "Personalización de formularios",
|
||||
|
||||
@@ -1658,7 +1658,7 @@
|
||||
"ServerJob": "Travail serveur",
|
||||
"AyaNova7Import": "Fichier d'importation AyaNova 7",
|
||||
"TrialSeeder": "Données sur les semences d'essai",
|
||||
"Metrics": "Indicateurs de performance",
|
||||
"BizMetrics": "Indicateurs de performance",
|
||||
"UserOptions": "Options utilisateur",
|
||||
"FileAttachment": "Pièce jointe",
|
||||
"FormCustom": "Personnalisation des formulaires",
|
||||
|
||||
Reference in New Issue
Block a user