Browse Source

UI: Refactor vc settings to repository settings. Add remove other entities confirm.

pull/6641/head
Igor Kulikov 4 years ago
parent
commit
09402f40ac
  1. 77
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  2. 8
      application/src/main/java/org/thingsboard/server/service/sync/vc/repository/DefaultTbRepositorySettingsService.java
  3. 2
      common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/RepositoryAuthMethod.java
  4. 6
      common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/RepositorySettings.java
  5. 14
      common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java
  6. 10
      ui-ngx/src/app/core/auth/auth.actions.ts
  7. 2
      ui-ngx/src/app/core/auth/auth.models.ts
  8. 4
      ui-ngx/src/app/core/auth/auth.reducer.ts
  9. 4
      ui-ngx/src/app/core/auth/auth.selectors.ts
  10. 10
      ui-ngx/src/app/core/auth/auth.service.ts
  11. 47
      ui-ngx/src/app/core/http/admin.service.ts
  12. 8
      ui-ngx/src/app/core/http/entities-version-control.service.ts
  13. 8
      ui-ngx/src/app/core/services/menu.service.ts
  14. 13
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  15. 13
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html
  16. 2
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts
  17. 28
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html
  18. 42
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts
  19. 3
      ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html
  20. 6
      ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts
  21. 3
      ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html
  22. 6
      ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts
  23. 41
      ui-ngx/src/app/modules/home/components/vc/remove-other-entities-confirm.component.html
  24. 66
      ui-ngx/src/app/modules/home/components/vc/remove-other-entities-confirm.component.ts
  25. 26
      ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html
  26. 2
      ui-ngx/src/app/modules/home/components/vc/repository-settings.component.scss
  27. 216
      ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts
  28. 218
      ui-ngx/src/app/modules/home/components/vc/version-control-settings.component.ts
  29. 6
      ui-ngx/src/app/modules/home/components/vc/version-control.component.html
  30. 10
      ui-ngx/src/app/modules/home/components/vc/version-control.component.ts
  31. 10
      ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts
  32. 4
      ui-ngx/src/app/modules/home/pages/admin/admin.module.ts
  33. 2
      ui-ngx/src/app/modules/home/pages/admin/repository-admin-settings.component.html
  34. 12
      ui-ngx/src/app/modules/home/pages/admin/repository-admin-settings.component.ts
  35. 2
      ui-ngx/src/app/shared/models/constants.ts
  36. 19
      ui-ngx/src/app/shared/models/settings.models.ts
  37. 23
      ui-ngx/src/app/shared/models/vc.models.ts
  38. 19
      ui-ngx/src/assets/locale/locale.constant-en_US.json

77
application/src/main/java/org/thingsboard/server/controller/AdminController.java

@ -191,12 +191,12 @@ public class AdminController extends BaseController {
} }
} }
@ApiOperation(value = "Get version control settings (getVersionControlSettings)", @ApiOperation(value = "Get repository settings (getRepositorySettings)",
notes = "Get the version control settings object. " + TENANT_AUTHORITY_PARAGRAPH) notes = "Get the repository settings object. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping("/vcSettings") @GetMapping("/repositorySettings")
@ResponseBody @ResponseBody
public RepositorySettings getVersionControlSettings() throws ThingsboardException { public RepositorySettings getRepositorySettings() throws ThingsboardException {
try { try {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
RepositorySettings versionControlSettings = checkNotNull(versionControlService.getVersionControlSettings(getTenantId())); RepositorySettings versionControlSettings = checkNotNull(versionControlService.getVersionControlSettings(getTenantId()));
@ -209,12 +209,12 @@ public class AdminController extends BaseController {
} }
} }
@ApiOperation(value = "Check version control settings exists (versionControlSettingsExists)", @ApiOperation(value = "Check repository settings exists (repositorySettingsExists)",
notes = "Check whether the version control settings exists. " + TENANT_AUTHORITY_PARAGRAPH) notes = "Check whether the repository settings exists. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping("/vcSettings/exists") @GetMapping("/repositorySettings/exists")
@ResponseBody @ResponseBody
public Boolean versionControlSettingsExists() throws ThingsboardException { public Boolean repositorySettingsExists() throws ThingsboardException {
try { try {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
return versionControlService.getVersionControlSettings(getTenantId()) != null; return versionControlService.getVersionControlSettings(getTenantId()) != null;
@ -223,11 +223,11 @@ public class AdminController extends BaseController {
} }
} }
@ApiOperation(value = "Creates or Updates the version control settings (saveVersionControlSettings)", @ApiOperation(value = "Creates or Updates the repository settings (saveRepositorySettings)",
notes = "Creates or Updates the version control settings object. " + TENANT_AUTHORITY_PARAGRAPH) notes = "Creates or Updates the repository settings object. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping("/vcSettings") @PostMapping("/repositorySettings")
public DeferredResult<RepositorySettings> saveVersionControlSettings(@RequestBody RepositorySettings settings) throws ThingsboardException { public DeferredResult<RepositorySettings> saveRepositorySettings(@RequestBody RepositorySettings settings) throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE);
ListenableFuture<RepositorySettings> future = versionControlService.saveVersionControlSettings(getTenantId(), settings); ListenableFuture<RepositorySettings> future = versionControlService.saveVersionControlSettings(getTenantId(), settings);
return wrapFuture(Futures.transform(future, savedSettings -> { return wrapFuture(Futures.transform(future, savedSettings -> {
@ -238,13 +238,13 @@ public class AdminController extends BaseController {
}, MoreExecutors.directExecutor())); }, MoreExecutors.directExecutor()));
} }
@ApiOperation(value = "Delete version control settings (deleteVersionControlSettings)", @ApiOperation(value = "Delete repository settings (deleteRepositorySettings)",
notes = "Deletes the version control settings." notes = "Deletes the repository settings."
+ TENANT_AUTHORITY_PARAGRAPH) + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/vcSettings", method = RequestMethod.DELETE) @RequestMapping(value = "/repositorySettings", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)
public DeferredResult<Void> deleteVersionControlSettings() throws ThingsboardException { public DeferredResult<Void> deleteRepositorySettings() throws ThingsboardException {
try { try {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.DELETE); accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.DELETE);
return wrapFuture(versionControlService.deleteVersionControlSettings(getTenantId())); return wrapFuture(versionControlService.deleteVersionControlSettings(getTenantId()));
@ -253,6 +253,23 @@ public class AdminController extends BaseController {
} }
} }
@ApiOperation(value = "Check repository access (checkRepositoryAccess)",
notes = "Attempts to check repository access. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/repositorySettings/checkAccess", method = RequestMethod.POST)
public DeferredResult<Void> checkRepositoryAccess(
@ApiParam(value = "A JSON value representing the Repository Settings.")
@RequestBody RepositorySettings settings) throws ThingsboardException {
try {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
settings = checkNotNull(settings);
return wrapFuture(versionControlService.checkVersionControlAccess(getTenantId(), settings));
} catch (Exception e) {
throw handleException(e);
}
}
@ApiOperation(value = "Get auto commit settings (getAutoCommitSettings)", @ApiOperation(value = "Get auto commit settings (getAutoCommitSettings)",
notes = "Get the auto commit settings object. " + TENANT_AUTHORITY_PARAGRAPH) notes = "Get the auto commit settings object. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@ -267,8 +284,8 @@ public class AdminController extends BaseController {
} }
} }
@ApiOperation(value = "Check version control settings exists (versionControlSettingsExists)", @ApiOperation(value = "Check auto commit settings exists (autoCommitSettingsExists)",
notes = "Check whether the version control settings exists. " + TENANT_AUTHORITY_PARAGRAPH) notes = "Check whether the auto commit settings exists. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping("/vc/autoCommitSettings/exists") @GetMapping("/vc/autoCommitSettings/exists")
@ResponseBody @ResponseBody
@ -281,8 +298,8 @@ public class AdminController extends BaseController {
} }
} }
@ApiOperation(value = "Creates or Updates the version control settings (saveVersionControlSettings)", @ApiOperation(value = "Creates or Updates the auto commit settings (saveAutoCommitSettings)",
notes = "Creates or Updates the version control settings object. " + TENANT_AUTHORITY_PARAGRAPH) notes = "Creates or Updates the auto commit settings object. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping("/vc/autoCommitSettings") @PostMapping("/vc/autoCommitSettings")
public AutoCommitSettings saveAutoCommitSettings(@RequestBody AutoCommitSettings settings) throws ThingsboardException { public AutoCommitSettings saveAutoCommitSettings(@RequestBody AutoCommitSettings settings) throws ThingsboardException {
@ -290,8 +307,8 @@ public class AdminController extends BaseController {
return autoCommitSettingsService.save(getTenantId(), settings); return autoCommitSettingsService.save(getTenantId(), settings);
} }
@ApiOperation(value = "Delete version control settings (deleteVersionControlSettings)", @ApiOperation(value = "Delete auto commit settings (deleteAutoCommitSettings)",
notes = "Deletes the version control settings." notes = "Deletes the auto commit settings."
+ TENANT_AUTHORITY_PARAGRAPH) + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/vc/autoCommitSettings", method = RequestMethod.DELETE) @RequestMapping(value = "/vc/autoCommitSettings", method = RequestMethod.DELETE)
@ -305,22 +322,6 @@ public class AdminController extends BaseController {
} }
} }
@ApiOperation(value = "Check version control access (checkVersionControlAccess)",
notes = "Attempts to check version control access. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/vcSettings/checkAccess", method = RequestMethod.POST)
public DeferredResult<Void> checkVersionControlAccess(
@ApiParam(value = "A JSON value representing the Entities Version Control Settings.")
@RequestBody RepositorySettings settings) throws ThingsboardException {
try {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
settings = checkNotNull(settings);
return wrapFuture(versionControlService.checkVersionControlAccess(getTenantId(), settings));
} catch (Exception e) {
throw handleException(e);
}
}
@ApiOperation(value = "Check for new Platform Releases (checkUpdates)", @ApiOperation(value = "Check for new Platform Releases (checkUpdates)",
notes = "Check notifications about new platform releases. " notes = "Check notifications about new platform releases. "
+ SYSTEM_AUTHORITY_PARAGRAPH) + SYSTEM_AUTHORITY_PARAGRAPH)

8
application/src/main/java/org/thingsboard/server/service/sync/vc/repository/DefaultTbRepositorySettingsService.java

@ -19,7 +19,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.cache.TbTransactionalCache;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.sync.vc.RepositorySettings; import org.thingsboard.server.common.data.sync.vc.RepositorySettings;
import org.thingsboard.server.common.data.sync.vc.VersionControlAuthMethod; import org.thingsboard.server.common.data.sync.vc.RepositoryAuthMethod;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.sync.vc.TbAbstractVersionControlSettingsService; import org.thingsboard.server.service.sync.vc.TbAbstractVersionControlSettingsService;
@ -38,10 +38,10 @@ public class DefaultTbRepositorySettingsService extends TbAbstractVersionControl
public RepositorySettings restore(TenantId tenantId, RepositorySettings settings) { public RepositorySettings restore(TenantId tenantId, RepositorySettings settings) {
RepositorySettings storedSettings = get(tenantId); RepositorySettings storedSettings = get(tenantId);
if (storedSettings != null) { if (storedSettings != null) {
VersionControlAuthMethod authMethod = settings.getAuthMethod(); RepositoryAuthMethod authMethod = settings.getAuthMethod();
if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(authMethod) && settings.getPassword() == null) { if (RepositoryAuthMethod.USERNAME_PASSWORD.equals(authMethod) && settings.getPassword() == null) {
settings.setPassword(storedSettings.getPassword()); settings.setPassword(storedSettings.getPassword());
} else if (VersionControlAuthMethod.PRIVATE_KEY.equals(authMethod) && settings.getPrivateKey() == null) { } else if (RepositoryAuthMethod.PRIVATE_KEY.equals(authMethod) && settings.getPrivateKey() == null) {
settings.setPrivateKey(storedSettings.getPrivateKey()); settings.setPrivateKey(storedSettings.getPrivateKey());
if (settings.getPrivateKeyPassword() == null) { if (settings.getPrivateKeyPassword() == null) {
settings.setPrivateKeyPassword(storedSettings.getPrivateKeyPassword()); settings.setPrivateKeyPassword(storedSettings.getPrivateKeyPassword());

2
common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/VersionControlAuthMethod.java → common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/RepositoryAuthMethod.java

@ -15,7 +15,7 @@
*/ */
package org.thingsboard.server.common.data.sync.vc; package org.thingsboard.server.common.data.sync.vc;
public enum VersionControlAuthMethod { public enum RepositoryAuthMethod {
USERNAME_PASSWORD, USERNAME_PASSWORD,
PRIVATE_KEY PRIVATE_KEY
} }

6
common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/RepositorySettings.java

@ -17,12 +17,8 @@ package org.thingsboard.server.common.data.sync.vc;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.sync.vc.request.create.AutoVersionCreateConfig;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
@Data @Data
@JsonIgnoreProperties(ignoreUnknown = true) // temporary to make sure no need to wipe db during development. @JsonIgnoreProperties(ignoreUnknown = true) // temporary to make sure no need to wipe db during development.
@ -30,7 +26,7 @@ public class RepositorySettings implements Serializable {
private static final long serialVersionUID = -3211552851889198721L; private static final long serialVersionUID = -3211552851889198721L;
private String repositoryUri; private String repositoryUri;
private VersionControlAuthMethod authMethod; private RepositoryAuthMethod authMethod;
private String username; private String username;
private String password; private String password;
private String privateKeyFileName; private String privateKeyFileName;

14
common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java

@ -59,7 +59,7 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.sync.vc.RepositorySettings; import org.thingsboard.server.common.data.sync.vc.RepositorySettings;
import org.thingsboard.server.common.data.sync.vc.VersionControlAuthMethod; import org.thingsboard.server.common.data.sync.vc.RepositoryAuthMethod;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -95,9 +95,9 @@ public class GitRepository {
public static GitRepository clone(RepositorySettings settings, File directory) throws GitAPIException { public static GitRepository clone(RepositorySettings settings, File directory) throws GitAPIException {
CredentialsProvider credentialsProvider = null; CredentialsProvider credentialsProvider = null;
SshdSessionFactory sshSessionFactory = null; SshdSessionFactory sshSessionFactory = null;
if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) { if (RepositoryAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) {
credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword()); credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword());
} else if (VersionControlAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) { } else if (RepositoryAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) {
sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory); sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory);
} }
CloneCommand cloneCommand = Git.cloneRepository() CloneCommand cloneCommand = Git.cloneRepository()
@ -113,9 +113,9 @@ public class GitRepository {
Git git = Git.open(directory); Git git = Git.open(directory);
CredentialsProvider credentialsProvider = null; CredentialsProvider credentialsProvider = null;
SshdSessionFactory sshSessionFactory = null; SshdSessionFactory sshSessionFactory = null;
if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) { if (RepositoryAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) {
credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword()); credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword());
} else if (VersionControlAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) { } else if (RepositoryAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) {
sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory); sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory);
} }
return new GitRepository(git, settings, credentialsProvider, sshSessionFactory, directory.getAbsolutePath()); return new GitRepository(git, settings, credentialsProvider, sshSessionFactory, directory.getAbsolutePath());
@ -124,9 +124,9 @@ public class GitRepository {
public static void test(RepositorySettings settings, File directory) throws GitAPIException { public static void test(RepositorySettings settings, File directory) throws GitAPIException {
CredentialsProvider credentialsProvider = null; CredentialsProvider credentialsProvider = null;
SshdSessionFactory sshSessionFactory = null; SshdSessionFactory sshSessionFactory = null;
if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) { if (RepositoryAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) {
credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword()); credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword());
} else if (VersionControlAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) { } else if (RepositoryAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) {
sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory); sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory);
} }
LsRemoteCommand lsRemoteCommand = Git.lsRemoteRepository().setRemote(settings.getRepositoryUri()); LsRemoteCommand lsRemoteCommand = Git.lsRemoteRepository().setRemote(settings.getRepositoryUri());

10
ui-ngx/src/app/core/auth/auth.actions.ts

@ -24,7 +24,7 @@ export enum AuthActionTypes {
LOAD_USER = '[Auth] Load User', LOAD_USER = '[Auth] Load User',
UPDATE_USER_DETAILS = '[Auth] Update User Details', UPDATE_USER_DETAILS = '[Auth] Update User Details',
UPDATE_LAST_PUBLIC_DASHBOARD_ID = '[Auth] Update Last Public Dashboard Id', UPDATE_LAST_PUBLIC_DASHBOARD_ID = '[Auth] Update Last Public Dashboard Id',
UPDATE_HAS_VERSION_CONTROL = '[Auth] Change Has Version Control' UPDATE_HAS_REPOSITORY = '[Auth] Change Has Repository'
} }
export class ActionAuthAuthenticated implements Action { export class ActionAuthAuthenticated implements Action {
@ -55,11 +55,11 @@ export class ActionAuthUpdateLastPublicDashboardId implements Action {
constructor(readonly payload: { lastPublicDashboardId: string }) {} constructor(readonly payload: { lastPublicDashboardId: string }) {}
} }
export class ActionAuthUpdateHasVersionControl implements Action { export class ActionAuthUpdateHasRepository implements Action {
readonly type = AuthActionTypes.UPDATE_HAS_VERSION_CONTROL; readonly type = AuthActionTypes.UPDATE_HAS_REPOSITORY;
constructor(readonly payload: { hasVersionControl: boolean }) {} constructor(readonly payload: { hasRepository: boolean }) {}
} }
export type AuthActions = ActionAuthAuthenticated | ActionAuthUnauthenticated | export type AuthActions = ActionAuthAuthenticated | ActionAuthUnauthenticated |
ActionAuthLoadUser | ActionAuthUpdateUserDetails | ActionAuthUpdateLastPublicDashboardId | ActionAuthUpdateHasVersionControl; ActionAuthLoadUser | ActionAuthUpdateUserDetails | ActionAuthUpdateLastPublicDashboardId | ActionAuthUpdateHasRepository;

2
ui-ngx/src/app/core/auth/auth.models.ts

@ -20,7 +20,7 @@ export interface SysParamsState {
userTokenAccessEnabled: boolean; userTokenAccessEnabled: boolean;
allowedDashboardIds: string[]; allowedDashboardIds: string[];
edgesSupportEnabled: boolean; edgesSupportEnabled: boolean;
hasVersionControl: boolean; hasRepository: boolean;
} }
export interface AuthPayload extends SysParamsState { export interface AuthPayload extends SysParamsState {

4
ui-ngx/src/app/core/auth/auth.reducer.ts

@ -24,7 +24,7 @@ const emptyUserAuthState: AuthPayload = {
forceFullscreen: false, forceFullscreen: false,
allowedDashboardIds: [], allowedDashboardIds: [],
edgesSupportEnabled: false, edgesSupportEnabled: false,
hasVersionControl: false hasRepository: false
}; };
export const initialState: AuthState = { export const initialState: AuthState = {
@ -55,7 +55,7 @@ export function authReducer(
case AuthActionTypes.UPDATE_LAST_PUBLIC_DASHBOARD_ID: case AuthActionTypes.UPDATE_LAST_PUBLIC_DASHBOARD_ID:
return { ...state, ...action.payload}; return { ...state, ...action.payload};
case AuthActionTypes.UPDATE_HAS_VERSION_CONTROL: case AuthActionTypes.UPDATE_HAS_REPOSITORY:
return { ...state, ...action.payload}; return { ...state, ...action.payload};
default: default:

4
ui-ngx/src/app/core/auth/auth.selectors.ts

@ -55,9 +55,9 @@ export const selectUserTokenAccessEnabled = createSelector(
(state: AuthState) => state.userTokenAccessEnabled (state: AuthState) => state.userTokenAccessEnabled
); );
export const selectHasVersionControl = createSelector( export const selectHasRepository = createSelector(
selectAuthState, selectAuthState,
(state: AuthState) => state.hasVersionControl (state: AuthState) => state.hasRepository
); );
export function getCurrentAuthState(store: Store<AppState>): AuthState { export function getCurrentAuthState(store: Store<AppState>): AuthState {

10
ui-ngx/src/app/core/auth/auth.service.ts

@ -437,9 +437,9 @@ export class AuthService {
return this.http.get<boolean>('/api/edges/enabled', defaultHttpOptions()); return this.http.get<boolean>('/api/edges/enabled', defaultHttpOptions());
} }
private loadHasVersionControl(authUser: AuthUser): Observable<boolean> { private loadHasRepository(authUser: AuthUser): Observable<boolean> {
if (authUser.authority === Authority.TENANT_ADMIN) { if (authUser.authority === Authority.TENANT_ADMIN) {
return this.http.get<boolean>('/api/admin/vcSettings/exists', defaultHttpOptions()); return this.http.get<boolean>('/api/admin/repositorySettings/exists', defaultHttpOptions());
} else { } else {
return of(false); return of(false);
} }
@ -449,15 +449,15 @@ export class AuthService {
const sources = [this.loadIsUserTokenAccessEnabled(authPayload.authUser), const sources = [this.loadIsUserTokenAccessEnabled(authPayload.authUser),
this.fetchAllowedDashboardIds(authPayload), this.fetchAllowedDashboardIds(authPayload),
this.loadIsEdgesSupportEnabled(), this.loadIsEdgesSupportEnabled(),
this.loadHasVersionControl(authPayload.authUser), this.loadHasRepository(authPayload.authUser),
this.timeService.loadMaxDatapointsLimit()]; this.timeService.loadMaxDatapointsLimit()];
return forkJoin(sources) return forkJoin(sources)
.pipe(map((data) => { .pipe(map((data) => {
const userTokenAccessEnabled: boolean = data[0] as boolean; const userTokenAccessEnabled: boolean = data[0] as boolean;
const allowedDashboardIds: string[] = data[1] as string[]; const allowedDashboardIds: string[] = data[1] as string[];
const edgesSupportEnabled: boolean = data[2] as boolean; const edgesSupportEnabled: boolean = data[2] as boolean;
const hasVersionControl: boolean = data[3] as boolean; const hasRepository: boolean = data[3] as boolean;
return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasVersionControl}; return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasRepository};
}, catchError((err) => { }, catchError((err) => {
return of({}); return of({});
}))); })));

47
ui-ngx/src/app/core/http/admin.service.ts

@ -15,19 +15,21 @@
/// ///
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { defaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { import {
AdminSettings, AdminSettings,
EntitiesVersionControlSettings, RepositorySettings,
MailServerSettings, MailServerSettings,
SecuritySettings, SecuritySettings,
TestSmsRequest, TestSmsRequest,
UpdateMessage UpdateMessage, AutoCommitSettings
} from '@shared/models/settings.models'; } from '@shared/models/settings.models';
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service'; import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { AuthUser } from '@shared/models/user.model';
import { Authority } from '@shared/models/authority.enum';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -68,13 +70,13 @@ export class AdminService {
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }
public getEntitiesVersionControlSettings(config?: RequestConfig): Observable<EntitiesVersionControlSettings> { public getRepositorySettings(config?: RequestConfig): Observable<RepositorySettings> {
return this.http.get<EntitiesVersionControlSettings>(`/api/admin/vcSettings`, defaultHttpOptionsFromConfig(config)); return this.http.get<RepositorySettings>(`/api/admin/repositorySettings`, defaultHttpOptionsFromConfig(config));
} }
public saveEntitiesVersionControlSettings(versionControlSettings: EntitiesVersionControlSettings, public saveRepositorySettings(repositorySettings: RepositorySettings,
config?: RequestConfig): Observable<EntitiesVersionControlSettings> { config?: RequestConfig): Observable<RepositorySettings> {
return this.http.post<EntitiesVersionControlSettings>('/api/admin/vcSettings', versionControlSettings, return this.http.post<RepositorySettings>('/api/admin/repositorySettings', repositorySettings,
defaultHttpOptionsFromConfig(config)).pipe( defaultHttpOptionsFromConfig(config)).pipe(
tap(() => { tap(() => {
this.entitiesVersionControlService.clearBranchList(); this.entitiesVersionControlService.clearBranchList();
@ -82,17 +84,34 @@ export class AdminService {
); );
} }
public deleteEntitiesVersionControlSettings(config?: RequestConfig) { public deleteRepositorySettings(config?: RequestConfig) {
return this.http.delete('/api/admin/vcSettings', defaultHttpOptionsFromConfig(config)).pipe( return this.http.delete('/api/admin/repositorySettings', defaultHttpOptionsFromConfig(config)).pipe(
tap(() => { tap(() => {
this.entitiesVersionControlService.clearBranchList(); this.entitiesVersionControlService.clearBranchList();
}) })
); );
} }
public checkVersionControlAccess(versionControlSettings: EntitiesVersionControlSettings, public checkRepositoryAccess(repositorySettings: RepositorySettings,
config?: RequestConfig): Observable<void> { config?: RequestConfig): Observable<void> {
return this.http.post<void>('/api/admin/vcSettings/checkAccess', versionControlSettings, defaultHttpOptionsFromConfig(config)); return this.http.post<void>('/api/admin/repositorySettings/checkAccess', repositorySettings, defaultHttpOptionsFromConfig(config));
}
public getAutoCommitSettings(config?: RequestConfig): Observable<AutoCommitSettings> {
return this.http.get<AutoCommitSettings>(`/api/admin/autoCommitSettings`, defaultHttpOptionsFromConfig(config));
}
private autoCommitSettingsExists(config?: RequestConfig): Observable<boolean> {
return this.http.get<boolean>('/api/admin/autoCommitSettings/exists', defaultHttpOptionsFromConfig(config));
}
public saveAutoCommitSettings(autoCommitSettings: AutoCommitSettings,
config?: RequestConfig): Observable<AutoCommitSettings> {
return this.http.post<AutoCommitSettings>('/api/admin/autoCommitSettings', autoCommitSettings, defaultHttpOptionsFromConfig(config));
}
public deleteAutoCommitSettings(config?: RequestConfig) {
return this.http.delete('/api/admin/autoCommitSettings', defaultHttpOptionsFromConfig(config));
} }
public checkUpdates(config?: RequestConfig): Observable<UpdateMessage> { public checkUpdates(config?: RequestConfig): Observable<UpdateMessage> {

8
ui-ngx/src/app/core/http/entities-version-control.service.ts

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
import { combineLatest, Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { import {
BranchInfo, EntityDataDiff, BranchInfo, EntityDataDiff,
EntityVersion, EntityVersion,
@ -29,10 +29,10 @@ import { PageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { createSelector, select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { selectHasVersionControl, selectIsAuthenticated, selectIsUserLoaded } from '@core/auth/auth.selectors'; import { selectIsUserLoaded } from '@core/auth/auth.selectors';
import { catchError, combineAll, tap } from 'rxjs/operators'; import { catchError, tap } from 'rxjs/operators';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'

8
ui-ngx/src/app/core/services/menu.service.ts

@ -388,9 +388,9 @@ export class MenuService {
}, },
{ {
id: guid(), id: guid(),
name: 'admin.git-settings', name: 'admin.repository-settings',
type: 'link', type: 'link',
path: '/settings/vc', path: '/settings/repository',
icon: 'manage_history' icon: 'manage_history'
} }
] ]
@ -538,9 +538,9 @@ export class MenuService {
path: '/settings/resources-library' path: '/settings/resources-library'
}, },
{ {
name: 'admin.git-settings', name: 'admin.repository-settings',
icon: 'manage_history', icon: 'manage_history',
path: '/settings/vc', path: '/settings/repository',
} }
] ]
} }

13
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -153,7 +153,7 @@ import { TenantProfileQueuesComponent } from '@home/components/profile/queue/ten
import { QueueFormComponent } from '@home/components/queue/queue-form.component'; import { QueueFormComponent } from '@home/components/queue/queue-form.component';
import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module'; import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module';
import { WidgetSettingsComponent } from '@home/components/widget/widget-settings.component'; import { WidgetSettingsComponent } from '@home/components/widget/widget-settings.component';
import { VersionControlSettingsComponent } from '@home/components/vc/version-control-settings.component'; import { RepositorySettingsComponent } from '@home/components/vc/repository-settings.component';
import { VersionControlComponent } from '@home/components/vc/version-control.component'; import { VersionControlComponent } from '@home/components/vc/version-control.component';
import { EntityVersionsTableComponent } from '@home/components/vc/entity-versions-table.component'; import { EntityVersionsTableComponent } from '@home/components/vc/entity-versions-table.component';
import { EntityVersionCreateComponent } from '@home/components/vc/entity-version-create.component'; import { EntityVersionCreateComponent } from '@home/components/vc/entity-version-create.component';
@ -163,6 +163,7 @@ import { ComplexVersionCreateComponent } from '@home/components/vc/complex-versi
import { EntityTypesVersionCreateComponent } from '@home/components/vc/entity-types-version-create.component'; import { EntityTypesVersionCreateComponent } from '@home/components/vc/entity-types-version-create.component';
import { EntityTypesVersionLoadComponent } from '@home/components/vc/entity-types-version-load.component'; import { EntityTypesVersionLoadComponent } from '@home/components/vc/entity-types-version-load.component';
import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version-load.component'; import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version-load.component';
import { RemoveOtherEntitiesConfirmComponent } from '@home/components/vc/remove-other-entities-confirm.component';
@NgModule({ @NgModule({
declarations: declarations:
@ -287,7 +288,7 @@ import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version
DisplayWidgetTypesPanelComponent, DisplayWidgetTypesPanelComponent,
TenantProfileQueuesComponent, TenantProfileQueuesComponent,
QueueFormComponent, QueueFormComponent,
VersionControlSettingsComponent, RepositorySettingsComponent,
VersionControlComponent, VersionControlComponent,
EntityVersionsTableComponent, EntityVersionsTableComponent,
EntityVersionCreateComponent, EntityVersionCreateComponent,
@ -296,7 +297,8 @@ import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version
ComplexVersionCreateComponent, ComplexVersionCreateComponent,
EntityTypesVersionCreateComponent, EntityTypesVersionCreateComponent,
EntityTypesVersionLoadComponent, EntityTypesVersionLoadComponent,
ComplexVersionLoadComponent ComplexVersionLoadComponent,
RemoveOtherEntitiesConfirmComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -415,7 +417,7 @@ import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version
DisplayWidgetTypesPanelComponent, DisplayWidgetTypesPanelComponent,
TenantProfileQueuesComponent, TenantProfileQueuesComponent,
QueueFormComponent, QueueFormComponent,
VersionControlSettingsComponent, RepositorySettingsComponent,
VersionControlComponent, VersionControlComponent,
EntityVersionsTableComponent, EntityVersionsTableComponent,
EntityVersionCreateComponent, EntityVersionCreateComponent,
@ -424,7 +426,8 @@ import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version
ComplexVersionCreateComponent, ComplexVersionCreateComponent,
EntityTypesVersionCreateComponent, EntityTypesVersionCreateComponent,
EntityTypesVersionLoadComponent, EntityTypesVersionLoadComponent,
ComplexVersionLoadComponent ComplexVersionLoadComponent,
RemoveOtherEntitiesConfirmComponent
], ],
providers: [ providers: [
WidgetComponentService, WidgetComponentService,

13
ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html

@ -53,7 +53,7 @@
[allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)"> [allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)">
</tb-entity-type-select> </tb-entity-type-select>
<div fxFlex fxLayout="row" fxLayoutGap="16px" fxLayoutAlign="start center" formGroupName="config"> <div fxFlex fxLayout="row" fxLayoutGap="16px" fxLayoutAlign="start center" formGroupName="config">
<mat-form-field class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>version-control.sync-strategy</mat-label> <mat-label translate>version-control.sync-strategy</mat-label>
<mat-select formControlName="syncStrategy"> <mat-select formControlName="syncStrategy">
<mat-option [value]="'default'"> <mat-option [value]="'default'">
@ -64,9 +64,14 @@
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox formControlName="saveRelations"> <div fxFlex fxLayout="column" fxLayoutGap="8px">
{{ 'version-control.export-entity-relations' | translate }} <mat-checkbox formControlName="saveRelations">
</mat-checkbox> {{ 'version-control.export-entity-relations' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="saveAttributes">
{{ 'version-control.export-entity-attributes' | translate }}
</mat-checkbox>
</div>
</div> </div>
</div> </div>
<div fxLayout="row" fxLayoutGap="16px" fxLayoutAlign="start center" <div fxLayout="row" fxLayoutGap="16px" fxLayoutAlign="start center"

2
ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts

@ -135,6 +135,7 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements
config: this.fb.group({ config: this.fb.group({
syncStrategy: [config.syncStrategy === null ? 'default' : config.syncStrategy, []], syncStrategy: [config.syncStrategy === null ? 'default' : config.syncStrategy, []],
saveRelations: [config.saveRelations, []], saveRelations: [config.saveRelations, []],
saveAttributes: [config.saveAttributes, []],
allEntities: [config.allEntities, []], allEntities: [config.allEntities, []],
entityIds: [config.entityIds, [Validators.required]] entityIds: [config.entityIds, [Validators.required]]
}) })
@ -183,6 +184,7 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements
const config: EntityTypeVersionCreateConfig = { const config: EntityTypeVersionCreateConfig = {
syncStrategy: null, syncStrategy: null,
saveRelations: false, saveRelations: false,
saveAttributes: false,
allEntities: true, allEntities: true,
entityIds: [] entityIds: []
}; };

28
ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html

@ -44,7 +44,7 @@
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<div class="entity-type-config-content" fxLayout="column" fxLayoutGap="0.5em"> <div class="entity-type-config-content" fxLayout="column" fxLayoutGap="0.5em">
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div fxLayout="row" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutGap="32px">
<tb-entity-type-select <tb-entity-type-select
showLabel showLabel
formControlName="entityType" formControlName="entityType"
@ -52,13 +52,25 @@
[filterAllowedEntityTypes]="false" [filterAllowedEntityTypes]="false"
[allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)"> [allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)">
</tb-entity-type-select> </tb-entity-type-select>
<div fxFlex fxLayout="row" fxLayoutGap="16px" fxLayoutAlign="start center" formGroupName="config"> <div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px" formGroupName="config">
<mat-checkbox formControlName="removeOtherEntities"> <div fxFlex fxLayout="column" fxLayoutGap="8px">
{{ 'version-control.remove-other-entities' | translate }} <mat-checkbox #removeOtherEntitiesCheckbox
</mat-checkbox> formControlName="removeOtherEntities"
<mat-checkbox formControlName="loadRelations"> (click)="onRemoveOtherEntities(removeOtherEntitiesCheckbox, entityTypeFormGroup, $event)">
{{ 'version-control.load-entities-relations' | translate }} {{ 'version-control.remove-other-entities' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox formControlName="findExistingEntityByName">
{{ 'version-control.find-existing-entity-by-name' | translate }}
</mat-checkbox>
</div>
<div fxFlex fxLayout="column" fxLayoutGap="8px">
<mat-checkbox formControlName="loadRelations">
{{ 'version-control.load-entities-relations' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="loadAttributes">
{{ 'version-control.load-entities-attributes' | translate }}
</mat-checkbox>
</div>
</div> </div>
</div> </div>
</div> </div>

42
ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -28,11 +28,15 @@ import {
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { EntityTypeVersionLoadConfig, exportableEntityTypes } from '@shared/models/vc.models'; import { EntityTypeVersionLoadConfig, exportableEntityTypes, VersionCreationResult } from '@shared/models/vc.models';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import { MatCheckbox } from '@angular/material/checkbox/checkbox';
import { TbPopoverService } from '@shared/components/popover.service';
import { EntityVersionCreateComponent } from '@home/components/vc/entity-version-create.component';
import { RemoveOtherEntitiesConfirmComponent } from '@home/components/vc/remove-other-entities-confirm.component';
@Component({ @Component({
selector: 'tb-entity-types-version-load', selector: 'tb-entity-types-version-load',
@ -64,6 +68,9 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store); super(store);
} }
@ -124,7 +131,9 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
entityType: [entityType, [Validators.required]], entityType: [entityType, [Validators.required]],
config: this.fb.group({ config: this.fb.group({
loadRelations: [config.loadRelations, []], loadRelations: [config.loadRelations, []],
removeOtherEntities: [config.removeOtherEntities, []] loadAttributes: [config.loadAttributes, []],
removeOtherEntities: [config.removeOtherEntities, []],
findExistingEntityByName: [config.findExistingEntityByName, []]
}) })
} }
); );
@ -156,7 +165,9 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
const entityTypesArray = this.entityTypesVersionLoadFormGroup.get('entityTypes') as FormArray; const entityTypesArray = this.entityTypesVersionLoadFormGroup.get('entityTypes') as FormArray;
const config: EntityTypeVersionLoadConfig = { const config: EntityTypeVersionLoadConfig = {
loadRelations: false, loadRelations: false,
removeOtherEntities: false loadAttributes: false,
removeOtherEntities: false,
findExistingEntityByName: false
}; };
const allowed = this.allowedEntityTypes(); const allowed = this.allowedEntityTypes();
let entityType: EntityType = null; let entityType: EntityType = null;
@ -194,6 +205,29 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
return res; return res;
} }
onRemoveOtherEntities(removeOtherEntitiesCheckbox: MatCheckbox, entityTypeControl: AbstractControl, $event: Event) {
const removeOtherEntities: boolean = entityTypeControl.get('config.removeOtherEntities').value;
if (!removeOtherEntities) {
$event.preventDefault();
$event.stopPropagation();
const trigger = $('.mat-checkbox-frame', removeOtherEntitiesCheckbox._elementRef.nativeElement)[0];
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const removeOtherEntitiesConfirmPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, RemoveOtherEntitiesConfirmComponent, 'bottom', true, null,
{
onClose: (result: boolean | null) => {
removeOtherEntitiesConfirmPopover.hide();
if (result) {
entityTypeControl.get('config').get('removeOtherEntities').patchValue(true, {emitEvent: true});
}
}
}, {}, {}, {}, false);
}
}
}
private updateModel() { private updateModel() {
const value: [{entityType: string, config: EntityTypeVersionLoadConfig}] = const value: [{entityType: string, config: EntityTypeVersionLoadConfig}] =
this.entityTypesVersionLoadFormGroup.get('entityTypes').value || []; this.entityTypesVersionLoadFormGroup.get('entityTypes').value || [];

3
ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html

@ -41,6 +41,9 @@
<mat-checkbox formControlName="saveRelations" style="margin-bottom: 16px;"> <mat-checkbox formControlName="saveRelations" style="margin-bottom: 16px;">
{{ 'version-control.export-entity-relations' | translate }} {{ 'version-control.export-entity-relations' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox formControlName="saveAttributes" style="margin-bottom: 16px;">
{{ 'version-control.export-entity-attributes' | translate }}
</mat-checkbox>
</div> </div>
</fieldset> </fieldset>
</form> </form>

6
ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts

@ -62,7 +62,8 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
this.createVersionFormGroup = this.fb.group({ this.createVersionFormGroup = this.fb.group({
branch: [this.branch, [Validators.required]], branch: [this.branch, [Validators.required]],
versionName: [null, [Validators.required]], versionName: [null, [Validators.required]],
saveRelations: [false, []] saveRelations: [false, []],
saveAttributes: [false, []]
}); });
} }
@ -78,7 +79,8 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
branch: this.createVersionFormGroup.get('branch').value, branch: this.createVersionFormGroup.get('branch').value,
versionName: this.createVersionFormGroup.get('versionName').value, versionName: this.createVersionFormGroup.get('versionName').value,
config: { config: {
saveRelations: this.createVersionFormGroup.get('saveRelations').value saveRelations: this.createVersionFormGroup.get('saveRelations').value,
saveAttributes: this.createVersionFormGroup.get('saveAttributes').value,
}, },
type: VersionCreateRequestType.SINGLE_ENTITY type: VersionCreateRequestType.SINGLE_ENTITY
}; };

3
ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html

@ -29,6 +29,9 @@
<mat-checkbox formControlName="loadRelations" style="margin-bottom: 16px;"> <mat-checkbox formControlName="loadRelations" style="margin-bottom: 16px;">
{{ 'version-control.load-entity-relations' | translate }} {{ 'version-control.load-entity-relations' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox formControlName="loadAttributes" style="margin-bottom: 16px;">
{{ 'version-control.load-entity-attributes' | translate }}
</mat-checkbox>
</div> </div>
</fieldset> </fieldset>
</form> </form>

6
ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts

@ -57,7 +57,8 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
ngOnInit(): void { ngOnInit(): void {
this.restoreFormGroup = this.fb.group({ this.restoreFormGroup = this.fb.group({
loadRelations: [false, []] loadRelations: [false, []],
loadAttributes: [false, []]
}); });
} }
@ -73,7 +74,8 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
versionId: this.versionId, versionId: this.versionId,
externalEntityId: this.externalEntityId, externalEntityId: this.externalEntityId,
config: { config: {
loadRelations: this.restoreFormGroup.get('loadRelations').value loadRelations: this.restoreFormGroup.get('loadRelations').value,
loadAttributes: this.restoreFormGroup.get('loadAttributes').value
}, },
type: VersionLoadRequestType.SINGLE_ENTITY type: VersionLoadRequestType.SINGLE_ENTITY
}; };

41
ui-ngx/src/app/modules/home/components/vc/remove-other-entities-confirm.component.html

@ -0,0 +1,41 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section style="min-width: 300px;">
<form [formGroup]="confirmFormGroup" style="padding-top: 16px;">
<fieldset [disabled]="(isLoading$ | async)">
<div style="user-select: none;" [innerHTML]="removeOtherEntitiesConfirmText"></div>
<mat-form-field class="mat-block">
<input matInput formControlName="verification"/>
</mat-form-field>
</fieldset>
</form>
<div fxLayoutAlign="end center" fxLayoutGap="8px">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button color="primary"
type="button"
(click)="confirm()"
[disabled]="(isLoading$ | async) || confirmFormGroup.invalid || confirmFormGroup.get('verification').value !== removeOtherEntitiesVerificationText">
{{ 'action.confirm' | translate }}
</button>
</div>
</section>

66
ui-ngx/src/app/modules/home/components/vc/remove-other-entities-confirm.component.ts

@ -0,0 +1,66 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'tb-remove-other-entities-confirm',
templateUrl: './remove-other-entities-confirm.component.html',
styleUrls: []
})
export class RemoveOtherEntitiesConfirmComponent extends PageComponent implements OnInit {
@Input()
onClose: (result: boolean | null) => void;
confirmFormGroup: FormGroup;
removeOtherEntitiesConfirmText: SafeHtml;
removeOtherEntitiesVerificationText = 'remove other entities';
constructor(protected store: Store<AppState>,
private translate: TranslateService,
private sanitizer: DomSanitizer,
private fb: FormBuilder) {
super(store);
this.removeOtherEntitiesConfirmText = this.sanitizer.bypassSecurityTrustHtml(this.translate.instant('version-control.remove-other-entities-confirm-text'));
}
ngOnInit(): void {
this.confirmFormGroup = this.fb.group({
verification: [null, []]
});
}
cancel(): void {
if (this.onClose) {
this.onClose(null);
}
}
confirm(): void {
if (this.onClose) {
this.onClose(true);
}
}
}

26
ui-ngx/src/app/modules/home/components/vc/version-control-settings.component.html → ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html

@ -16,24 +16,24 @@
--> -->
<div> <div>
<mat-card class="vc-settings" [ngClass]="{'settings-card': !detailsMode}"> <mat-card class="repository-settings" [ngClass]="{'settings-card': !detailsMode}">
<mat-card-title> <mat-card-title>
<div fxLayout="row"> <div fxLayout="row">
<span class="mat-headline" translate>admin.git-repository-settings</span> <span class="mat-headline" translate>admin.repository-settings</span>
<span fxFlex></span> <span fxFlex></span>
<div tb-help="versionControlSettings"></div> <div tb-help="repositorySettings"></div>
</div> </div>
</mat-card-title> </mat-card-title>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar> </mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<mat-card-content style="padding-top: 16px;"> <mat-card-content style="padding-top: 16px;">
<form [formGroup]="versionControlSettingsForm" #formDirective="ngForm" (ngSubmit)="save()"> <form [formGroup]="repositorySettingsForm" #formDirective="ngForm" (ngSubmit)="save()">
<fieldset [disabled]="isLoading$ | async"> <fieldset [disabled]="isLoading$ | async">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>admin.repository-url</mat-label> <mat-label translate>admin.repository-url</mat-label>
<input matInput required formControlName="repositoryUri"> <input matInput required formControlName="repositoryUri">
<mat-error translate *ngIf="versionControlSettingsForm.get('repositoryUri').hasError('required')"> <mat-error translate *ngIf="repositorySettingsForm.get('repositoryUri').hasError('required')">
admin.repository-url-required admin.repository-url-required
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
@ -46,12 +46,12 @@
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.auth-method</mat-label> <mat-label translate>admin.auth-method</mat-label>
<mat-select required formControlName="authMethod"> <mat-select required formControlName="authMethod">
<mat-option *ngFor="let method of versionControlAuthMethods" [value]="method"> <mat-option *ngFor="let method of repositoryAuthMethods" [value]="method">
{{versionControlAuthMethodTranslations.get(method) | translate}} {{repositoryAuthMethodTranslations.get(method) | translate}}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<section [fxShow]="versionControlSettingsForm.get('authMethod').value === versionControlAuthMethod.USERNAME_PASSWORD" fxLayout="column"> <section [fxShow]="repositorySettingsForm.get('authMethod').value === repositoryAuthMethod.USERNAME_PASSWORD" fxLayout="column">
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>common.username</mat-label> <mat-label translate>common.username</mat-label>
<input matInput formControlName="username" placeholder="{{ 'common.enter-username' | translate }}" <input matInput formControlName="username" placeholder="{{ 'common.enter-username' | translate }}"
@ -68,14 +68,14 @@
<tb-toggle-password matSuffix></tb-toggle-password> <tb-toggle-password matSuffix></tb-toggle-password>
</mat-form-field> </mat-form-field>
</section> </section>
<section [fxShow]="versionControlSettingsForm.get('authMethod').value === versionControlAuthMethod.PRIVATE_KEY" fxLayout="column"> <section [fxShow]="repositorySettingsForm.get('authMethod').value === repositoryAuthMethod.PRIVATE_KEY" fxLayout="column">
<tb-file-input style="margin-bottom: 16px;" <tb-file-input style="margin-bottom: 16px;"
[existingFileName]="versionControlSettingsForm.get('privateKeyFileName').value" [existingFileName]="repositorySettingsForm.get('privateKeyFileName').value"
required required
formControlName="privateKey" formControlName="privateKey"
dropLabel="{{ 'admin.drop-private-key-file-or' | translate }}" dropLabel="{{ 'admin.drop-private-key-file-or' | translate }}"
[label]="'admin.private-key' | translate" [label]="'admin.private-key' | translate"
(fileNameChanged)="versionControlSettingsForm.get('privateKeyFileName').patchValue($event)"> (fileNameChanged)="repositorySettingsForm.get('privateKeyFileName').patchValue($event)">
</tb-file-input> </tb-file-input>
<mat-checkbox *ngIf="showChangePrivateKeyPassword" (change)="changePrivateKeyPasswordChanged()" <mat-checkbox *ngIf="showChangePrivateKeyPassword" (change)="changePrivateKeyPasswordChanged()"
[(ngModel)]="changePrivateKeyPassword" [ngModelOptions]="{ standalone: true }" style="padding-bottom: 16px;"> [(ngModel)]="changePrivateKeyPassword" [ngModelOptions]="{ standalone: true }" style="padding-bottom: 16px;">
@ -96,10 +96,10 @@
</button> </button>
<span fxFlex></span> <span fxFlex></span>
<button mat-raised-button type="button" <button mat-raised-button type="button"
[disabled]="(isLoading$ | async) || versionControlSettingsForm.invalid" (click)="checkAccess()"> [disabled]="(isLoading$ | async) || repositorySettingsForm.invalid" (click)="checkAccess()">
{{'admin.check-access' | translate}} {{'admin.check-access' | translate}}
</button> </button>
<button mat-raised-button color="primary" [disabled]="(isLoading$ | async) || versionControlSettingsForm.invalid || !versionControlSettingsForm.dirty" <button mat-raised-button color="primary" [disabled]="(isLoading$ | async) || repositorySettingsForm.invalid || !repositorySettingsForm.dirty"
type="submit">{{'action.save' | translate}} type="submit">{{'action.save' | translate}}
</button> </button>
</div> </div>

2
ui-ngx/src/app/modules/home/components/vc/version-control-settings.component.scss → ui-ngx/src/app/modules/home/components/vc/repository-settings.component.scss

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
:host { :host {
mat-card.vc-settings { mat-card.repository-settings {
margin: 8px; margin: 8px;
} }
.fields-group { .fields-group {

216
ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts

@ -0,0 +1,216 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { FormBuilder, FormGroup, FormGroupDirective, Validators } from '@angular/forms';
import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { AdminService } from '@core/http/admin.service';
import {
RepositorySettings,
RepositoryAuthMethod,
repositoryAuthMethodTranslationMap
} from '@shared/models/settings.models';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core';
import { isNotEmptyStr } from '@core/utils';
import { DialogService } from '@core/services/dialog.service';
import { ActionAuthUpdateHasRepository } from '@core/auth/auth.actions';
import { selectHasRepository } from '@core/auth/auth.selectors';
import { catchError, mergeMap, take } from 'rxjs/operators';
import { of } from 'rxjs';
@Component({
selector: 'tb-repository-settings',
templateUrl: './repository-settings.component.html',
styleUrls: ['./repository-settings.component.scss', './../../pages/admin/settings-card.scss']
})
export class RepositorySettingsComponent extends PageComponent implements OnInit {
@Input()
detailsMode = false;
repositorySettingsForm: FormGroup;
settings: RepositorySettings = null;
repositoryAuthMethod = RepositoryAuthMethod;
repositoryAuthMethods = Object.values(RepositoryAuthMethod);
repositoryAuthMethodTranslations = repositoryAuthMethodTranslationMap;
showChangePassword = false;
changePassword = false;
showChangePrivateKeyPassword = false;
changePrivateKeyPassword = false;
constructor(protected store: Store<AppState>,
private adminService: AdminService,
private dialogService: DialogService,
private translate: TranslateService,
public fb: FormBuilder) {
super(store);
}
ngOnInit() {
this.repositorySettingsForm = this.fb.group({
repositoryUri: [null, [Validators.required]],
defaultBranch: ['main', []],
authMethod: [RepositoryAuthMethod.USERNAME_PASSWORD, [Validators.required]],
username: [null, []],
password: [null, []],
privateKeyFileName: [null, [Validators.required]],
privateKey: [null, []],
privateKeyPassword: [null, []]
});
this.updateValidators(false);
this.repositorySettingsForm.get('authMethod').valueChanges.subscribe(() => {
this.updateValidators(true);
});
this.repositorySettingsForm.get('privateKeyFileName').valueChanges.subscribe(() => {
this.updateValidators(false);
});
this.store.pipe(
select(selectHasRepository),
take(1),
mergeMap((hasRepository) => {
if (hasRepository) {
return this.adminService.getRepositorySettings({ignoreErrors: true}).pipe(
catchError(() => of(null))
);
} else {
return of(null);
}
})
).subscribe(
(settings) => {
this.settings = settings;
if (this.settings != null) {
if (this.settings.authMethod === RepositoryAuthMethod.USERNAME_PASSWORD) {
this.showChangePassword = true;
} else {
this.showChangePrivateKeyPassword = true;
}
this.repositorySettingsForm.reset(this.settings);
this.updateValidators(false);
}
});
}
checkAccess(): void {
const settings: RepositorySettings = this.repositorySettingsForm.value;
this.adminService.checkRepositoryAccess(settings).subscribe(() => {
this.store.dispatch(new ActionNotificationShow({ message: this.translate.instant('admin.check-repository-access-success'),
type: 'success' }));
});
}
save(): void {
const settings: RepositorySettings = this.repositorySettingsForm.value;
this.adminService.saveRepositorySettings(settings).subscribe(
(savedSettings) => {
this.settings = savedSettings;
if (this.settings.authMethod === RepositoryAuthMethod.USERNAME_PASSWORD) {
this.showChangePassword = true;
this.changePassword = false;
} else {
this.showChangePrivateKeyPassword = true;
this.changePrivateKeyPassword = false;
}
this.repositorySettingsForm.reset(this.settings);
this.updateValidators(false);
this.store.dispatch(new ActionAuthUpdateHasRepository({ hasRepository: true }));
}
);
}
delete(formDirective: FormGroupDirective): void {
this.dialogService.confirm(
this.translate.instant('admin.delete-repository-settings-title', ),
this.translate.instant('admin.delete-repository-settings-text'), null,
this.translate.instant('action.delete')
).subscribe((data) => {
if (data) {
this.adminService.deleteRepositorySettings().subscribe(
() => {
this.settings = null;
this.showChangePassword = false;
this.changePassword = false;
this.showChangePrivateKeyPassword = false;
this.changePrivateKeyPassword = false;
formDirective.resetForm();
this.repositorySettingsForm.reset({ defaultBranch: 'main', authMethod: RepositoryAuthMethod.USERNAME_PASSWORD });
this.updateValidators(false);
this.store.dispatch(new ActionAuthUpdateHasRepository({ hasRepository: false }));
}
);
}
});
}
changePasswordChanged() {
if (this.changePassword) {
this.repositorySettingsForm.get('password').patchValue('');
this.repositorySettingsForm.get('password').markAsDirty();
}
this.updateValidators(false);
}
changePrivateKeyPasswordChanged() {
if (this.changePrivateKeyPassword) {
this.repositorySettingsForm.get('privateKeyPassword').patchValue('');
this.repositorySettingsForm.get('privateKeyPassword').markAsDirty();
}
this.updateValidators(false);
}
updateValidators(emitEvent?: boolean): void {
const authMethod: RepositoryAuthMethod = this.repositorySettingsForm.get('authMethod').value;
const privateKeyFileName: string = this.repositorySettingsForm.get('privateKeyFileName').value;
if (authMethod === RepositoryAuthMethod.USERNAME_PASSWORD) {
this.repositorySettingsForm.get('username').enable({emitEvent});
if (this.changePassword || !this.showChangePassword) {
this.repositorySettingsForm.get('password').enable({emitEvent});
} else {
this.repositorySettingsForm.get('password').disable({emitEvent});
}
this.repositorySettingsForm.get('privateKeyFileName').disable({emitEvent});
this.repositorySettingsForm.get('privateKey').disable({emitEvent});
this.repositorySettingsForm.get('privateKeyPassword').disable({emitEvent});
} else {
this.repositorySettingsForm.get('username').disable({emitEvent});
this.repositorySettingsForm.get('password').disable({emitEvent});
this.repositorySettingsForm.get('privateKeyFileName').enable({emitEvent});
this.repositorySettingsForm.get('privateKey').enable({emitEvent});
if (this.changePrivateKeyPassword || !this.showChangePrivateKeyPassword) {
this.repositorySettingsForm.get('privateKeyPassword').enable({emitEvent});
} else {
this.repositorySettingsForm.get('privateKeyPassword').disable({emitEvent});
}
if (isNotEmptyStr(privateKeyFileName)) {
this.repositorySettingsForm.get('privateKey').clearValidators();
} else {
this.repositorySettingsForm.get('privateKey').setValidators([Validators.required]);
}
}
this.repositorySettingsForm.get('username').updateValueAndValidity({emitEvent: false});
this.repositorySettingsForm.get('password').updateValueAndValidity({emitEvent: false});
this.repositorySettingsForm.get('privateKeyFileName').updateValueAndValidity({emitEvent: false});
this.repositorySettingsForm.get('privateKey').updateValueAndValidity({emitEvent: false});
this.repositorySettingsForm.get('privateKeyPassword').updateValueAndValidity({emitEvent: false});
}
}

218
ui-ngx/src/app/modules/home/components/vc/version-control-settings.component.ts

@ -1,218 +0,0 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
import { FormBuilder, FormGroup, FormGroupDirective, Validators } from '@angular/forms';
import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { AdminService } from '@core/http/admin.service';
import {
EntitiesVersionControlSettings,
VersionControlAuthMethod,
versionControlAuthMethodTranslationMap
} from '@shared/models/settings.models';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core';
import { isNotEmptyStr } from '@core/utils';
import { DialogService } from '@core/services/dialog.service';
import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions';
import { ActionAuthUpdateHasVersionControl } from '@core/auth/auth.actions';
import { selectHasVersionControl } from '@core/auth/auth.selectors';
import { catchError, mergeMap, take } from 'rxjs/operators';
import { of } from 'rxjs';
@Component({
selector: 'tb-version-control-settings',
templateUrl: './version-control-settings.component.html',
styleUrls: ['./version-control-settings.component.scss', './../../pages/admin/settings-card.scss']
})
export class VersionControlSettingsComponent extends PageComponent implements OnInit {
@Input()
detailsMode = false;
versionControlSettingsForm: FormGroup;
settings: EntitiesVersionControlSettings = null;
versionControlAuthMethod = VersionControlAuthMethod;
versionControlAuthMethods = Object.values(VersionControlAuthMethod);
versionControlAuthMethodTranslations = versionControlAuthMethodTranslationMap;
showChangePassword = false;
changePassword = false;
showChangePrivateKeyPassword = false;
changePrivateKeyPassword = false;
constructor(protected store: Store<AppState>,
private adminService: AdminService,
private dialogService: DialogService,
private translate: TranslateService,
public fb: FormBuilder) {
super(store);
}
ngOnInit() {
this.versionControlSettingsForm = this.fb.group({
repositoryUri: [null, [Validators.required]],
defaultBranch: ['main', []],
authMethod: [VersionControlAuthMethod.USERNAME_PASSWORD, [Validators.required]],
username: [null, []],
password: [null, []],
privateKeyFileName: [null, [Validators.required]],
privateKey: [null, []],
privateKeyPassword: [null, []]
});
this.updateValidators(false);
this.versionControlSettingsForm.get('authMethod').valueChanges.subscribe(() => {
this.updateValidators(true);
});
this.versionControlSettingsForm.get('privateKeyFileName').valueChanges.subscribe(() => {
this.updateValidators(false);
});
this.store.pipe(
select(selectHasVersionControl),
take(1),
mergeMap((hasVersionControl) => {
if (hasVersionControl) {
return this.adminService.getEntitiesVersionControlSettings({ignoreErrors: true}).pipe(
catchError(() => of(null))
);
} else {
return of(null);
}
})
).subscribe(
(settings) => {
this.settings = settings;
if (this.settings != null) {
if (this.settings.authMethod === VersionControlAuthMethod.USERNAME_PASSWORD) {
this.showChangePassword = true;
} else {
this.showChangePrivateKeyPassword = true;
}
this.versionControlSettingsForm.reset(this.settings);
this.updateValidators(false);
}
});
}
checkAccess(): void {
const settings: EntitiesVersionControlSettings = this.versionControlSettingsForm.value;
this.adminService.checkVersionControlAccess(settings).subscribe(() => {
this.store.dispatch(new ActionNotificationShow({ message: this.translate.instant('admin.check-vc-access-success'),
type: 'success' }));
});
}
save(): void {
const settings: EntitiesVersionControlSettings = this.versionControlSettingsForm.value;
this.adminService.saveEntitiesVersionControlSettings(settings).subscribe(
(savedSettings) => {
this.settings = savedSettings;
if (this.settings.authMethod === VersionControlAuthMethod.USERNAME_PASSWORD) {
this.showChangePassword = true;
this.changePassword = false;
} else {
this.showChangePrivateKeyPassword = true;
this.changePrivateKeyPassword = false;
}
this.versionControlSettingsForm.reset(this.settings);
this.updateValidators(false);
this.store.dispatch(new ActionAuthUpdateHasVersionControl({ hasVersionControl: true }));
}
);
}
delete(formDirective: FormGroupDirective): void {
this.dialogService.confirm(
this.translate.instant('admin.delete-git-settings-title', ),
this.translate.instant('admin.delete-git-settings-text'), null,
this.translate.instant('action.delete')
).subscribe((data) => {
if (data) {
this.adminService.deleteEntitiesVersionControlSettings().subscribe(
() => {
this.settings = null;
this.showChangePassword = false;
this.changePassword = false;
this.showChangePrivateKeyPassword = false;
this.changePrivateKeyPassword = false;
formDirective.resetForm();
this.versionControlSettingsForm.reset({ defaultBranch: 'main', authMethod: VersionControlAuthMethod.USERNAME_PASSWORD });
this.updateValidators(false);
this.store.dispatch(new ActionAuthUpdateHasVersionControl({ hasVersionControl: false }));
}
);
}
});
}
changePasswordChanged() {
if (this.changePassword) {
this.versionControlSettingsForm.get('password').patchValue('');
this.versionControlSettingsForm.get('password').markAsDirty();
}
this.updateValidators(false);
}
changePrivateKeyPasswordChanged() {
if (this.changePrivateKeyPassword) {
this.versionControlSettingsForm.get('privateKeyPassword').patchValue('');
this.versionControlSettingsForm.get('privateKeyPassword').markAsDirty();
}
this.updateValidators(false);
}
updateValidators(emitEvent?: boolean): void {
const authMethod: VersionControlAuthMethod = this.versionControlSettingsForm.get('authMethod').value;
const privateKeyFileName: string = this.versionControlSettingsForm.get('privateKeyFileName').value;
if (authMethod === VersionControlAuthMethod.USERNAME_PASSWORD) {
this.versionControlSettingsForm.get('username').enable({emitEvent});
if (this.changePassword || !this.showChangePassword) {
this.versionControlSettingsForm.get('password').enable({emitEvent});
} else {
this.versionControlSettingsForm.get('password').disable({emitEvent});
}
this.versionControlSettingsForm.get('privateKeyFileName').disable({emitEvent});
this.versionControlSettingsForm.get('privateKey').disable({emitEvent});
this.versionControlSettingsForm.get('privateKeyPassword').disable({emitEvent});
} else {
this.versionControlSettingsForm.get('username').disable({emitEvent});
this.versionControlSettingsForm.get('password').disable({emitEvent});
this.versionControlSettingsForm.get('privateKeyFileName').enable({emitEvent});
this.versionControlSettingsForm.get('privateKey').enable({emitEvent});
if (this.changePrivateKeyPassword || !this.showChangePrivateKeyPassword) {
this.versionControlSettingsForm.get('privateKeyPassword').enable({emitEvent});
} else {
this.versionControlSettingsForm.get('privateKeyPassword').disable({emitEvent});
}
if (isNotEmptyStr(privateKeyFileName)) {
this.versionControlSettingsForm.get('privateKey').clearValidators();
} else {
this.versionControlSettingsForm.get('privateKey').setValidators([Validators.required]);
}
}
this.versionControlSettingsForm.get('username').updateValueAndValidity({emitEvent: false});
this.versionControlSettingsForm.get('password').updateValueAndValidity({emitEvent: false});
this.versionControlSettingsForm.get('privateKeyFileName').updateValueAndValidity({emitEvent: false});
this.versionControlSettingsForm.get('privateKey').updateValueAndValidity({emitEvent: false});
this.versionControlSettingsForm.get('privateKeyPassword').updateValueAndValidity({emitEvent: false});
}
}

6
ui-ngx/src/app/modules/home/components/vc/version-control.component.html

@ -15,9 +15,9 @@
limitations under the License. limitations under the License.
--> -->
<tb-version-control-settings #versionControlSettingsComponent [detailsMode]="detailsMode" <tb-repository-settings #repositorySettingsComponent [detailsMode]="detailsMode"
*ngIf="!(hasVersionControl$ | async); else versionsTable"> *ngIf="!(hasRepository$ | async); else versionsTable">
</tb-version-control-settings> </tb-repository-settings>
<ng-template #versionsTable> <ng-template #versionsTable>
<tb-entity-versions-table [singleEntityMode]="singleEntityMode" <tb-entity-versions-table [singleEntityMode]="singleEntityMode"
[active]="active" [active]="active"

10
ui-ngx/src/app/modules/home/components/vc/version-control.component.ts

@ -17,9 +17,9 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { selectHasVersionControl } from '@core/auth/auth.selectors'; import { selectHasRepository } from '@core/auth/auth.selectors';
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
import { VersionControlSettingsComponent } from '@home/components/vc/version-control-settings.component'; import { RepositorySettingsComponent } from '@home/components/vc/repository-settings.component';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
@ -30,7 +30,7 @@ import { EntityId } from '@shared/models/id/entity-id';
}) })
export class VersionControlComponent implements OnInit, HasConfirmForm { export class VersionControlComponent implements OnInit, HasConfirmForm {
@ViewChild('versionControlSettingsComponent', {static: false}) versionControlSettingsComponent: VersionControlSettingsComponent; @ViewChild('repositorySettingsComponent', {static: false}) repositorySettingsComponent: RepositorySettingsComponent;
@Input() @Input()
detailsMode = false; detailsMode = false;
@ -50,7 +50,7 @@ export class VersionControlComponent implements OnInit, HasConfirmForm {
@Output() @Output()
versionRestored = new EventEmitter<void>(); versionRestored = new EventEmitter<void>();
hasVersionControl$ = this.store.pipe(select(selectHasVersionControl)); hasRepository$ = this.store.pipe(select(selectHasRepository));
constructor(private store: Store<AppState>) { constructor(private store: Store<AppState>) {
@ -61,7 +61,7 @@ export class VersionControlComponent implements OnInit, HasConfirmForm {
} }
confirmForm(): FormGroup { confirmForm(): FormGroup {
return this.versionControlSettingsComponent?.versionControlSettingsForm; return this.repositorySettingsComponent?.repositorySettingsForm;
} }
} }

10
ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts

@ -33,7 +33,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models';
import { BreadCrumbConfig } from '@shared/components/breadcrumb'; import { BreadCrumbConfig } from '@shared/components/breadcrumb';
import { QueuesTableConfigResolver } from '@home/pages/admin/queue/queues-table-config.resolver'; import { QueuesTableConfigResolver } from '@home/pages/admin/queue/queues-table-config.resolver';
import { VersionControlAdminSettingsComponent } from '@home/pages/admin/version-control-admin-settings.component'; import { RepositoryAdminSettingsComponent } from '@home/pages/admin/repository-admin-settings.component';
@Injectable() @Injectable()
export class OAuth2LoginProcessingUrlResolver implements Resolve<string> { export class OAuth2LoginProcessingUrlResolver implements Resolve<string> {
@ -225,14 +225,14 @@ const routes: Routes = [
] ]
}, },
{ {
path: 'vc', path: 'repository',
component: VersionControlAdminSettingsComponent, component: RepositoryAdminSettingsComponent,
canDeactivate: [ConfirmOnExitGuard], canDeactivate: [ConfirmOnExitGuard],
data: { data: {
auth: [Authority.TENANT_ADMIN], auth: [Authority.TENANT_ADMIN],
title: 'admin.git-settings', title: 'admin.repository-settings',
breadcrumb: { breadcrumb: {
label: 'admin.git-settings', label: 'admin.repository-settings',
icon: 'manage_history' icon: 'manage_history'
} }
} }

4
ui-ngx/src/app/modules/home/pages/admin/admin.module.ts

@ -29,7 +29,7 @@ import { SendTestSmsDialogComponent } from '@home/pages/admin/send-test-sms-dial
import { HomeSettingsComponent } from '@home/pages/admin/home-settings.component'; import { HomeSettingsComponent } from '@home/pages/admin/home-settings.component';
import { ResourcesLibraryComponent } from '@home/pages/admin/resource/resources-library.component'; import { ResourcesLibraryComponent } from '@home/pages/admin/resource/resources-library.component';
import { QueueComponent} from '@home/pages/admin/queue/queue.component'; import { QueueComponent} from '@home/pages/admin/queue/queue.component';
import { VersionControlAdminSettingsComponent } from '@home/pages/admin/version-control-admin-settings.component'; import { RepositoryAdminSettingsComponent } from '@home/pages/admin/repository-admin-settings.component';
@NgModule({ @NgModule({
declarations: declarations:
@ -43,7 +43,7 @@ import { VersionControlAdminSettingsComponent } from '@home/pages/admin/version-
HomeSettingsComponent, HomeSettingsComponent,
ResourcesLibraryComponent, ResourcesLibraryComponent,
QueueComponent, QueueComponent,
VersionControlAdminSettingsComponent RepositoryAdminSettingsComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

2
ui-ngx/src/app/modules/home/pages/admin/version-control-admin-settings.component.html → ui-ngx/src/app/modules/home/pages/admin/repository-admin-settings.component.html

@ -15,4 +15,4 @@
limitations under the License. limitations under the License.
--> -->
<tb-version-control-settings #versionControlSettingsComponent></tb-version-control-settings> <tb-repository-settings #repositorySettingsComponent></tb-repository-settings>

12
ui-ngx/src/app/modules/home/pages/admin/version-control-admin-settings.component.ts → ui-ngx/src/app/modules/home/pages/admin/repository-admin-settings.component.ts

@ -20,16 +20,16 @@ import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { VersionControlSettingsComponent } from '@home/components/vc/version-control-settings.component'; import { RepositorySettingsComponent } from '@home/components/vc/repository-settings.component';
@Component({ @Component({
selector: 'tb-version-control-admin-settings', selector: 'tb-repository-admin-settings',
templateUrl: './version-control-admin-settings.component.html', templateUrl: './repository-admin-settings.component.html',
styleUrls: [] styleUrls: []
}) })
export class VersionControlAdminSettingsComponent extends PageComponent implements OnInit, HasConfirmForm { export class RepositoryAdminSettingsComponent extends PageComponent implements OnInit, HasConfirmForm {
@ViewChild('versionControlSettingsComponent') versionControlSettingsComponent: VersionControlSettingsComponent; @ViewChild('repositorySettingsComponent') repositorySettingsComponent: RepositorySettingsComponent;
constructor(protected store: Store<AppState>) { constructor(protected store: Store<AppState>) {
super(store); super(store);
@ -39,6 +39,6 @@ export class VersionControlAdminSettingsComponent extends PageComponent implemen
} }
confirmForm(): FormGroup { confirmForm(): FormGroup {
return this.versionControlSettingsComponent?.versionControlSettingsForm; return this.repositorySettingsComponent?.repositorySettingsForm;
} }
} }

2
ui-ngx/src/app/shared/models/constants.ts

@ -135,7 +135,7 @@ export const HelpLinks = {
ruleNodePushToCloud: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/action-nodes/#push-to-cloud', ruleNodePushToCloud: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/action-nodes/#push-to-cloud',
ruleNodePushToEdge: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/action-nodes/#push-to-edge', ruleNodePushToEdge: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/action-nodes/#push-to-edge',
queue: helpBaseUrl + '/docs/user-guide/queue', queue: helpBaseUrl + '/docs/user-guide/queue',
versionControlSettings: helpBaseUrl + '/docs/user-guide/ui/version-control-settings' repositorySettings: helpBaseUrl + '/docs/user-guide/ui/repository-settings'
} }
}; };

19
ui-ngx/src/app/shared/models/settings.models.ts

@ -16,6 +16,7 @@
import { ValidatorFn } from '@angular/forms'; import { ValidatorFn } from '@angular/forms';
import { isNotEmptyStr, isNumber } from '@core/utils'; import { isNotEmptyStr, isNumber } from '@core/utils';
import { VersionCreateConfig } from '@shared/models/vc.models';
export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/; export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
@ -397,23 +398,29 @@ export function createSmsProviderConfiguration(type: SmsProviderType): SmsProvid
return smsProviderConfiguration; return smsProviderConfiguration;
} }
export enum VersionControlAuthMethod { export enum RepositoryAuthMethod {
USERNAME_PASSWORD = 'USERNAME_PASSWORD', USERNAME_PASSWORD = 'USERNAME_PASSWORD',
PRIVATE_KEY = 'PRIVATE_KEY' PRIVATE_KEY = 'PRIVATE_KEY'
} }
export const versionControlAuthMethodTranslationMap = new Map<VersionControlAuthMethod, string>([ export const repositoryAuthMethodTranslationMap = new Map<RepositoryAuthMethod, string>([
[VersionControlAuthMethod.USERNAME_PASSWORD, 'admin.auth-method-username-password'], [RepositoryAuthMethod.USERNAME_PASSWORD, 'admin.auth-method-username-password'],
[VersionControlAuthMethod.PRIVATE_KEY, 'admin.auth-method-private-key'] [RepositoryAuthMethod.PRIVATE_KEY, 'admin.auth-method-private-key']
]); ]);
export interface EntitiesVersionControlSettings { export interface RepositorySettings {
repositoryUri: string; repositoryUri: string;
defaultBranch: string; defaultBranch: string;
authMethod: VersionControlAuthMethod; authMethod: RepositoryAuthMethod;
username: string; username: string;
password: string; password: string;
privateKeyFileName: string; privateKeyFileName: string;
privateKey: string; privateKey: string;
privateKeyPassword: string; privateKeyPassword: string;
} }
export interface AutoVersionCreateConfig extends VersionCreateConfig {
branch: string;
}
export type AutoCommitSettings = {[entityType: string]: AutoVersionCreateConfig};

23
ui-ngx/src/app/shared/models/vc.models.ts

@ -32,6 +32,7 @@ export const exportableEntityTypes: Array<EntityType> = [
export interface VersionCreateConfig { export interface VersionCreateConfig {
saveRelations: boolean; saveRelations: boolean;
saveAttributes: boolean;
} }
export enum VersionCreateRequestType { export enum VersionCreateRequestType {
@ -81,6 +82,7 @@ export function createDefaultEntityTypesVersionCreate(): {[entityType: string]:
res[entityType] = { res[entityType] = {
syncStrategy: null, syncStrategy: null,
saveRelations: false, saveRelations: false,
saveAttributes: false,
allEntities: true, allEntities: true,
entityIds: [] entityIds: []
}; };
@ -90,6 +92,7 @@ export function createDefaultEntityTypesVersionCreate(): {[entityType: string]:
export interface VersionLoadConfig { export interface VersionLoadConfig {
loadRelations: boolean; loadRelations: boolean;
loadAttributes: boolean;
} }
export enum VersionLoadRequestType { export enum VersionLoadRequestType {
@ -111,6 +114,7 @@ export interface SingleEntityVersionLoadRequest extends VersionLoadRequest {
export interface EntityTypeVersionLoadConfig extends VersionLoadConfig { export interface EntityTypeVersionLoadConfig extends VersionLoadConfig {
removeOtherEntities: boolean; removeOtherEntities: boolean;
findExistingEntityByName: boolean;
} }
export interface EntityTypeVersionLoadRequest extends VersionLoadRequest { export interface EntityTypeVersionLoadRequest extends VersionLoadRequest {
@ -123,7 +127,9 @@ export function createDefaultEntityTypesVersionLoad(): {[entityType: string]: En
for (const entityType of exportableEntityTypes) { for (const entityType of exportableEntityTypes) {
res[entityType] = { res[entityType] = {
loadRelations: false, loadRelations: false,
removeOtherEntities: false loadAttributes: false,
removeOtherEntities: false,
findExistingEntityByName: false
}; };
} }
return res; return res;
@ -176,18 +182,5 @@ export interface EntityDataDiff {
} }
export function entityExportDataToJsonString(data: EntityExportData<any>): string { export function entityExportDataToJsonString(data: EntityExportData<any>): string {
if (!data.relations) { return JSON.stringify(data, null, 4);
data.relations = [];
}
const allKeys = new Set<string>();
JSON.stringify(data, (key, value) => (allKeys.add(key), value));
return JSON.stringify(data, Array.from(allKeys).sort((key1, key2) => {
if (key1 === 'relations') {
return 1;
} else if (key2 === 'relations') {
return -1;
} else {
return 0;
}
}), 4);
} }

19
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -58,7 +58,8 @@
"next-with-label": "Next: {{label}}", "next-with-label": "Next: {{label}}",
"read-more": "Read more", "read-more": "Read more",
"hide": "Hide", "hide": "Hide",
"restore": "Restore" "restore": "Restore",
"confirm": "Confirm"
}, },
"aggregation": { "aggregation": {
"aggregation": "Aggregation", "aggregation": "Aggregation",
@ -321,8 +322,7 @@
"queue-submit-strategy": "Submit strategy", "queue-submit-strategy": "Submit strategy",
"queue-processing-strategy": "Processing strategy", "queue-processing-strategy": "Processing strategy",
"queue-configuration": "Queue configuration", "queue-configuration": "Queue configuration",
"git-settings": "Git settings", "repository-settings": "Repository settings",
"git-repository-settings": "Git repository settings",
"repository-url": "Repository URL", "repository-url": "Repository URL",
"repository-url-required": "Repository URL is required.", "repository-url-required": "Repository URL is required.",
"default-branch": "Default branch name", "default-branch": "Default branch name",
@ -338,9 +338,9 @@
"enter-passphrase": "Enter passphrase", "enter-passphrase": "Enter passphrase",
"change-passphrase": "Change passphrase", "change-passphrase": "Change passphrase",
"check-access": "Check access", "check-access": "Check access",
"check-vc-access-success": "Git repository access successfully verified!", "check-repository-access-success": "Repository access successfully verified!",
"delete-git-settings-title": "Are you sure you want to delete git settings?", "delete-repository-settings-title": "Are you sure you want to delete repository settings?",
"delete-git-settings-text": "Be careful, after the confirmation the git settings will be removed and git synchronization feature will be unavailable." "delete-repository-settings-text": "Be careful, after the confirmation the repository settings will be removed and version control feature will be unavailable."
}, },
"alarm": { "alarm": {
"alarm": "Alarm", "alarm": "Alarm",
@ -3121,6 +3121,7 @@
"version-name-required": "Version name is required", "version-name-required": "Version name is required",
"author": "Author", "author": "Author",
"export-entity-relations": "Export entity relations", "export-entity-relations": "Export entity relations",
"export-entity-attributes": "Export entity attributes",
"entity-versions": "Entity versions", "entity-versions": "Entity versions",
"versions": "Versions", "versions": "Versions",
"created-time": "Created time", "created-time": "Created time",
@ -3133,6 +3134,7 @@
"restore-version": "Restore version", "restore-version": "Restore version",
"restore-entity-from-version": "Restore entity from version '{{versionName}}'", "restore-entity-from-version": "Restore entity from version '{{versionName}}'",
"load-entity-relations": "Load entity relations", "load-entity-relations": "Load entity relations",
"load-entity-attributes": "Load entity attributes",
"show-version-diff": "Show version diff", "show-version-diff": "Show version diff",
"diff-entity-with-version": "Diff with entity version '{{versionName}}'", "diff-entity-with-version": "Diff with entity version '{{versionName}}'",
"previous-difference": "Previous Difference", "previous-difference": "Previous Difference",
@ -3151,12 +3153,15 @@
"remove-all": "Remove all", "remove-all": "Remove all",
"version-create-result": "{ added, plural, 0 {No entities} 1 {1 entity} other {# entities} } added.\n{ modified, plural, 0 {No entities} 1 {1 entity} other {# entities} } modified.\n{ removed, plural, 0 {No entities} 1 {1 entity} other {# entities} } removed.", "version-create-result": "{ added, plural, 0 {No entities} 1 {1 entity} other {# entities} } added.\n{ modified, plural, 0 {No entities} 1 {1 entity} other {# entities} } modified.\n{ removed, plural, 0 {No entities} 1 {1 entity} other {# entities} } removed.",
"load-entities-relations": "Load entities relations", "load-entities-relations": "Load entities relations",
"load-entities-attributes": "Load entities attributes",
"remove-other-entities": "Remove other entities", "remove-other-entities": "Remove other entities",
"find-existing-entity-by-name": "Find existing entity by name",
"restore-entities-from-version": "Restore entities from version '{{versionName}}'", "restore-entities-from-version": "Restore entities from version '{{versionName}}'",
"no-entities-restored": "No entities restored", "no-entities-restored": "No entities restored",
"created": "{{created}} created", "created": "{{created}} created",
"updated": "{{updated}} updated", "updated": "{{updated}} updated",
"deleted": "{{deleted}} deleted" "deleted": "{{deleted}} deleted",
"remove-other-entities-confirm-text": "Be careful! This will permanently <b>delete</b> <b>all</b> current entities<br/>not present in the version you want to restore.<br/><br/>Please type <b>remove other entities</b> to confirm."
}, },
"widget": { "widget": {
"widget-library": "Widgets Library", "widget-library": "Widgets Library",

Loading…
Cancel
Save