From 344f45f00c742089ff7360734d8b471dbf560f8e Mon Sep 17 00:00:00 2001 From: Lee Jihaeng <75159489+SejoWuigui@users.noreply.github.com> Date: Fri, 3 Jan 2025 22:39:54 +0900 Subject: [PATCH 01/18] Update ko.json (#11277) --- src/assets/i18n/ko.json | 244 ++++++++++++++++++++-------------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/src/assets/i18n/ko.json b/src/assets/i18n/ko.json index 747a7e20e1c..24d8be1229b 100644 --- a/src/assets/i18n/ko.json +++ b/src/assets/i18n/ko.json @@ -207,7 +207,7 @@ "LBA of First Error": "", "LDAP server hostnames or IP addresses. Separate entries with an empty space. Multiple hostnames or IP addresses can be entered to create an LDAP failover priority list. If a host does not respond, the next host in the list is tried until a new connection is established.": "", "LDAP server to use for SID/uid/gid map entries. When undefined, idmap_ldap uses *ldap://localhost/*. Example: ldap://ldap.netscape.com/o=Airius.com.": "", - "LONG": "", + "LONG": "LONG", "Leave at the default of 512 unless the initiator requires a different block size.": "", "Leave blank to allow all or enter a list of initiator hostnames. Separate entries by pressing Enter.": "", "Leave empty for default (OpsGenie API)": "", @@ -244,7 +244,7 @@ "Method": "", "Method Call": "", "Metrics": "", - "MiB": "", + "MiB": "MiB", "Microsoft Azure": "", "Microsoft Onedrive Access Token. Log in to the Microsoft account to add an access token.": "", "Minimum Passive Port": "", @@ -254,26 +254,26 @@ "Mutual secret password. Required when Peer User is set. Must be different than the Secret.": "", "NIC modifications are currently restricted due to pending network changes.": "", "NIC selection is currently restricted due to pending network changes.": "", - "NIC was added": "", - "Name must start and end with a lowercase alphanumeric character. Hyphen is allowed in the middle e.g abc123, abc, abcd-1232": "", - "Name of the WebDAV site, service, or software being used.": "", - "Name of the new alert service.": "", - "Name of the new cloned boot environment. Alphanumeric characters, dashes (-), underscores (_), and periods (.) are allowed.": "", - "Name of the new dataset created from the cloned snapshot.": "", - "Name of the pool is required": "", - "Name of the pool must be correct": "", - "Name of the zvol is required": "", - "Name of the zvol must be correct": "", - "Name of this SSH connection. SSH connection names must be unique.": "", - "Name of this replication configuration.": "", - "Name or Naming Schema must be provided.": "", + "NIC was added": "NIC 추가함", + "Name must start and end with a lowercase alphanumeric character. Hyphen is allowed in the middle e.g abc123, abc, abcd-1232": "이름은 반드시 알파벳 소문자로 시작하고 끝나야 합니다. 하이픈(-)은 중간에 사용할 수 있습니다(예: abc123, abc, abcd-1232).", + "Name of the WebDAV site, service, or software being used.": "사용중인 WebDAV 사이트, 서비스 또는 소프트웨어의 이름입니다.", + "Name of the new alert service.": "새로운 경고 서비스의 이름입니다.", + "Name of the new cloned boot environment. Alphanumeric characters, dashes (-), underscores (_), and periods (.) are allowed.": "새롭게 복제한 시작 환경의 이름입니다. 알파벳과 숫자, 대시(-), 밑줄(_), 온점(.)을 사용할 수 있습니다.", + "Name of the new dataset created from the cloned snapshot.": "복제한 스냅샷으로부터 생성된 새로운 데이터셋의 이름입니다.", + "Name of the pool is required": "풀 이름 필요", + "Name of the pool must be correct": "올바르지 않은 풀 이름", + "Name of the zvol is required": "ZVOL 이름 필요", + "Name of the zvol must be correct": "올바르지 않은 ZVOL 이름", + "Name of this SSH connection. SSH connection names must be unique.": "이 SSH 연결의 이름입니다. SSH 연결 이름은 고유해야 합니다.", + "Name of this replication configuration.": "이 복제 구성의 이름입니다.", + "Name or Naming Schema must be provided.": "이름 또는 이름짓기 방식을 제공해야 합니다.", "Name ~ \"admin\"": "", - "Negotiate – only encrypt transport if explicitly requested by the SMB client": "", - "Netcat Active Side": "", - "Netcat Active Side Connect Address": "", - "Netcat Active Side Listen Address": "", - "Netcat Active Side Max Port": "", - "Netcat Active Side Min Port": "", + "Negotiate – only encrypt transport if explicitly requested by the SMB client": "협상 – SMB 클라이언트가 명시적으로 요청하는 경우에만 전송 암호화", + "Netcat Active Side": "Netcat 액티브 사이드", + "Netcat Active Side Connect Address": "Netcat 액티브 사이드 연결 주소", + "Netcat Active Side Listen Address": "Netcat 액티브 사이드 수신 주소", + "Netcat Active Side Max Port": "Netcat 액티브 사이드 최대 포트", + "Netcat Active Side Min Port": "Netcat 액티브 사이드 최소 포트", "Network addresses allowed to use this initiator. Leave blank to allow all networks or list network addresses with a CIDR mask. Separate entries by pressing Enter.": "", "Network addresses allowed use this initiator. Each address can include an optional CIDR netmask. Click + to add the network address to the list. Example: 192.168.2.0/24.": "", "Network community string. The community string acts like a user ID or password. A user with the correct community string has access to network information. The default is public. For more information, see What is an SNMP Community String?.": "", @@ -301,9 +301,9 @@ "Number of virtual CPUs to allocate to the virtual machine. The VM operating system might have operational or licensing restrictions on the number of CPUs.": "", "OQ % Used": "", "OQ Used": "", - "Off by default. When set, smbd(8) attempts to authenticate users with the insecure and vulnerable NTLMv1 encryption. This setting allows backward compatibility with older versions of Windows, but is not recommended and should not be used on untrusted networks.": "", - "Offload Read": "", - "Offload Write": "", + "Off by default. When set, smbd(8) attempts to authenticate users with the insecure and vulnerable NTLMv1 encryption. This setting allows backward compatibility with older versions of Windows, but is not recommended and should not be used on untrusted networks.": "기본으로 꺼져있습니다. 설정하면 smbd(8)은(는) 안전하지 않고 취약한 NTLMv1 암호화로 사용자를 인증하려고 시도합니다. 이 설정은 이전 버전의 Windows와의 하위 호환성을 허용하지만 권장하지 않으며, 신뢰할 수 없는 네트워크에서는 사용해서는 안 됩니다.", + "Offload Read": "오프로드 읽기", + "Offload Write": "오프로드 쓰기", "Once an enclosure is selected, all other VDEV creation steps will limit disk selection options to disks in the selected enclosure. If the enclosure selection is changed, all disk selections will be reset.": "", "Once enabled, users will be required to set up two factor authentication next time they login.": "", "One half widget and two quarter widgets below": "", @@ -322,73 +322,73 @@ "Only one can be active at a time.": "", "Only override the default if the initiator requires a different block size.": "", "Only replicate snapshots that match a defined creation time. To specify which snapshots will be replicated, set this checkbox and define the snapshot creation times that will be replicated. For example, setting this time frame to Hourly will only replicate snapshots that were created at the beginning of each hour.": "", - "Open ticket": "", + "Open ticket": "티켓 발행", "OpenStack Swift": "", "Opened at": "", - "Optional IP": "", - "Optional IP of 2nd Redfish management interface.": "", - "Optional description. Portals are automatically assigned a numeric group.": "", - "Optional user-friendly name.": "", - "Optional. Enter a server description.": "", - "Optional. Only needed for private images.": "", - "Optional: CPU Set (Examples: 0-3,8-11)": "", - "Optional: Choose installation media image": "", - "Optional: NUMA nodeset (Example: 0-1)": "", - "Options": "", - "Options cannot be loaded": "", - "Options for encrypting the LDAP connection: ": "", + "Optional IP": "선택적 IP", + "Optional IP of 2nd Redfish management interface.": "2번째 Redfish 관리 인터페이스의 선택적 IP입니다.", + "Optional description. Portals are automatically assigned a numeric group.": "선택적 설명입니다. 포털에는 자동으로 숫자 그룹이 지정됩니다.", + "Optional user-friendly name.": "선택적 사용자 친화 이름입니다.", + "Optional. Enter a server description.": "선택사항입니다. 서버 설명을 입력합니다.", + "Optional. Only needed for private images.": "선택사항입니다. 개인 이미지에만 필요합니다.", + "Optional: CPU Set (Examples: 0-3,8-11)": "선택사항: CPU 세트 (예시: 0-3,8-11)", + "Optional: Choose installation media image": "선택사항: 설치 매체 이미지 선택", + "Optional: NUMA nodeset (Example: 0-1)": "선택사항: NUMA 노드세트 (예시: 0-1)", + "Options": "선택사항", + "Options cannot be loaded": "선택사항 불러올 수 없음", + "Options for encrypting the LDAP connection: ": "LDAP 연결 암호화 선택사항: ", "Other Execute": "", - "Other node is currently processing a failover event.": "", - "Others": "", - "Outgoing Mail Server": "", - "Outlook": "", - "Overrides default directory creation mask of 0777 which grants directory read, write and execute access for everybody.": "", - "Overrides default file creation mask of 0666 which creates files with read and write access for everybody.": "", - "PCI Passthrough Device": "", - "PCI device does not have a reset mechanism defined and you may experience inconsistent/degraded behavior when starting/stopping the VM.": "", - "POSIX": "", - "POSIX Permissions": "", + "Other node is currently processing a failover event.": "다른 노드가 장애조치 진행중입니다.", + "Others": "기타", + "Outgoing Mail Server": "발송 메일 서버", + "Outlook": "OUTLOOK", + "Overrides default directory creation mask of 0777 which grants directory read, write and execute access for everybody.": "모든 사용자에게 디렉터리 읽기, 쓰기, 실행 권한을 부여하는 기본 디렉터리 생성 마스크인 0777을 대체합니다.", + "Overrides default file creation mask of 0666 which creates files with read and write access for everybody.": "모든 사용자가 읽고 쓸 수 있는 파일을 생성하는 기본 파일 생성 마스크인 0666을 대체합니다.", + "PCI Passthrough Device": "PCI 통과 장치", + "PCI device does not have a reset mechanism defined and you may experience inconsistent/degraded behavior when starting/stopping the VM.": "PCI 장치에는 재설정 메커니즘이 정의되어 있지 않으므로, 가상머신을 시작/중지할 때 일관되지 않거나 저하된 동작이 발생할 수 있습니다.", + "POSIX": "POSIX", + "POSIX Permissions": "POSIX 권한", "PULL": "", "PUSH": "", - "PagerDuty client name.": "", - "Pair this certificate's public key with the Certificate Authority private key used to sign this certificate.": "", - "Passive Controller": "", - "Passthrough": "", - "Password associated with the LDAP User DN.": "", - "Password for the Active Directory administrator account. Required the first time a domain is configured. After initial configuration, the password is not needed to edit, start, or stop the service.": "", - "Password for the Bind DN.": "", - "Password to encrypt and decrypt remote data. Warning: Always securely back up this password! Losing the encryption password will result in data loss.": "", - "Passwords cannot contain a ?. Passwords should be at least eight characters and contain a mix of lower and upper case, numbers, and special characters.": "", - "Paste either or both public and private keys. If only a public key is entered, it will be stored alone. If only a private key is pasted, the public key will be automatically calculated and entered in the public key field. Click Generate Keypair to create a new keypair. Encrypted keypairs or keypairs with passphrases are not supported.": "", - "Paste the incoming webhook URL associated with this service.": "", - "Paste the certificate for the CA.": "", - "Paste the contents of your Certificate Signing Request here.": "", - "Paste the private key associated with the Certificate when available. Please provide a key at least 1024 bits long.": "", - "Pattern of naming custom snapshots to include in the replication with the periodic snapshot schedule. Enter the strftime(3) strings that match the snapshots to include in the replication.

When a periodic snapshot is not linked to the replication, enter the naming schema for manually created snapshots. Has the same %Y, %m, %d, %H, and %M string requirements as the Naming Schema in a Periodic Snapshot Task. Separate entries by pressing Enter.": "", - "Pattern of naming custom snapshots to be replicated. Enter the name and strftime(3) %Y, %m, %d, %H, and %M strings that match the snapshots to include in the replication. Separate entries by pressing Enter. The number of snapshots matching the patterns are shown.": "", - "Peer Secret": "", - "Peer Secret (Confirm)": "", - "Peer User": "", - "Percentage used of dataset quota at which to generate a critical alert.": "", - "Percentage used of dataset quota at which to generate a warning alert.": "", - "Perform Reverse DNS Lookups": "", - "Performs authentication from an LDAP server.": "", - "Photo Library API client secret generated from the Google API Console": "", + "PagerDuty client name.": "PagerDuty 클라이언트 이름입니다.", + "Pair this certificate's public key with the Certificate Authority private key used to sign this certificate.": "이 인증서의 공개키를 이 인증서에 서명하는 데 사용한 인증기관의 개인 키와 묶습니다.", + "Passive Controller": "패시브 컨트롤러", + "Passthrough": "통과", + "Password associated with the LDAP User DN.": "LDAP 사용자 DN과 연결된 비밀번호입니다.", + "Password for the Active Directory administrator account. Required the first time a domain is configured. After initial configuration, the password is not needed to edit, start, or stop the service.": "액티브 디렉터리 관리자 계정의 비밀번호입니다. 도메인을 처음 구성할 때 필요합니다. 초기 구성이 끝나면 서비스를 수정, 시작 또는 멈추는 데 비밀번호가 필요하지 않습니다.", + "Password for the Bind DN.": "Bind DN의 비밀번호입니다.", + "Password to encrypt and decrypt remote data. Warning: Always securely back up this password! Losing the encryption password will result in data loss.": "원격 데이터를 암호화 및 복호화하는 비밀번호입니다. 위험: 항상 이 비밀번호를 안전한 곳에 보관하십시오! 암호화 비밀번호를 잃으면 데이터도 잃습니다.", + "Passwords cannot contain a ?. Passwords should be at least eight characters and contain a mix of lower and upper case, numbers, and special characters.": "비밀번호에 물음표(?)를 포함할 수 없습니다. 비밀번호는 최소 8자 이상이어야 하고 대소문자, 숫자, 특수문자를 포함해야 합니다.", + "Paste either or both public and private keys. If only a public key is entered, it will be stored alone. If only a private key is pasted, the public key will be automatically calculated and entered in the public key field. Click Generate Keypair to create a new keypair. Encrypted keypairs or keypairs with passphrases are not supported.": "공개 키와 개인 키를 붙여넣습니다. 공개 키만 입력한 경우 단독으로 저장합니다. 개인 키만 입력한 경우 자동으로 계산한 공개 키가 입력됩니다. 키쌍 생성을 누르면 새로운 키쌍을 생성합니다. 암호화된 키쌍 또는 비밀구절이 있는 키쌍은 지원하지 않습니다.", + "Paste the incoming webhook URL associated with this service.": "이 서비스와 연결된 웹훅 수신 URL을 붙여넣습니다.", + "Paste the certificate for the CA.": "인증기관의 인증서를 붙여넣습니다.", + "Paste the contents of your Certificate Signing Request here.": "인증서 서명 요청의 내용을 여기에 붙여넣습니다.", + "Paste the private key associated with the Certificate when available. Please provide a key at least 1024 bits long.": "가능한 경우 인증서와 연결된 개인 키를 붙여넣습니다. 키 길이는 최소 1024비트여야 합니다.", + "Pattern of naming custom snapshots to include in the replication with the periodic snapshot schedule. Enter the strftime(3) strings that match the snapshots to include in the replication.

When a periodic snapshot is not linked to the replication, enter the naming schema for manually created snapshots. Has the same %Y, %m, %d, %H, and %M string requirements as the Naming Schema in a Periodic Snapshot Task. Separate entries by pressing Enter.": "주기적인 스냅샷 일정에 따라 복제할 사용자 정의 스냅샷의 이름짓기 패턴입니다. 복제에 포함할 스냅샷과 일치하는 strftime(3) 문자열을 입력합니다.

주기적인 스냅샷 작업이 복제 작업과 연결되어있지 않은 경우, 수동으로 생성한 스냅샷의 이름짓기 방식을 입력하십시오. 주기적인 스냅샷 작업이름짓기 방식과 같이 %Y, %m, %d, %H%M 문자열이 필요합니다. Enter키를 눌러 항목을 구분합니다.", + "Pattern of naming custom snapshots to be replicated. Enter the name and strftime(3) %Y, %m, %d, %H, and %M strings that match the snapshots to include in the replication. Separate entries by pressing Enter. The number of snapshots matching the patterns are shown.": "복제할 사용자 정의 스냅샷의 이름짓기 패턴입니다. 복제에 포함할 스냅샷과 일치하는 이름과 strftime(3) %Y, %m, %d, %H%M 문자열을 입력합니다. Enter키를 눌러 항목을 구분합니다. 패턴과 일치하는 스냅샷의 수가 표시됩니다.", + "Peer Secret": "다른 지점 시크릿", + "Peer Secret (Confirm)": "다른 지점 시크릿 (수락)", + "Peer User": "다른 지점 사용자", + "Percentage used of dataset quota at which to generate a critical alert.": "심각 경고를 생성할 데이터셋 할당량의 사용율입니다.", + "Percentage used of dataset quota at which to generate a warning alert.": "위험 경고를 생성할 데이터셋 할당량의 사용율입니다.", + "Perform Reverse DNS Lookups": "역방향 DNS 조회 수행", + "Performs authentication from an LDAP server.": "LDAP 서버에서 인증을 수행합니다.", + "Photo Library API client secret generated from the Google API Console": "Google API 콘솔에서 생성한 사진 라이브러리 API 클라이언트 시크릿", "Pin vcpus": "", - "Please accept the terms of service for the given ACME Server.": "", - "Please click the button below to create a pool.": "", - "Please specify the name of the image to pull. Format for the name is \"registry/repo/image\"": "", - "Please specify trains from which UI should retrieve available applications for the catalog.": "", - "Pool is using more than {maxPct}% of available space": "", + "Please accept the terms of service for the given ACME Server.": "해당 ACME 서버의 서비스 약관에 동의해 주십시오.", + "Please click the button below to create a pool.": "풀을 생성하려면 아래 버튼을 눌러 주십시오.", + "Please specify the name of the image to pull. Format for the name is \"registry/repo/image\"": "가져올 이미지의 이름을 지정해 주십시오. 이름의 형식은 \"registry/repo/image\"입니다.", + "Please specify trains from which UI should retrieve available applications for the catalog.": "UI가 카탈로그에 사용 가능한 애플리케이션을 검색할 버전 구분을 지정해 주십시오.", + "Pool is using more than {maxPct}% of available space": "풀이 사용 가능한 공간의 {maxPct}% 이상을 사용함", "Power On Hours Ago": "", - "Predefined certificate extensions. Choose a profile that best matches your certificate usage scenario.": "", - "Predefined permission combinations:
Read: Read access and Execute permission on the object (RX).
Change: Read access, Execute permission, Write access, and Delete object (RXWD).
Full: Read access, Execute permission, Write access, Delete object, change Permissions, and take Ownership (RXWDPO).

For more details, see smbacls(1).": "", - "Prevent the user from logging in or using password-based services until this option is unset. Locking an account is only possible when Disable Password is No and a Password has been created for the account.": "", - "Priority Code Point": "", - "Proceed with upgrading the pool? WARNING: Upgrading a pool is a one-way operation that might make some features of the pool incompatible with older versions of TrueNAS: ": "", + "Predefined certificate extensions. Choose a profile that best matches your certificate usage scenario.": "사전 정의된 인증서 확장입니다. 인증서의 용도에 가장 부합하는 프로파일을 선택하십시오.", + "Predefined permission combinations:
Read: Read access and Execute permission on the object (RX).
Change: Read access, Execute permission, Write access, and Delete object (RXWD).
Full: Read access, Execute permission, Write access, Delete object, change Permissions, and take Ownership (RXWDPO).

For more details, see smbacls(1).": "사전 정의된 권한 조합:
읽기: 개체에 대한 읽기 및 실행 권한(RX)
변경: 읽기, 실행, 쓰기 및 개체 삭제(RXWD)
전체: 읽기, 실행, 쓰기, 개체 삭제, 권한 변경 및 소유권 취득(RXWDPO)

보다 자세한 사항은 smbacls(1)을(를) 참고하십시오.", + "Prevent the user from logging in or using password-based services until this option is unset. Locking an account is only possible when Disable Password is No and a Password has been created for the account.": "이 선택사항을 설정 해제하기 전에는 사용자가 로그인하거나 비밀번호 기반 서비스를 사용할 수 없습니다. 비밀번호 비활성화아니오이고 계정 비밀번호가 생성된 경우에만 계정을 잠글 수 있습니다.", + "Priority Code Point": "우선순위 코드 포인트(PCP)", + "Proceed with upgrading the pool? WARNING: Upgrading a pool is a one-way operation that might make some features of the pool incompatible with older versions of TrueNAS: ": "풀을 업그레이드하시겠습니까? 위험: 풀 업그레이드는 되돌릴 수 없고, 일부 풀 기능이 이전 버전의 TrueNAS와 호환되지 않을 수 있습니다: ", "Prototyping": "", - "Provide helpful notations related to the share, e.g. ‘Shared to everybody’. Maximum length is 120 characters.": "", - "Provides a plugin interface for Winbind to use varying backends to store SID/uid/gid mapping tables. The correct setting depends on the environment in which the NAS is deployed.": "", + "Provide helpful notations related to the share, e.g. ‘Shared to everybody’. Maximum length is 120 characters.": "공유에대한 유용한 설명을 제공합니다(예: '모두에게 공유됨'). 최대 길이는 120자입니다.", + "Provides a plugin interface for Winbind to use varying backends to store SID/uid/gid mapping tables. The correct setting depends on the environment in which the NAS is deployed.": "Winbind가 다양한 백엔드를 사용하여 SID/uid/gid 매핑 테이블을 저장할 수 있도록 플러그인 인터페이스를 제공합니다. 올바른 설정은 NAS가 배포된 환경에 따라 달라집니다.", "Prune By": "", "Pull": "", "Pull Image": "", @@ -398,16 +398,16 @@ "Quiet": "", "Realm": "", "Rear": "", - "Restrict share visibility to users with read or write access to the share. See the smb.conf manual page.": "", - "SHORT": "", + "Restrict share visibility to users with read or write access to the share. See the smb.conf manual page.": "공유가 읽기 또는 쓰기 권한을 가진 사용자에게만 보이도록 제한합니다. smb.conf 매뉴얼을 참고하십시오.", + "SHORT": "SHORT", "Series": "", - "Session dialect": "", - "Set for the LDAP server to disable authentication and allow read and write access to any client.": "", - "Set for the default configuration to listen on all interfaces using the known values of user: upsmon and password: fixmepass.": "", - "Set only if required by the NFS client. Set to allow serving non-root mount requests.": "", - "Set production status as active": "", - "Set specific times to snapshot the Source Datasets and replicate the snapshots to the Destination Dataset. Select a preset schedule or choose Custom to use the advanced scheduler.": "", - "Set to determine if the system participates in a browser election. Leave unset when the network contains an AD or LDAP server, or when Vista or Windows 7 machines are present.": "", + "Session dialect": "세션 변형", + "Set for the LDAP server to disable authentication and allow read and write access to any client.": "LDAP 서버가 인증을 비활성화하고 모든 클라이언트에 대해 읽기와 쓰기 권한을 허용하도록 설정합니다.", + "Set for the default configuration to listen on all interfaces using the known values of user: upsmon and password: fixmepass.": "모든 인터페이스가 기본적으로 알려진 값(사용자: upsmon, 비밀번호: fixmepass)을 사용해 수신하도록 구성합니다.", + "Set only if required by the NFS client. Set to allow serving non-root mount requests.": "NFS 클라이언트에서 요청하는 경우 설정합니다. root가 아닌 탑재 요청을 허용합니다.", + "Set production status as active": "운영 상태를 활성화합니다.", + "Set specific times to snapshot the Source Datasets and replicate the snapshots to the Destination Dataset. Select a preset schedule or choose Custom to use the advanced scheduler.": "원본 데이터셋의 스냅샷을 저장하고 도착지 데이터셋으로 복제할 시각을 지정합니다. 사전설정 일정을 선택하거나 사용자 정의를 선택해 고급 일정관리를 사용합니다.", + "Set to determine if the system participates in a browser election. Leave unset when the network contains an AD or LDAP server, or when Vista or Windows 7 machines are present.": "시스템을 브라우저 선정에 포함할지 여부를 결정합니다. 네트워크에 AD 또는 LDAP 서버가 있거나, Vista 또는 Windows 7 장치가 있는 경우 설정하지 않습니다.", "Short": "", "Tail Lines": "", "Thick": "", @@ -478,7 +478,7 @@ "MOVE: After files are copied from the source to the destination, they are deleted from the source. Files with the same names on the destination are overwritten.": "MOVE: 원본 파일이 대상으로 복사된 후, 원본에서는 해당 파일이 삭제됩니다. 대상에 이미 동일한 이름의 파일이 있는 경우, 해당 파일들이 덮어씌워집니다.", "SET will changes all destination datasets to readonly=on after finishing the replication.
REQUIRE stops replication unless all existing destination datasets to have the property readonly=on.
IGNORE disables checking the readonly property during replication.": "SET은 복제 작업이 완료된 후 대상 데이터셋의 모든 속성을 readonly=on으로 변경합니다.
REQUIRE은 대상 데이터셋의 모든 속성이 readonly=on으로 설정되어 있지 않으면 복제를 중지합니다.
IGNORE는 복제 중에 readonly 속성을 확인하지 않도록 설정합니다.", "SYNC: Files on the destination are changed to match those on the source. If a file does not exist on the source, it is also deleted from the destination.": "SYNC: 대상에 있는 파일들이 원본과 일치하도록 변경됩니다. 원본에 존재하지 않는 파일은 대상에서도 삭제됩니다.", - "WARNING: Rolling the dataset back destroys data on the dataset and can destroy additional snapshots that are related to the dataset. This can result in permanent data loss! Do not roll back until all desired data and snapshots are backed up.": "위험: 데이터셋을 되돌리면 포함된 데이터는 물론 연계된 스냅샷도 파기될 수 있습니다. 데이터는 영구히 손실됩니다! 필요한 데이터와 스냅샷을 백업하기 전에는 되돌리기를 수행하지 마십시오.", + "WARNING: Rolling the dataset back destroys data on the dataset and can destroy additional snapshots that are related to the dataset. This can result in permanent data loss! Do not roll back until all desired data and snapshots are backed up.": "위험: 데이터셋을 되돌리면 포함된 데이터는 물론 관련된 스냅샷도 파기될 수 있습니다. 데이터는 영구히 손실됩니다! 필요한 데이터와 스냅샷을 백업하기 전에는 되돌리기를 수행하지 마십시오.", "WARNING: The configuration file contains sensitive data like system passwords. However, SSH keys that are stored in /root/.ssh are NOT backed up by this operation. Additional sensitive information can be included in the configuration file.
": "위험: 구성 파일에는 시스템 비밀번호와 같은 민감한 데이터가 포함되어 있습니다. 그러나 /root/.ssh에 저장된 SSH 키는 이 작업에 의해 백업되지 않습니다. 구성 파일에 추가적인 민감한 정보가 포함될 수 있습니다.
", "Warning: The WireGuard service must be active on the client system to access the TrueCommand UI.": "위험: 클라이언트 시스템에서 WireGuard 서비스를 활성화해야 TrueCommand UI에 접속할 수 있습니다.", "0 disables quotas. Specify a maximum allowed space for this dataset.": "0은 할당량을 비활성화합니다. 이 데이터셋에 대한 최대 허용 공간을 지정하십시오.", @@ -642,7 +642,7 @@ "Add Periodic S.M.A.R.T. Test": "주기적인 S.M.A.R.T. 검사 추가", "Add Periodic Snapshot Task": "주기적인 스냅샷 작업 추가", "Add Pool": "풀 추가", - "Add Portal": "포탈 추가", + "Add Portal": "포털 추가", "Add Privilege": "권한 추가", "Add Proxy": "프록시 추가", "Add Replication Task": "복제 작업 추가", @@ -1097,7 +1097,7 @@ "CLI": "CLI", "CN": "CN", "CN Realm": "CN 영역", - "CONVEYANCE": "양도", + "CONVEYANCE": "CONVEYANCE", "COPY": "복사", "CPU": "CPU", "CPU & Memory": "CPU와 메모리", @@ -1175,7 +1175,7 @@ "Change the default password to improve system security. The new password cannot contain a space or #.": "기본 비밀번호를 바꿔 시스템 보안을 향상시킵니다. 비밀번호는 공백이나 #을(를) 포함할 수 없습니다.", "Changelog": "변경내역", "Changes Saved": "변경사항 저장됨", - "Changing to a nightly train is one-way. Changing back to a stable train is not supported! ": "개발자 버전으로의 전환은 되돌릴 수 없습니다. 안정 버전으로 되돌릴 수 없습니다!", + "Changing to a nightly train is one-way. Changing back to a stable train is not supported! ": "개발자 버전으로의 전환은 되돌릴 수 없습니다. 안정 버전으로 돌아갈 수 없습니다!", "Channel": "채널", "Channel {n}": "채널 {n}", "Check": "확인", @@ -1674,7 +1674,7 @@ "Delete zvol {name}": "Zvol {name} 삭제", "Delete {n, plural, one {# user} other {# users}} with this primary group?": "{n}명의 사용자를 기본 그룹에서 삭제하시겠습니까?", "Delete {name}?": "{name}을(를) 삭제하시겠습니까?", - "Delete {n} associated {n, plural, one {extent} other {extents}}": "연계된 {n}개의 익스텐트 삭제", + "Delete {n} associated {n, plural, one {extent} other {extents}}": "연결된 {n}개의 익스텐트 삭제", "Deleted {n, plural, one {# snapshot} other {# snapshots} }": "{n}개의 스냅샷 삭제됨", "Deleting exporter": "내보내기 삭제중", "Deleting interfaces while HA is enabled is not allowed.": "HA가 활성화된 상태에서는 인터페이스를 삭제할 수 없습니다.", @@ -1902,7 +1902,7 @@ "Edit NTP Server": "NTP 서버 수정", "Edit Periodic Snapshot Task": "주기적인 스냅샷 작업 수정", "Edit Permissions": "권한 수정", - "Edit Portal": "포탈 수정", + "Edit Portal": "포털 수정", "Edit Privilege": "권한 수정", "Edit Proxy": "프록시 수정", "Edit Replication Task": "복제 작업 수정", @@ -2002,7 +2002,7 @@ "Enabled Protocols": "활성화 된 프로토콜", "Enabling Time Machine on an SMB share requires restarting the SMB service.": "SMB 공유를 통한 타임머신을 활성화하려면 SMB 서비스를 재시작해야 합니다.", "Enabling allows using a password to authenticate the SSH login. Warning: when directory services are enabled, allowing password authentication can grant access to all users imported by the directory service.
Disabling changes authentication to require keys for all users. This requires additional setup on both the SSH client and server.": "SSH 로그인 인증에 비밀번호를 사용할 수 있도록 허용합니다. 위험: 디렉터리 서비스가 활성화되어 있다면, 비밀번호 인증은 디렉터리 서비스에서 불러온 사용자에게 접근 권한을 부여합니다.
비활성화하면 모든 사용자의 인증에 키를 요구합니다. 이를 위해 SSH 클라이언트와 서버에 추가적인 설정이 필요합니다.", - "Enabling redirect will require all URLs served from current host to be served via HTTPS regardless of port used. This may make some App portals inaccessible if they don't use HTTPS. Do you wish to continue?": "재연결을 활성화하면 현재 호스트가 제공하는 모든 URL이 포트에 상관없이 HTTPS로 연결됩니다. HTTPS를 사용하지 않는 앱 포탈의 경우 접속이 불가할 수 있습니다. 계속하시겠습니까?", + "Enabling redirect will require all URLs served from current host to be served via HTTPS regardless of port used. This may make some App portals inaccessible if they don't use HTTPS. Do you wish to continue?": "재연결을 활성화하면 현재 호스트가 제공하는 모든 URL이 포트에 상관없이 HTTPS로 연결됩니다. HTTPS를 사용하지 않는 앱 포털의 경우 접속이 불가할 수 있습니다. 계속하시겠습니까?", "Enabling this option is not recommended as it bypasses a security mechanism.": "이 옵션은 보안 설정을 우회하므로 활성화를 권장하지 않습니다.", "Encipher Only": "암호화 전용", "Enclosure": "인클로저", @@ -2552,7 +2552,7 @@ "Install Manual Update File": "수동 업데이트 파일 설치", "Install NVIDIA Drivers": "NVIDIA 드라이버 설치", "Install via YAML": "YAML로 설치", - "Installation Media": "설치 미디어", + "Installation Media": "설치 매체", "Installed": "설치됨", "Installed Apps": "설치된 앱", "Installer image file": "설치 이미지 파일", @@ -2787,7 +2787,7 @@ "Loss of Functionality": "기능 상실", "Low Bandwidth (4)": "낮은 대역폭 (4)", "Low Capacity": "낮은 용량", - "Lowest Temperature": "낮은 온도", + "Lowest Temperature": "최저 온도", "MAC Address": "MAC 주소", "MAC VLAN": "MAC VLAN", "MOTD": "MOTD", @@ -2861,8 +2861,8 @@ "Maximum Upload Parts": "최대 업로드 부분", "Maximum value is {max}": "최대값은 {max}입니다", "May": "5월", - "Media Subtype": "미디어 보조유형", - "Media Type": "미디어 유형", + "Media Subtype": "매체 보조유형", + "Media Type": "매체 유형", "Members": "구성원", "Memory": "메모리", "Memory Reports": "메모리 보고서", @@ -3084,13 +3084,13 @@ "No enclosure": "인클로저 없음", "No errors": "오류 없음", "No events to display.": "표시할 이벤트가 없습니다.", - "No extents associated.": "연계된 익스텐트가 없습니다.", + "No extents associated.": "연결된 익스텐트가 없습니다.", "No images found": "이미지 찾을 수 없음", "No instances": "인스턴스 없음", "No interfaces configured with Virtual IP.": "가상 IP로 설정된 인터페이스가 없습니다.", "No items have been added yet.": "추가된 항목이 아직 없습니다.", "No jobs running.": "실행중인 작업이 없습니다.", - "No logs are available": "사용 가능한 기록이 없습니다.", + "No logs are available": "사용 가능한 기록 없음", "No logs are available for this task.": "이 작업에 대한 기록이 없습니다.", "No logs available": "사용 가능한 기록 없음", "No logs yet": "아직 기록되지 않음", @@ -3109,7 +3109,7 @@ "No similar apps found.": "비슷한 앱 찾을 수 없음", "No snapshots sent yet": "아직 전송된 스냅샷 없음", "No temperature data was reported by the system. There can be a number of reasons why this might occur.": "시스템으로부터 보고된 온도 데이터가 없습니다. 여러 문제가 복합적일 수 있습니다.", - "No unassociated extents available.": "사용 가능한 미연계 인스텐트가 없습니다.", + "No unassociated extents available.": "사용 가능한 미연결 인스텐트가 없습니다.", "No unused disks": "사용하지 않은 디스크 없음", "No update found.": "업데이트를 찾을 수 없습니다.", "No updates available.": "가능한 업데이트가 없습니다.", @@ -3143,7 +3143,7 @@ "OAuth Client ID": "OAuth 클라이언트 ID", "OAuth Client Secret": "OAuth 클라이언트 시크릿", "OAuth Token for current session": "현재 세션에 대한 OAuth 토큰", - "OFFLINE": "오프라인", + "OFFLINE": "OFFLINE", "OK": "확인", "OS": "운영체제", "OS Version": "운영체제 버전", @@ -3291,9 +3291,9 @@ "Port": "포트", "Port number on the remote system to use for the SSH connection.": "SSH 연결에 사용할 원격 시스템의 포트 번호입니다.", "Port or Hostname": "포트 또는 호스트 이름", - "Portal": "포탈", - "Portal Group ID": "포탈 그룹 ID", - "Portals": "포탈", + "Portal": "포털", + "Portal Group ID": "포털 그룹 ID", + "Portals": "포털", "Ports": "포트", "Post Init": "초기화 이후", "Post Script": "이후 스크립트", @@ -3456,7 +3456,7 @@ "Remove Keep Flag": "플래그 유지 제거", "Remove device": "장치 제거", "Remove device {name}?": "장치 {name}을(를) 제거하시겠습니까?", - "Remove extent association": "익스텐트 연계 제거", + "Remove extent association": "익스텐트 연결 제거", "Remove file": "파일 제거", "Remove file?": "파일을 제거하시겠습니까?", "Remove iXVolumes": "iXVolume 제거", @@ -3804,7 +3804,7 @@ "Secret Confirm must match Secret": "시크릿 확인값은 시크릿과 일치해야 함", "Secret Encryption Key": "시크릿 암호화 키", "Secret Key": "시크릿 키", - "Secret and Peer Secret can not be the same.": "시크릿과 피어 시크릿은 같을 수 없습니다.", + "Secret and Peer Secret can not be the same.": "시크릿과 다른 지점 시크릿은 같을 수 없습니다.", "Secret and confirmation should match.": "시크릿과 학인값은 일치해야 합니다.", "Section Help": "구역 도움말", "Secure data within this dataset. Data is unusable until unlocked with an encryption key or passphrase. If parent dataset has encryption enabled, it is not possible to disable this option.": "이 데이터셋의 자료를 보호합니다. 암호화 키 또는 비밀구절로 잠금을 해제하기 전에는 자료를 사용할 수 없습니다. 상위 데이터셋의 암호화가 활성화 되었다면, 이 선택사항을 비활성화할 수 없습니다.", @@ -3861,7 +3861,7 @@ "Select an existing SSH connection to a remote system or choose Create New to create a new SSH connection.": "리모트 시스템과의 SSH 연결을 선택하거나 새로 만들기를 선택해 새로운 SSH 연결을 만듭니다.", "Select an existing SSH connection to a remote system or choose Create New to create a new SSH connection.": "리모트 시스템과의 SSH 연결을 선택하거나 새로 만들기를 선택해 새로운 SSH 연결을 만듭니다.", "Select an existing extent.": "기존 익스텐트를 선택합니다.", - "Select an existing portal or choose Create New to configure a new portal.": "기존 포탈을 선택하거나 새로 만들기를 선택해 새로운 포탈을 구성합니다.", + "Select an existing portal or choose Create New to configure a new portal.": "기존 포털을 선택하거나 새로 만들기를 선택해 새로운 포털을 구성합니다.", "Select an existing realm that was added in Directory Services > Kerberos Realms.": "디렉터리 서비스 > Kerberos 영역에 추가된 기존 영역을 선택하십시오.", "Select an existing target.": "기존 대상을 선택합니다.", "Select an unused disk to add to this vdev.
WARNING: any data stored on the unused disk will be erased!": "이 VDEV에 추가할 사용하지 않은 디스크를 선택하합니다.
위험: 사용되지 않은 디스크에 저장된 데이터는 지워집니다!", @@ -3880,7 +3880,7 @@ "Select the syslog(3) level of the SFTP server.": "SFTP 서버의 syslog(3) 수준을 선택합니다.", "Select the Certificate Signing Request to sign the Certificate Authority with.": "인증 기관에 서명할 인증서 서명 요청을 선택합니다.", "Select the Class of Service. The available 802.1p Class of Service ranges from Best effort (default) to Network control (highest).": "우선순위를 선택합니다. 사용 가능한 802.1p 우선순위 범위는 최고의 효울(기본)부터 네트워크 제어(가장 높음)까지입니다.", - "Select the IP addresses to be listened on by the portal. Click ADD to add IP addresses with a different network port. The address 0.0.0.0 can be selected to listen on all IPv4 addresses, or :: to listen on all IPv6 addresses.": "포탈이 수신할 IP 주소를 선택합니다. 추가를 눌러 다른 네트워크 포트가 있는 IP 주소를 추가합니다. 0.0.0.0(IPv4) 또는 ::(IPv6)을(를) 통해 모든 주소에서 수신할 수 있습니다.", + "Select the IP addresses to be listened on by the portal. Click ADD to add IP addresses with a different network port. The address 0.0.0.0 can be selected to listen on all IPv4 addresses, or :: to listen on all IPv6 addresses.": "포털이 수신할 IP 주소를 선택합니다. 추가를 눌러 다른 네트워크 포트가 있는 IP 주소를 추가합니다. 0.0.0.0(IPv4) 또는 ::(IPv6)을(를) 통해 모든 주소에서 수신할 수 있습니다.", "Select the VLAN Parent Interface. Usually an Ethernet card connected to a switch port configured for the VLAN. New link aggregations are not available until the system is restarted.": "VLAN 상위 인터페이스를 선택합니다. 보통 VLAN 포트에 연결된 이더넷 카드입니다. 새로운 Link Aggregation은 시스템을 재시작할 때까지 사용할 수 없습니다.", "Select the appropriate environment.": "적절한 환경을 선택합니다.", "Select the appropriate level of criticality.": "적절한 심각도를 선택합니다.", @@ -4032,7 +4032,7 @@ "Set to query the cn instead of uid attribute for the user name in LDAP.": "LDAP에서 사용자 이름에 대한 uid 속성 대신 cn 속성을 쿼리하도록 설정합니다.", "Set to reduce the size of data to transmit. Recommended for slow connections.": "전송할 데이터의 크기를 줄입니다. 연결 속도가 느린 경우 권장합니다.", "Set to remove all ACLs from the current dataset. ACLs are also recursively stripped from directories and child datasets when those options are set.": "현재 데이터셋의 모든 ACL을(를) 제거합니다. 선택사항을 통해 디렉터리와 하위 데이터셋에서도 ACL을(를) 제거할 수 있습니다.", - "Set to remove the data associated with this Virtual Machine (which will result in data loss if the data is not backed up). Unset to leave the data intact.": "이 가상머신과 연계된 데이터를 제거합니다(데이터를 백업하지 않으면 모두 소실됩니다). 선택하지 않으면 데이터를 그대로 유지합니다.", + "Set to remove the data associated with this Virtual Machine (which will result in data loss if the data is not backed up). Unset to leave the data intact.": "이 가상머신과 연결된 데이터를 제거합니다(데이터를 백업하지 않으면 모두 소실됩니다). 선택하지 않으면 데이터를 그대로 유지합니다.", "Set to restrict SSH access in certain circumstances to only members of BUILTIN\\Administrators": "특정 상황에서 SSH 액세스를 BUILTIN\\Administrators 의 구성원으로 제한", "Set to run resilver tasks between the configured times.": "구성한 시각 사이에 리실버 작업을 실행합니다.", "Set to save the temporary file from each updated file to a holding directory until the end of the transfer when all transferred files are renamed into place.": "파일을 제자리에 전송하고 이름을 원래대로 바꾸는 전송 작업을 마칠 때까지 각 갱신된 파일의 임시 파일을 보관 디렉터리에 저장합니다.", @@ -4093,8 +4093,8 @@ "Sharing iSCSI Host Write": "iSCSI 공유 호스트 쓰기", "Sharing iSCSI Initiator Read": "iSCSI 공유 이니시에이터 읽기", "Sharing iSCSI Initiator Write": "iSCSI 공유 이니시에이터 쓰기", - "Sharing iSCSI Portal Read": "iSCSI 공유 포탈 읽기", - "Sharing iSCSI Portal Write": "iSCSI 공유 포탈 쓰기", + "Sharing iSCSI Portal Read": "iSCSI 공유 포털 읽기", + "Sharing iSCSI Portal Write": "iSCSI 공유 포털 쓰기", "Sharing iSCSI Read": "iSCSI 공유 읽기", "Sharing iSCSI Target Extent Read": "iSCSI 공유 대상 익스텐트 읽기", "Sharing iSCSI Target Extent Write": "iSCSI 공유 대상 익스텐트 쓰기", @@ -4519,7 +4519,7 @@ "The preconfigured system Certificate to use for authenticating the TLS protocol connection to the remote system log server.": "원격 시스템 기록 서버에 연결하기 위한 TLS 프로토콜 인증을 위해 사전구성된 시스템 인증서입니다.", "The product of vCPUs, cores and threads must not exceed {maxVcpus} on this system.": "vCPU, 코어, 스레드의 곱은 이 시스템의 {maxVcpus}을(를) 초과할 수 없습니다.", "The remote node must be rebooted because": "원격 노드를 반드시 재시작해야합니다:", - "The rollback will destroy any related intermediate, child dataset, and cloned snapshots that are newer than the rollback snapshot.": "되돌리기를 수행하면 되돌리기 스냅샷보다 후에 생성된 모든 연계된 중간내용, 하위 데이터셋, 복제한 스냅샷을 파기합니다.", + "The rollback will destroy any related intermediate, child dataset, and cloned snapshots that are newer than the rollback snapshot.": "되돌리기를 수행하면 되돌리기 스냅샷보다 후에 생성된 모든 관련된 중간내용, 하위 데이터셋, 복제한 스냅샷을 파기합니다.", "The search base where group objects can be found in the LDAP server.": "LDAP 서버에서 그룹 객체를 검색할 기준입니다.", "The search base where user objects can be found in the LDAP server.": "LDAP 서버에서 사용자 객체를 검색할 기준입니다.", "The secret used to generate OTPs. The secret is produced by the system when Two-Factor Authentication is first activated.": "OTP를 생성하는 데 사용하는 시크릿입니다. 시크릿은 2단계 인증을 처음 활성화할 때 시스템이 발급합니다.", @@ -4587,7 +4587,7 @@ "This dataset is used by the system": "시스템이 사용하는 데이터셋", "This dataset is used by: {apps}": "데이터셋을 사용하는 앱: {apps}", "This dataset is used by: {vms}": "데이터셋을 사용하는 VM: {vms}", - "This dataset is used to store apps config and other container related data": "이 데이터셋은 앱 구성과 컨테이너와 연계된 데이터를 저장하는데 사용합니다.", + "This dataset is used to store apps config and other container related data": "이 데이터셋은 앱 구성과 컨테이너와 관련된 데이터를 저장하는데 사용합니다.", "This disk is part of the exported pool {pool}. Adding this disk to a new or other existing pools will make {pool} unable to import. You will lose any and all data in {pool}. Please make sure you have backed up any sensitive data in {pool} before reusing/repurposing this disk.": "이 디스크는 내보낸 풀 {pool}의 일부입니다. 이 디스크를 새로운 풀 또는 기존 풀에 추가하면 {pool}을(를) 불러올 수 없게 됩니다. {pool}에 있는 모든 데이터를 잃게 됩니다. 이 디스크를 재사용/재구성 하기 전에 {pool}에 있는 민감한 데이터를 백업했는지 확인하시기 바랍니다.", "This disk is part of the exported pool {pool}. Reusing this disk will make {pool} unable to import. You will lose any and all data in {pool}. Please make sure any sensitive data in {pool} is backed up before reusing/repurposing this disk.": "이 디스크는 내보낸 풀 {pool}의 일부입니다. 이 디스크를 재사용하면 {pool}을(를) 불러올 수 없게 됩니다. {pool}에 있는 모든 데이터를 잃게 됩니다. 이 디스크를 재사용/재구성 하기 전에 {pool}에 있는 민감한 데이터를 백업했는지 확인하시기 바랍니다.", "This disk is part of the exported pool {pool}. Wiping this disk will make {pool} unable\n to import. You will lose any and all data in {pool}. Please make sure that any sensitive data in {pool} is backed up before wiping this disk.": "이 디스크는 내보낸 풀 {pool}의 일부입니다. 이 디스크를 완전히 지우면 {pool}을(를) 불러올 수 없게 됩니다. {pool}에 있는 모든 데이터를 잃게 됩니다. 이 디스크를 완전히 지우기 전에 {pool}에 있는 민감한 데이터를 백업했는지 확인하시기 바랍니다.", @@ -5057,7 +5057,7 @@ "Web Interface IPv4 Address": "웹 인터페이스 IPv4 주소", "Web Interface IPv6 Address": "웹 인터페이스 IPv6 주소", "Web Interface Port": "웹 인터페이스 포트", - "Web Portal": "웹 포탈", + "Web Portal": "웹 포털", "Web Shell Access": "웹 셸 접근", "WebDAV": "WebDAV", "WebDAV Service": "WebDAV 서비스", @@ -5140,7 +5140,7 @@ "Yesterday": "어제", "You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "{appName}을(를) 사용자 앱으로 변환하려고 합니다. 이를 통해 yaml 파일을 직접 수정할 수 있습니다.\n위험. 이 작업은 되돌릴 수 없습니다.", "You are about to delete the target \"{name}\".": "대상 \"{name}\"을(를) 삭제하려고 합니다.", - "You are about to delete the target \"{name}\". You may also choose to delete all extents associated with this target. Note the volumes will not be deleted with the extents.": "대상 \"{name}\"을(를) 삭제하려고 합니다. 이 대상과 연계된 모든 익스텐트를 삭제하도록 선택할 수 있습니다. 익스텐트에 포함된 볼륨은 삭제되지 않습니다.", + "You are about to delete the target \"{name}\". You may also choose to delete all extents associated with this target. Note the volumes will not be deleted with the extents.": "대상 \"{name}\"을(를) 삭제하려고 합니다. 이 대상과 연결된 모든 익스텐트를 삭제하도록 선택할 수 있습니다. 익스텐트에 포함된 볼륨은 삭제되지 않습니다.", "You are trying to open:
\n{url}

\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.
\nThis may create issues if app does not support secure connections.
\n
\nYou can try opening app url in an incognito mode.
\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "다음을 열려고 합니다:
\n{url}

\nHTTP를 HTTPS로 재연결하는 설정이 활성화 되었기에 브라우저는 이 주소를 HTTPS로 강제 연결합니다.
\n앱이 안전한 연결을 지원하지 않는 경우 문제가 발생할 수 있습니다.
\n
\n앱 주소를 시크릿 모드에서 열어볼 수 있습니다.
\n도는 설정에서 재연결을 비활성화 하고, 브라우저 캐시를 지운 뒤 다시 시도합니다.", "You are using an insecure connection. Switch to HTTPS for secure access.": "안전하지 않은 연결입니다. HTTPS로 전환해 안전하게 접근합니다.", "You can also vote for new features on our forum.": "또한 포럼을 통해 새로운 기능들에 투표할 수 있습니다.", @@ -5315,4 +5315,4 @@ "{used} of {total} ({used_pct})": "{total} 중 {used} ({used_pct})", "{version} is available!": "{version}이 준비되었습니다!", "{view} on {enclosure}": "{enclousure}의 {view}" -} \ No newline at end of file +} From bdfb42e5a0a7af3fdca5f86233fd287ae98939b5 Mon Sep 17 00:00:00 2001 From: Evgeny Stepanovych Date: Fri, 3 Jan 2025 08:40:04 -0500 Subject: [PATCH 02/18] NAS-133299: Extend window not showing up (#11270) --- src/app/pages/storage/modules/devices/devices.component.ts | 3 --- .../storage/modules/devices/stores/devices-store.service.ts | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/pages/storage/modules/devices/devices.component.ts b/src/app/pages/storage/modules/devices/devices.component.ts index 82b42155405..fbeffad54a8 100644 --- a/src/app/pages/storage/modules/devices/devices.component.ts +++ b/src/app/pages/storage/modules/devices/devices.component.ts @@ -85,9 +85,6 @@ const raidzItems = [TopologyItemType.Raidz, TopologyItemType.Raidz1, TopologyIte TreeNodeToggleDirective, TreeNodeOutletDirective, ], - providers: [ - DevicesStore, - ], }) export class DevicesComponent implements OnInit, AfterViewInit { protected readonly requiredRoles = [Role.FullAdmin]; diff --git a/src/app/pages/storage/modules/devices/stores/devices-store.service.ts b/src/app/pages/storage/modules/devices/stores/devices-store.service.ts index 5bdfdf9424e..715eb8ca70d 100644 --- a/src/app/pages/storage/modules/devices/stores/devices-store.service.ts +++ b/src/app/pages/storage/modules/devices/stores/devices-store.service.ts @@ -32,7 +32,9 @@ const initialState: DevicesState = { disksWithSmartTestSupport: [], }; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class DevicesStore extends ComponentStore { readonly isLoading$ = this.select((state) => state.isLoading); readonly error$ = this.select((state) => state.error); From 601e061343511399b013bf03c01bd47a70cec27c Mon Sep 17 00:00:00 2001 From: Evgeny Stepanovych Date: Fri, 3 Jan 2025 08:40:17 -0500 Subject: [PATCH 03/18] NAS-133353 / 25.04 / Add tests in reporting section (#11274) --- .../components/report/report.component.html | 6 +- .../components/report/report.component.ts | 4 - .../reports-global-controls.component.spec.ts | 159 ++++++++++++++++++ .../reports-global-controls.component.ts | 18 +- 4 files changed, 169 insertions(+), 18 deletions(-) create mode 100644 src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.spec.ts diff --git a/src/app/pages/reports-dashboard/components/report/report.component.html b/src/app/pages/reports-dashboard/components/report/report.component.html index 2aa140ca4c0..96e917a4f06 100644 --- a/src/app/pages/reports-dashboard/components/report/report.component.html +++ b/src/app/pages/reports-dashboard/components/report/report.component.html @@ -1,9 +1,5 @@
-
+
@if (isReady) { diff --git a/src/app/pages/reports-dashboard/components/report/report.component.ts b/src/app/pages/reports-dashboard/components/report/report.component.ts index e857292d0f5..eafbb00d18b 100644 --- a/src/app/pages/reports-dashboard/components/report/report.component.ts +++ b/src/app/pages/reports-dashboard/components/report/report.component.ts @@ -277,10 +277,6 @@ export class ReportComponent implements OnInit, OnChanges { this.customZoom = true; } - setChartInteractive(value: boolean): void { - this.isActive = value; - } - timeZoomReset(): void { this.zoomLevelIndex = this.zoomLevelMax; const rrdOptions = this.convertTimeSpan(this.currentZoomLevel); diff --git a/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.spec.ts b/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.spec.ts new file mode 100644 index 00000000000..67f1a5b30e6 --- /dev/null +++ b/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.spec.ts @@ -0,0 +1,159 @@ +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { + fakeAsync, flush, flushMicrotasks, tick, +} from '@angular/core/testing'; +import { MatButtonHarness } from '@angular/material/button/testing'; +import { MatMenuHarness } from '@angular/material/menu/testing'; +import { ActivatedRoute } from '@angular/router'; +import { + createComponentFactory, + mockProvider, + Spectator, +} from '@ngneat/spectator/jest'; +import { Store } from '@ngrx/store'; +import { provideMockStore } from '@ngrx/store/testing'; +import { of } from 'rxjs'; +import { IxSelectHarness } from 'app/modules/forms/ix-forms/components/ix-select/ix-select.harness'; +import { IxSlideToggleHarness } from 'app/modules/forms/ix-forms/components/ix-slide-toggle/ix-slide-toggle.harness'; +import { + ReportsGlobalControlsComponent, +} from 'app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component'; +import { ReportTab, ReportType } from 'app/pages/reports-dashboard/interfaces/report-tab.interface'; +import { ReportsService } from 'app/pages/reports-dashboard/reports.service'; +import { autoRefreshReportsToggled } from 'app/store/preferences/preferences.actions'; +import { selectPreferences } from 'app/store/preferences/preferences.selectors'; + +describe('ReportsGlobalControlsComponent', () => { + let spectator: Spectator; + let loader: HarnessLoader; + const createComponent = createComponentFactory({ + component: ReportsGlobalControlsComponent, + providers: [ + mockProvider(ReportsService, { + getReportGraphs: jest.fn(() => of([])), + getReportTabs: jest.fn(() => [ + { label: 'Disk', value: ReportType.Disk }, + { label: 'CPU', value: ReportType.Cpu }, + { label: 'UPS', value: ReportType.Ups }, + ] as ReportTab[]), + getDiskDevices: jest.fn(() => of([ + { label: 'sda', value: 'sda' }, + { label: 'sdb', value: 'sdb' }, + ])), + getDiskMetrics: jest.fn(() => of([ + { label: 'Disk I/O', value: 'disk' }, + { label: 'Disk Temperature', value: 'disktemp' }, + ])), + }), + provideMockStore({ + selectors: [ + { + selector: selectPreferences, + value: { + autoRefreshReports: true, + }, + }, + ], + }), + mockProvider(ActivatedRoute, { + routeConfig: { + path: ReportType.Disk, + }, + snapshot: { + queryParams: { + disks: ['sda'], + }, + }, + }), + ], + }); + + beforeEach(fakeAsync(() => { + spectator = createComponent(); + loader = TestbedHarnessEnvironment.loader(spectator.fixture); + })); + + describe('report selector', () => { + it('shows a list of available reports', async () => { + const reportMenu = await loader.getHarness(MatMenuHarness); + + await reportMenu.open(); + const menuItems = await reportMenu.getItems(); + + expect(menuItems).toHaveLength(3); + expect(await menuItems[0].getText()).toBe('Disk'); + expect(await menuItems[1].getText()).toBe('CPU'); + expect(await menuItems[2].getText()).toBe('UPS'); + }); + + it('marks currently selected menu item based on current route', async () => { + const reportMenu = await loader.getHarness(MatMenuHarness); + + expect(await reportMenu.getTriggerText()).toBe('Disk'); + }); + }); + + describe('disk reports', () => { + it('shows disks multiselect when disk report is selected', async () => { + const devices = await loader.getHarness(IxSelectHarness.with({ label: 'Devices' })); + const options = await devices.getOptionLabels(); + + expect(options).toEqual(['sda', 'sdb']); + }); + + it('shows disk metrics when disk report is selected', async () => { + const metrics = await loader.getHarness(IxSelectHarness.with({ label: 'Metrics' })); + const options = await metrics.getOptionLabels(); + + expect(options).toEqual(['Disk I/O', 'Disk Temperature']); + }); + + it('pre-selects disks based on route params', async () => { + const devices = await loader.getHarness(IxSelectHarness.with({ label: 'Devices' })); + + expect(await devices.getValue()).toEqual(['sda']); + }); + + it('emits (diskOptionsChanged) when user changes disk or disk metric selection', fakeAsync(async () => { + jest.spyOn(spectator.component.diskOptionsChanged, 'emit'); + + const devices = await loader.getHarness(IxSelectHarness.with({ label: 'Devices' })); + await devices.setValue(['sdb']); + + flush(1); + flushMicrotasks(); + + tick(1000); + + expect(spectator.component.diskOptionsChanged.emit).toHaveBeenCalledWith({ + devices: ['sdb'], + metrics: ['disk', 'disktemp'], + }); + })); + }); + + describe('Auto Refresh toggle', () => { + it('shows Auto Refresh toggle with current value based on user preferences', async () => { + const autoRefreshToggle = await loader.getHarness(IxSlideToggleHarness.with({ label: 'Auto Refresh' })); + + expect(await autoRefreshToggle.getValue()).toBe(true); + }); + + it('dispatches autoRefreshReportsToggled() action when Auto Refresh is toggled', async () => { + const store$ = spectator.inject(Store); + jest.spyOn(store$, 'dispatch'); + + const autoRefreshToggle = await loader.getHarness(IxSlideToggleHarness.with({ label: 'Auto Refresh' })); + await autoRefreshToggle.toggle(); + + expect(store$.dispatch).toHaveBeenCalledWith(autoRefreshReportsToggled()); + }); + }); + + it('shows Exporters button', async () => { + const exportersButton = await loader.getHarness(MatButtonHarness.with({ text: 'Exporters' })); + + expect(exportersButton).toBeTruthy(); + }); +}); diff --git a/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.ts b/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.ts index 7e6a793d2f3..d6c1a9e7221 100644 --- a/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.ts +++ b/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.ts @@ -50,19 +50,19 @@ import { waitForPreferences } from 'app/store/preferences/preferences.selectors' export class ReportsGlobalControlsComponent implements OnInit { readonly diskOptionsChanged = output<{ devices: string[]; metrics: string[] }>(); - form = this.fb.group({ + protected form = this.fb.group({ autoRefresh: [false], devices: [[] as string[]], metrics: [[] as string[]], }); - activeTab: ReportTab; - allTabs: ReportTab[]; - diskDevices$ = this.reportsService.getDiskDevices(); - diskMetrics$ = this.reportsService.getDiskMetrics(); + protected activeTab: ReportTab; + protected allTabs: ReportTab[]; + protected diskDevices$ = this.reportsService.getDiskDevices(); + protected diskMetrics$ = this.reportsService.getDiskMetrics(); - readonly ReportType = ReportType; - readonly searchableElements = reportingGlobalControlsElements; + protected readonly ReportType = ReportType; + protected readonly searchableElements = reportingGlobalControlsElements; constructor( private fb: FormBuilder, @@ -78,11 +78,11 @@ export class ReportsGlobalControlsComponent implements OnInit { this.setupDisksTab(); } - isActiveTab(tab: ReportTab): boolean { + protected isActiveTab(tab: ReportTab): boolean { return this.activeTab?.value === tab.value; } - typeTab(tab: ReportTab): ReportTab { + protected typeTab(tab: ReportTab): ReportTab { return tab; } From 2b5ea90d9718b306d4c895c34f2742a78a529306 Mon Sep 17 00:00:00 2001 From: Evgeny Stepanovych Date: Fri, 3 Jan 2025 10:10:54 -0500 Subject: [PATCH 04/18] NAS-133382: Fix more strict null checks (#11279) --- .../get-credentials-creation-source.utils.ts | 10 +- .../error-template.component.ts | 2 +- .../session-expiring-dialog.component.ts | 11 +- .../show-logs-dialog.component.html | 2 +- .../start-service-dialog.component.ts | 7 +- src/app/modules/empty/empty.component.html | 4 +- src/app/modules/empty/empty.component.ts | 2 +- .../ix-chips/ix-chips.component.html | 2 +- .../ix-code-editor.component.ts | 30 ++-- .../ix-combobox/ix-combobox.component.html | 2 +- .../ix-combobox/ix-combobox.component.ts | 4 +- .../ix-form-glossary.component.ts | 2 +- .../components/ix-input/ix-input.component.ts | 15 +- .../components/warning/warning.component.ts | 2 +- .../ix-forms/services/ix-formatter.service.ts | 2 +- .../advanced-search.component.ts | 2 +- .../toolbar-slider.component.ts | 2 +- .../global-search-trigger.component.ts | 6 +- .../ix-table/classes/base-data-provider.ts | 2 +- .../ix-empty-row/ix-empty-row.component.html | 4 +- .../ix-empty-row/ix-empty-row.component.ts | 7 +- .../ix-cell-schedule.component.html | 2 +- .../ix-cell-schedule.component.ts | 3 +- .../ix-cell-state-button.component.html | 2 +- .../ix-cell-text/ix-cell-text.component.html | 2 +- .../ix-cell-toggle.component.ts | 2 +- .../ix-table-columns-selector.component.ts | 2 +- .../directives/ix-body-cell.directive.ts | 2 +- .../modules/ix-tree/nested-tree-datasource.ts | 2 +- .../job-item/job-item.component.html | 4 +- .../jobs-panel/jobs-panel.component.html | 2 +- src/app/modules/jobs/store/job.selectors.ts | 2 +- .../console-footer.component.ts | 2 +- .../change-password-dialog.component.ts | 6 +- .../topbar/user-menu/user-menu.component.html | 2 +- .../scheduler-modal-config.interface.ts | 2 +- .../scheduler/scheduler.component.html | 2 +- .../scheduler/scheduler.component.ts | 2 +- .../truecommand-connect-modal.component.ts | 5 +- .../app-wizard/app-wizard.component.ts | 19 ++- .../category-view.component.html | 2 +- .../apps-settings.component.html | 2 +- .../apps-settings.component.spec.ts | 1 + .../apps-settings.component.ts | 18 +- .../custom-app-form.component.ts | 2 +- .../docker-image-delete-dialog.component.ts | 4 +- .../docker-images-list.component.html | 2 +- .../app-details-panel.component.html | 8 +- .../app-details-panel.component.ts | 2 +- .../app-info-card.component.html | 16 +- .../app-info-card/app-info-card.component.ts | 2 +- .../installed-apps-list.component.ts | 5 +- .../select-pool-dialog.component.ts | 4 +- src/app/pages/audit/audit.component.ts | 2 +- .../audit-list/audit-list.component.html | 2 +- .../audit-list/audit-list.component.ts | 16 +- .../audit-search/audit-search.component.html | 2 +- .../audit-search/audit-search.component.ts | 12 +- .../utils/get-log-important-data.utils.ts | 4 +- .../cloud-credentials-card.component.html | 2 +- .../cloud-credentials-form.component.ts | 2 +- .../google-cloud-provider-form.component.ts | 4 +- .../pcloud-provider-form.component.ts | 2 +- .../ssh-connection-card.component.html | 2 +- .../ssh-connection-form.component.ts | 8 +- .../ssh-keypair-card.component.html | 2 +- .../ssh-keypair-form.component.spec.ts | 1 - .../ssh-keypair-form.component.ts | 15 +- ...acme-dns-authenticator-list.component.html | 2 +- .../certificate-acme-add.component.ts | 6 +- .../certificate-authority-add.component.ts | 2 +- .../certificate-edit.component.ts | 2 +- .../certificate-list.component.html | 2 +- .../csr-add/csr-add.component.ts | 2 +- .../acmedns-form/acmedns-form.component.ts | 2 +- .../certificate-add.component.ts | 2 +- ...rtificate-identifier-and-type.component.ts | 4 +- .../certificate-constraints.component.ts | 12 +- .../certificate-options.component.ts | 5 +- .../groups/group-form/group-form.component.ts | 4 +- .../group-list/group-list.component.html | 2 +- .../privilege-list.component.html | 2 +- .../user-api-keys.component.html | 2 +- .../users/user-list/user-list.component.html | 2 +- .../cloud-backup-card.component.html | 2 +- .../cloud-backup-list.component.html | 2 +- .../cloudsync-form.component.ts | 2 +- .../cloudsync-task-card.component.html | 2 +- .../replication-form.component.ts | 2 +- .../replication-list.component.html | 2 +- .../replication-task-card.component.html | 2 +- .../rsync-task-card.component.html | 2 +- .../rsync-task-list.component.html | 2 +- .../scrub-list/scrub-list.component.html | 2 +- .../scrub-task-card.component.html | 2 +- .../smart-task-card.component.html | 2 +- .../smart-task-list.component.html | 2 +- .../snapshot-task-card.component.html | 2 +- .../snapshot-task-list.component.html | 2 +- .../vmware-snapshot-list.component.html | 2 +- .../quotas-section.component.ts | 10 +- .../dataset-form/utils/zfs-property.utils.ts | 4 +- .../zvol-form/zvol-form.component.ts | 2 +- .../snapshot-list.component.html | 2 +- .../idmap-list/idmap-list.component.html | 2 +- .../kerberos-keytabs-list.component.html | 2 +- .../kerberos-realms-list.component.html | 2 +- .../ipmi-card/ipmi-card.component.html | 2 +- .../static-routes-card.component.html | 2 +- .../reporting-exporters-list.component.html | 2 +- .../components/report/report.component.ts | 12 +- .../iscsi-card/iscsi-card.component.html | 2 +- .../nfs-card/nfs-card.component.html | 2 +- .../smb-card/smb-card.component.html | 2 +- .../authorized-access-list.component.html | 4 +- .../extent-list/extent-list.component.html | 4 +- .../global-target-configuration.component.ts | 4 +- .../initiator-form.component.ts | 6 +- .../initiator-list.component.html | 4 +- .../iscsi-wizard/iscsi-wizard.component.ts | 22 +-- .../portal-list/portal-list.component.html | 4 +- .../target-list/target-list.component.html | 4 +- .../target-form/target-form.component.ts | 4 +- .../nfs/nfs-list/nfs-list.component.html | 4 +- .../nfs-session-list.component.html | 2 +- .../sharing/smb/smb-acl/smb-acl.component.ts | 6 +- .../smb/smb-form/smb-form.component.ts | 19 ++- .../smb/smb-list/smb-list.component.html | 4 +- .../smb-lock-list.component.html | 2 +- .../smb-notification-list.component.html | 2 +- .../smb-open-files.component.html | 2 +- .../smb-session-list.component.html | 2 +- .../smb-share-list.component.html | 2 +- .../disk-list/disk-list.component.html | 4 +- .../smart-test-result-list.component.html | 2 +- .../cron/cron-card/cron-card.component.html | 2 +- .../init-shutdown-card.component.html | 2 +- .../sysctl-card/sysctl-card.component.html | 2 +- .../tunable-list/tunable-list.component.html | 2 +- .../alert-service-list.component.html | 2 +- .../bootenv-list/bootenv-list.component.html | 4 +- .../jbof-list/jbof-list.component.html | 2 +- .../device-list/device-list.component.html | 2 +- src/app/services/session-timeout.service.ts | 7 +- src/assets/i18n/es-ar.json | 156 +++++++++--------- 145 files changed, 399 insertions(+), 351 deletions(-) diff --git a/src/app/helpers/get-credentials-creation-source.utils.ts b/src/app/helpers/get-credentials-creation-source.utils.ts index c41715abc48..2fa82cb821e 100644 --- a/src/app/helpers/get-credentials-creation-source.utils.ts +++ b/src/app/helpers/get-credentials-creation-source.utils.ts @@ -1,11 +1,15 @@ import { CredentialType, Credentials } from 'app/interfaces/credential-type.interface'; -export function getCredentialsCreationSource(credentials: Credentials): string { +export function getCredentialsCreationSource(credentials: Credentials | null): string { + if (!credentials?.type) { + return ''; + } + if ([CredentialType.UnixSocket, CredentialType.LoginPassword, CredentialType.Token].includes(credentials.type)) { - return credentials.data.username; + return credentials.data?.username || ''; } if (credentials.type === CredentialType.ApiKey) { - return credentials.data.api_key; + return credentials.data?.api_key || ''; } return ''; } diff --git a/src/app/modules/dialog/components/multi-error-dialog/error-template/error-template.component.ts b/src/app/modules/dialog/components/multi-error-dialog/error-template/error-template.component.ts index 91ef4c355c2..dd38eac4a0c 100644 --- a/src/app/modules/dialog/components/multi-error-dialog/error-template/error-template.component.ts +++ b/src/app/modules/dialog/components/multi-error-dialog/error-template/error-template.component.ts @@ -34,7 +34,7 @@ export class ErrorTemplateComponent { private readonly errorBtPanel: Signal | undefined> = viewChild('errorBtPanel', { read: ElementRef }); private readonly errorBtText: Signal | undefined> = viewChild('errorBtText', { read: ElementRef }); - readonly title = input(); + readonly title = input.required(); readonly message = input(); readonly backtrace = input(); readonly logs = input(); diff --git a/src/app/modules/dialog/components/session-expiring-dialog/session-expiring-dialog.component.ts b/src/app/modules/dialog/components/session-expiring-dialog/session-expiring-dialog.component.ts index ba5e980d2df..fe8d3268462 100644 --- a/src/app/modules/dialog/components/session-expiring-dialog/session-expiring-dialog.component.ts +++ b/src/app/modules/dialog/components/session-expiring-dialog/session-expiring-dialog.component.ts @@ -8,9 +8,14 @@ import { } from '@angular/material/dialog'; import { TranslateModule } from '@ngx-translate/core'; import { NavigateAndInteractDirective } from 'app/directives/navigate-and-interact/navigate-and-interact.directive'; -import { ConfirmOptionsWithSecondaryCheckbox } from 'app/interfaces/dialog.interface'; import { TestDirective } from 'app/modules/test-id/test.directive'; +export interface SessionExpiringDialogOptions { + title: string; + message: string; + buttonText: string; +} + @Component({ selector: 'ix-session-expiring-dialog', templateUrl: './session-expiring-dialog.component.html', @@ -28,11 +33,11 @@ import { TestDirective } from 'app/modules/test-id/test.directive'; ], }) export class SessionExpiringDialogComponent { - options: ConfirmOptionsWithSecondaryCheckbox; + options: SessionExpiringDialogOptions; constructor( private dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) options: ConfirmOptionsWithSecondaryCheckbox, + @Inject(MAT_DIALOG_DATA) options: SessionExpiringDialogOptions, ) { this.options = { ...options }; } diff --git a/src/app/modules/dialog/components/show-logs-dialog/show-logs-dialog.component.html b/src/app/modules/dialog/components/show-logs-dialog/show-logs-dialog.component.html index 394f5f60c3f..5da7351b112 100644 --- a/src/app/modules/dialog/components/show-logs-dialog/show-logs-dialog.component.html +++ b/src/app/modules/dialog/components/show-logs-dialog/show-logs-dialog.component.html @@ -1,6 +1,6 @@

{{ 'Logs' | translate }}

-@if (job?.logs_excerpt) { +@if (job.logs_excerpt) {
{{ job.logs_excerpt }}
diff --git a/src/app/modules/dialog/components/start-service-dialog/start-service-dialog.component.ts b/src/app/modules/dialog/components/start-service-dialog/start-service-dialog.component.ts index fddf94217f8..5a728bad2c9 100644 --- a/src/app/modules/dialog/components/start-service-dialog/start-service-dialog.component.ts +++ b/src/app/modules/dialog/components/start-service-dialog/start-service-dialog.component.ts @@ -9,7 +9,7 @@ import { import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { Observable, forkJoin } from 'rxjs'; +import { Observable, forkJoin, filter } from 'rxjs'; import { ServiceName, serviceNames } from 'app/enums/service-name.enum'; import { Service } from 'app/interfaces/service.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; @@ -131,7 +131,10 @@ export class StartServiceDialogComponent implements OnInit { private getService(): void { this.store$.select(selectService(this.serviceName)) - .pipe(untilDestroyed(this)) + .pipe( + filter((service) => !!service), + untilDestroyed(this), + ) .subscribe((service) => { this.service = service; this.cdr.markForCheck(); diff --git a/src/app/modules/empty/empty.component.html b/src/app/modules/empty/empty.component.html index c480383d35a..15708f386b8 100644 --- a/src/app/modules/empty/empty.component.html +++ b/src/app/modules/empty/empty.component.html @@ -1,7 +1,7 @@
@if (!isLoading()) {
diff --git a/src/app/modules/empty/empty.component.ts b/src/app/modules/empty/empty.component.ts index b987c388ae3..5f7a45a2676 100644 --- a/src/app/modules/empty/empty.component.ts +++ b/src/app/modules/empty/empty.component.ts @@ -31,7 +31,7 @@ import { TestDirective } from 'app/modules/test-id/test.directive'; }) export class EmptyComponent { readonly conf = input.required(); - readonly requiredRoles = input(); + readonly requiredRoles = input([]); doAction(): void { const action = this.conf().button?.action; diff --git a/src/app/modules/forms/ix-forms/components/ix-chips/ix-chips.component.html b/src/app/modules/forms/ix-forms/components/ix-chips/ix-chips.component.html index a858d272ac9..010f1be2861 100644 --- a/src/app/modules/forms/ix-forms/components/ix-chips/ix-chips.component.html +++ b/src/app/modules/forms/ix-forms/components/ix-chips/ix-chips.component.html @@ -3,7 +3,7 @@ [label]="label()" [tooltip]="tooltip()" [required]="required()" - [ixTestOverride]="controlDirective.name" + [ixTestOverride]="controlDirective.name || ''" > } diff --git a/src/app/modules/forms/ix-forms/components/ix-code-editor/ix-code-editor.component.ts b/src/app/modules/forms/ix-forms/components/ix-code-editor/ix-code-editor.component.ts index e2a0777a20c..55904c7f9c9 100644 --- a/src/app/modules/forms/ix-forms/components/ix-code-editor/ix-code-editor.component.ts +++ b/src/app/modules/forms/ix-forms/components/ix-code-editor/ix-code-editor.component.ts @@ -12,7 +12,7 @@ import { import { ControlValueAccessor, NgControl, ReactiveFormsModule } from '@angular/forms'; import { MatHint } from '@angular/material/form-field'; import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; -import { Compartment } from '@codemirror/state'; +import { Compartment, Extension } from '@codemirror/state'; import { EditorView, EditorViewConfig, keymap, lineNumbers, placeholder, } from '@codemirror/view'; @@ -142,19 +142,25 @@ export class IxCodeEditorComponent implements OnChanges, OnInit, AfterViewInit, this.onChange(update.state.doc.toString()); }); + const extensions: Extension[] = [ + basicSetup, + updateListener, + lineNumbers(), + history(), + keymap.of([...defaultKeymap as unknown[], ...historyKeymap]), + material, + this.editableCompartment.of(EditorView.editable.of(true)), + placeholder(this.placeholder()), + ]; + + const language = this.language(); + if (language) { + extensions.push(languageFunctionsMap[language]()); + } + const config: EditorViewConfig = { + extensions, doc: this.controlDirective.control?.value as string || '', - extensions: [ - basicSetup, - updateListener, - languageFunctionsMap[this.language()](), - lineNumbers(), - history(), - keymap.of([...defaultKeymap as unknown[], ...historyKeymap]), - material, - this.editableCompartment.of(EditorView.editable.of(true)), - placeholder(this.placeholder()), - ], parent: this.inputArea().nativeElement, }; this.editorView = new EditorView(config); diff --git a/src/app/modules/forms/ix-forms/components/ix-combobox/ix-combobox.component.html b/src/app/modules/forms/ix-forms/components/ix-combobox/ix-combobox.component.html index aa572d67ed7..80187af42ca 100644 --- a/src/app/modules/forms/ix-forms/components/ix-combobox/ix-combobox.component.html +++ b/src/app/modules/forms/ix-forms/components/ix-combobox/ix-combobox.component.html @@ -4,7 +4,7 @@ [label]="label()" [tooltip]="tooltip()" [required]="required()" - [ixTestOverride]="controlDirective.name" + [ixTestOverride]="controlDirective.name || ''" > } diff --git a/src/app/modules/forms/ix-forms/components/ix-combobox/ix-combobox.component.ts b/src/app/modules/forms/ix-forms/components/ix-combobox/ix-combobox.component.ts index 7178c43a547..a1d01d34259 100644 --- a/src/app/modules/forms/ix-forms/components/ix-combobox/ix-combobox.component.ts +++ b/src/app/modules/forms/ix-forms/components/ix-combobox/ix-combobox.component.ts @@ -84,9 +84,9 @@ export class IxComboboxComponent implements ControlValueAccessor, OnInit { private filterChanged$ = new Subject(); - value: string | number = ''; + value: string | number | null = ''; isDisabled = false; - filterValue: string; + filterValue: string | null; selectedOption: Option | null = null; textContent = ''; diff --git a/src/app/modules/forms/ix-forms/components/ix-form-glossary/ix-form-glossary.component.ts b/src/app/modules/forms/ix-forms/components/ix-form-glossary/ix-form-glossary.component.ts index 67bbc03612a..43163eb3fa7 100644 --- a/src/app/modules/forms/ix-forms/components/ix-form-glossary/ix-form-glossary.component.ts +++ b/src/app/modules/forms/ix-forms/components/ix-form-glossary/ix-form-glossary.component.ts @@ -147,7 +147,7 @@ export class IxFormGlossaryComponent implements OnInit { } protected isSectionValid(section: IxFormSectionComponent): boolean { - return this.sectionsValidity.get(section); + return !!this.sectionsValidity.get(section); } ngOnInit(): void { diff --git a/src/app/modules/forms/ix-forms/components/ix-input/ix-input.component.ts b/src/app/modules/forms/ix-forms/components/ix-input/ix-input.component.ts index 935601737b1..7cb8525d857 100644 --- a/src/app/modules/forms/ix-forms/components/ix-input/ix-input.component.ts +++ b/src/app/modules/forms/ix-forms/components/ix-input/ix-input.component.ts @@ -67,7 +67,7 @@ export class IxInputComponent implements ControlValueAccessor, OnInit, OnChanges readonly tooltip = input(); readonly required = input(false); readonly readonly = input(); - readonly type = input(); + readonly type = input('text'); readonly autocomplete = input('off'); readonly autocompleteOptions = input(); readonly maxLength = input(524288); @@ -77,7 +77,7 @@ export class IxInputComponent implements ControlValueAccessor, OnInit, OnChanges readonly format = input<(value: string | number) => string>(); readonly parse = input<(value: string | number) => string | number>(); - readonly inputElementRef: Signal> = viewChild('ixInput', { read: ElementRef }); + readonly inputElementRef: Signal> = viewChild.required('ixInput', { read: ElementRef }); private _value: string | number = this.controlDirective.value as string; formatted: string | number = ''; @@ -212,12 +212,15 @@ export class IxInputComponent implements ControlValueAccessor, OnInit, OnChanges this.onTouch(); if (this.formatted) { - if (this.parse()) { - this.value = this.parse()(this.formatted); + const parse = this.parse(); + if (parse) { + this.value = parse(this.formatted); this.formatted = this.value; } - if (this.format()) { - this.formatted = this.format()(this.value); + + const format = this.format(); + if (format) { + this.formatted = format(this.value); } } diff --git a/src/app/modules/forms/ix-forms/components/warning/warning.component.ts b/src/app/modules/forms/ix-forms/components/warning/warning.component.ts index 4154094059d..416b0a27587 100644 --- a/src/app/modules/forms/ix-forms/components/warning/warning.component.ts +++ b/src/app/modules/forms/ix-forms/components/warning/warning.component.ts @@ -15,6 +15,6 @@ import { TranslateModule } from '@ngx-translate/core'; imports: [NgClass, TranslateModule], }) export class WarningComponent { - readonly message = input(); + readonly message = input.required(); readonly color = input<'green' | 'orange'>('orange'); } diff --git a/src/app/modules/forms/ix-forms/services/ix-formatter.service.ts b/src/app/modules/forms/ix-forms/services/ix-formatter.service.ts index 22d823cfe1d..789db29d939 100644 --- a/src/app/modules/forms/ix-forms/services/ix-formatter.service.ts +++ b/src/app/modules/forms/ix-forms/services/ix-formatter.service.ts @@ -185,7 +185,7 @@ export class IxFormatterService { } return unitStr.charAt(0).toUpperCase() + 'iB'; } - return undefined; + return ''; }; /** diff --git a/src/app/modules/forms/search-input/components/advanced-search/advanced-search.component.ts b/src/app/modules/forms/search-input/components/advanced-search/advanced-search.component.ts index fb6a60435e3..67ea879970b 100644 --- a/src/app/modules/forms/search-input/components/advanced-search/advanced-search.component.ts +++ b/src/app/modules/forms/search-input/components/advanced-search/advanced-search.component.ts @@ -55,7 +55,7 @@ export class AdvancedSearchComponent implements OnInit { readonly switchToBasic = output(); readonly runSearch = output(); - private readonly inputArea: Signal> = viewChild('inputArea', { read: ElementRef }); + private readonly inputArea: Signal> = viewChild.required('inputArea', { read: ElementRef }); protected hasQueryErrors = false; protected queryInputValue: string; diff --git a/src/app/modules/forms/toolbar-slider/toolbar-slider.component.ts b/src/app/modules/forms/toolbar-slider/toolbar-slider.component.ts index 0583ac708c0..3bd6f9049b4 100644 --- a/src/app/modules/forms/toolbar-slider/toolbar-slider.component.ts +++ b/src/app/modules/forms/toolbar-slider/toolbar-slider.component.ts @@ -23,7 +23,7 @@ export class ToolbarSliderComponent { readonly label = input(1); readonly name = input(1); - readonly value = model(); + readonly value = model.required(); onChange(updatedValue: string): void { this.value.set(Number(updatedValue)); diff --git a/src/app/modules/global-search/components/global-search-trigger/global-search-trigger.component.ts b/src/app/modules/global-search/components/global-search-trigger/global-search-trigger.component.ts index b71c912bd03..bc889124461 100644 --- a/src/app/modules/global-search/components/global-search-trigger/global-search-trigger.component.ts +++ b/src/app/modules/global-search/components/global-search-trigger/global-search-trigger.component.ts @@ -36,7 +36,7 @@ import { FocusService } from 'app/services/focus.service'; ], }) export class GlobalSearchTriggerComponent implements AfterViewInit { - protected overlayRef: OverlayRef; + protected overlayRef: OverlayRef | undefined; constructor( private cdr: ChangeDetectorRef, @@ -64,7 +64,7 @@ export class GlobalSearchTriggerComponent implements AfterViewInit { } protected showOverlay(): void { - if (this.overlayRef.hasAttached()) { + if (!this.overlayRef || this.overlayRef.hasAttached()) { return; } @@ -86,7 +86,7 @@ export class GlobalSearchTriggerComponent implements AfterViewInit { } private detachOverlay(): void { - if (!this.overlayRef.hasAttached()) { + if (!this.overlayRef?.hasAttached()) { return; } this.overlayRef.detach(); diff --git a/src/app/modules/ix-table/classes/base-data-provider.ts b/src/app/modules/ix-table/classes/base-data-provider.ts index e4c7a89a829..179a4744955 100644 --- a/src/app/modules/ix-table/classes/base-data-provider.ts +++ b/src/app/modules/ix-table/classes/base-data-provider.ts @@ -28,7 +28,7 @@ export class BaseDataProvider implements DataProvider { } currentPage$ = new BehaviorSubject([]); - expandedRow$ = new BehaviorSubject(null); + expandedRow$ = new BehaviorSubject(null); expandedRow: T; totalRows = 0; diff --git a/src/app/modules/ix-table/components/ix-empty-row/ix-empty-row.component.html b/src/app/modules/ix-table/components/ix-empty-row/ix-empty-row.component.html index 7cd40895de6..1b861a81ea3 100644 --- a/src/app/modules/ix-table/components/ix-empty-row/ix-empty-row.component.html +++ b/src/app/modules/ix-table/components/ix-empty-row/ix-empty-row.component.html @@ -2,8 +2,8 @@
@if (isLoading()) { diff --git a/src/app/modules/ix-table/components/ix-empty-row/ix-empty-row.component.ts b/src/app/modules/ix-table/components/ix-empty-row/ix-empty-row.component.ts index 831fd5f0795..8d2ea154a78 100644 --- a/src/app/modules/ix-table/components/ix-empty-row/ix-empty-row.component.ts +++ b/src/app/modules/ix-table/components/ix-empty-row/ix-empty-row.component.ts @@ -42,7 +42,7 @@ export class IxTableEmptyRowComponent implements AfterViewInit { type: EmptyType.NoPageData, }); - readonly templatePortalContent = viewChild>('templatePortalContent'); + readonly templatePortalContent = viewChild.required>('templatePortalContent'); templatePortal: TemplatePortal; constructor( @@ -57,8 +57,9 @@ export class IxTableEmptyRowComponent implements AfterViewInit { } doAction(): void { - if (this.conf().button.action) { - this.conf().button.action(); + const action = this.conf().button?.action; + if (action) { + action(); } } diff --git a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-schedule/ix-cell-schedule.component.html b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-schedule/ix-cell-schedule.component.html index 4bae36c777c..d14afdfad6f 100644 --- a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-schedule/ix-cell-schedule.component.html +++ b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-schedule/ix-cell-schedule.component.html @@ -1,4 +1,4 @@ {{ value | scheduleDescription }} +>{{ value | cast | scheduleDescription }} diff --git a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-schedule/ix-cell-schedule.component.ts b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-schedule/ix-cell-schedule.component.ts index 7334faff5a1..3fa11e38876 100644 --- a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-schedule/ix-cell-schedule.component.ts +++ b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-schedule/ix-cell-schedule.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { MatTooltip } from '@angular/material/tooltip'; import { ScheduleDescriptionPipe } from 'app/modules/dates/pipes/schedule-description/schedule-description.pipe'; import { ColumnComponent, Column } from 'app/modules/ix-table/interfaces/column-component.class'; +import { CastPipe } from 'app/modules/pipes/cast/cast.pipe'; import { TestDirective } from 'app/modules/test-id/test.directive'; @Component({ @@ -9,7 +10,7 @@ import { TestDirective } from 'app/modules/test-id/test.directive'; templateUrl: './ix-cell-schedule.component.html', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [TestDirective, ScheduleDescriptionPipe, MatTooltip], + imports: [TestDirective, ScheduleDescriptionPipe, MatTooltip, CastPipe], }) export class IxCellScheduleComponent extends ColumnComponent {} diff --git a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-state-button/ix-cell-state-button.component.html b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-state-button/ix-cell-state-button.component.html index 56a27b3f5a9..98f0edf0ab4 100644 --- a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-state-button/ix-cell-state-button.component.html +++ b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-state-button/ix-cell-state-button.component.html @@ -10,7 +10,7 @@ (click)="$event.stopPropagation(); onButtonClick()" > {{ state() }} - @if (warnings?.length > 0) { + @if (warnings.length > 0) {
diff --git a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-text/ix-cell-text.component.html b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-text/ix-cell-text.component.html index 8635ae8b5d4..f807f209f9f 100644 --- a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-text/ix-cell-text.component.html +++ b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-text/ix-cell-text.component.html @@ -1,4 +1,4 @@ {{ value }} diff --git a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-toggle/ix-cell-toggle.component.ts b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-toggle/ix-cell-toggle.component.ts index 03e4e5decec..732d6a90ce7 100644 --- a/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-toggle/ix-cell-toggle.component.ts +++ b/src/app/modules/ix-table/components/ix-table-body/cells/ix-cell-toggle/ix-cell-toggle.component.ts @@ -24,7 +24,7 @@ import { TestDirective } from 'app/modules/test-id/test.directive'; export class IxCellToggleComponent extends ColumnComponent { requiredRoles: Role[]; onRowToggle: (row: T, checked: boolean, toggle: MatSlideToggle) => void; - dynamicRequiredRoles: (row: T) => Observable; + dynamicRequiredRoles?: (row: T) => Observable; get checked(): boolean { return this.value as boolean; diff --git a/src/app/modules/ix-table/components/ix-table-columns-selector/ix-table-columns-selector.component.ts b/src/app/modules/ix-table/components/ix-table-columns-selector/ix-table-columns-selector.component.ts index d2975273915..866f18536c1 100644 --- a/src/app/modules/ix-table/components/ix-table-columns-selector/ix-table-columns-selector.component.ts +++ b/src/app/modules/ix-table/components/ix-table-columns-selector/ix-table-columns-selector.component.ts @@ -30,7 +30,7 @@ import { TestDirective } from 'app/modules/test-id/test.directive'; ], }) export class IxTableColumnsSelectorComponent implements OnChanges { - readonly columns = model>[]>(); + readonly columns = model.required>[]>(); readonly columnsChange = output>[]>(); diff --git a/src/app/modules/ix-table/directives/ix-body-cell.directive.ts b/src/app/modules/ix-table/directives/ix-body-cell.directive.ts index 452ee7496a0..8d0a434d6c3 100644 --- a/src/app/modules/ix-table/directives/ix-body-cell.directive.ts +++ b/src/app/modules/ix-table/directives/ix-body-cell.directive.ts @@ -14,7 +14,7 @@ import { Column, ColumnComponent, ColumnKeys } from 'app/modules/ix-table/interf standalone: true, }) export class IxTableBodyCellDirective implements AfterViewInit, OnChanges { - readonly row = input(); + readonly row = input.required(); readonly column = input.required>>(); private componentRef: ComponentRef>; diff --git a/src/app/modules/ix-tree/nested-tree-datasource.ts b/src/app/modules/ix-tree/nested-tree-datasource.ts index 98634362ed6..838990b9f94 100644 --- a/src/app/modules/ix-tree/nested-tree-datasource.ts +++ b/src/app/modules/ix-tree/nested-tree-datasource.ts @@ -11,7 +11,7 @@ import { */ export class NestedTreeDataSource extends DataSource { filterPredicate: (data: T[], query: string) => T[]; - sortComparer: (a: T, b: T) => number; + sortComparer?: (a: T, b: T) => number; private filterValue: string; private readonly filterChanged$ = new BehaviorSubject(''); private readonly _data = new BehaviorSubject([]); diff --git a/src/app/modules/jobs/components/job-item/job-item.component.html b/src/app/modules/jobs/components/job-item/job-item.component.html index 4edf209eb73..68705bc4526 100644 --- a/src/app/modules/jobs/components/job-item/job-item.component.html +++ b/src/app/modules/jobs/components/job-item/job-item.component.html @@ -1,8 +1,8 @@
diff --git a/src/app/modules/jobs/components/jobs-panel/jobs-panel.component.html b/src/app/modules/jobs/components/jobs-panel/jobs-panel.component.html index 37e53eb4a53..162f098f42b 100644 --- a/src/app/modules/jobs/components/jobs-panel/jobs-panel.component.html +++ b/src/app/modules/jobs/components/jobs-panel/jobs-panel.component.html @@ -37,7 +37,7 @@

{{ 'Running Jobs' | translate }}

} @else { @let jobs = availableJobs$ | async; - @if (jobs.length) { + @if (jobs?.length) {
@for (job of jobs; track job.id) { => createSelector( +export const selectJob = (id: number): MemoizedSelector => createSelector( selectJobs, (jobs) => jobs.find((job) => job.id === id), ); diff --git a/src/app/modules/layout/console-footer/console-footer.component.ts b/src/app/modules/layout/console-footer/console-footer.component.ts index 31e052a9c8c..6e70724b296 100644 --- a/src/app/modules/layout/console-footer/console-footer.component.ts +++ b/src/app/modules/layout/console-footer/console-footer.component.ts @@ -17,7 +17,7 @@ import { ConsolePanelDialogComponent } from 'app/modules/layout/console-footer/c imports: [AsyncPipe], }) export class ConsoleFooterComponent implements OnInit { - private readonly messageContainer: Signal> = viewChild('messageContainer', { read: ElementRef }); + private readonly messageContainer: Signal> = viewChild.required('messageContainer', { read: ElementRef }); lastThreeLogLines$ = this.messagesStore.lastThreeLogLines$; diff --git a/src/app/modules/layout/topbar/change-password-dialog/change-password-dialog.component.ts b/src/app/modules/layout/topbar/change-password-dialog/change-password-dialog.component.ts index 7fad7ec6e40..7b6498d6436 100644 --- a/src/app/modules/layout/topbar/change-password-dialog/change-password-dialog.component.ts +++ b/src/app/modules/layout/topbar/change-password-dialog/change-password-dialog.component.ts @@ -40,7 +40,7 @@ import { ApiService } from 'app/modules/websocket/api.service'; ], }) export class ChangePasswordDialogComponent { - form = this.fb.group({ + form = this.fb.nonNullable.group({ old_password: [''], new_password: ['', [Validators.required]], passwordConfirmation: ['', [Validators.required]], @@ -81,8 +81,8 @@ export class ChangePasswordDialogComponent { onSubmit(): void { this.api.call('user.set_password', [{ - old_password: this.form.value.old_password, - new_password: this.form.value.new_password, + old_password: this.form.getRawValue().old_password, + new_password: this.form.getRawValue().new_password, username: this.loggedInUser.pw_name, }]).pipe( this.loader.withLoader(), diff --git a/src/app/modules/layout/topbar/user-menu/user-menu.component.html b/src/app/modules/layout/topbar/user-menu/user-menu.component.html index 3da97aba57a..100c247e948 100644 --- a/src/app/modules/layout/topbar/user-menu/user-menu.component.html +++ b/src/app/modules/layout/topbar/user-menu/user-menu.component.html @@ -22,7 +22,7 @@ - @if (user?.account_attributes.includes(AccountAttribute.Local)) { + @if (user?.account_attributes?.includes(AccountAttribute.Local)) { @@ -43,7 +43,7 @@

@@ -56,7 +56,7 @@

{{ 'App Version' | translate }}:
@if (!isCustomApp()) { - {{ app()?.metadata?.app_version | appVersion | orNotAvailable }} + {{ app().metadata?.app_version | appVersion | orNotAvailable }} } @else if (app().human_version) { {{ app().human_version?.split(':')?.[1]?.split('_')?.[0] }} @@ -67,7 +67,7 @@

{{ 'Version' | translate }}:
@if (!isCustomApp()) { - {{ app()?.version | appVersion | orNotAvailable }} + {{ app().version | appVersion | orNotAvailable }} }

@@ -75,7 +75,7 @@

{{ 'Source' | translate }}:
- @for (source of app()?.metadata?.sources; track source; let last = $last) { + @for (source of app().metadata?.sources; track source; let last = $last) {
{{ 'Train' | translate }}:
- {{ app()?.metadata?.train | orNotAvailable }} + {{ app().metadata?.train | orNotAvailable }}
@if ((app().portals | keyvalue).length > 0 ) { diff --git a/src/app/pages/apps/components/installed-apps/app-info-card/app-info-card.component.ts b/src/app/pages/apps/components/installed-apps/app-info-card/app-info-card.component.ts index 9ab6cc70236..3a162366709 100644 --- a/src/app/pages/apps/components/installed-apps/app-info-card/app-info-card.component.ts +++ b/src/app/pages/apps/components/installed-apps/app-info-card/app-info-card.component.ts @@ -78,7 +78,7 @@ import { RedirectService } from 'app/services/redirect.service'; ], }) export class AppInfoCardComponent { - readonly app = input(); + readonly app = input.required(); readonly startApp = output(); readonly stopApp = output(); protected readonly isCustomApp = computed(() => this.app()?.metadata?.name === customApp); diff --git a/src/app/pages/apps/components/installed-apps/installed-apps-list/installed-apps-list.component.ts b/src/app/pages/apps/components/installed-apps/installed-apps-list/installed-apps-list.component.ts index 8ee1cdc18e7..8d4077c1cc5 100644 --- a/src/app/pages/apps/components/installed-apps/installed-apps-list/installed-apps-list.component.ts +++ b/src/app/pages/apps/components/installed-apps/installed-apps-list/installed-apps-list.component.ts @@ -339,10 +339,11 @@ export class InstalledAppsListComponent implements OnInit { } openStatusDialog(name: string): void { - if (!this.appJobs.has(name)) { + const jobId = this.appJobs.get(name)?.id; + if (!jobId) { return; } - const job$ = this.store$.select(selectJob(this.appJobs.get(name).id)); + const job$ = this.store$.select(selectJob(jobId)); this.dialogService.jobDialog(job$, { title: name, canMinimize: true }) .afterClosed() .pipe(this.errorHandler.catchError(), untilDestroyed(this)) diff --git a/src/app/pages/apps/components/select-pool-dialog/select-pool-dialog.component.ts b/src/app/pages/apps/components/select-pool-dialog/select-pool-dialog.component.ts index 5d25681cbaf..92f0565aabf 100644 --- a/src/app/pages/apps/components/select-pool-dialog/select-pool-dialog.component.ts +++ b/src/app/pages/apps/components/select-pool-dialog/select-pool-dialog.component.ts @@ -46,7 +46,7 @@ import { ErrorHandlerService } from 'app/services/error-handler.service'; export class SelectPoolDialogComponent implements OnInit { readonly requiredRoles = [Role.FullAdmin]; - form = this.formBuilder.group({ + form = this.formBuilder.nonNullable.group({ pool: [''], migrateApplications: [false], }); @@ -72,7 +72,7 @@ export class SelectPoolDialogComponent implements OnInit { } onSubmit(): void { - this.dockerStore.setDockerPool(this.form.value.pool).pipe( + this.dockerStore.setDockerPool(this.form.getRawValue().pool).pipe( untilDestroyed(this), ).subscribe(() => { this.snackbar.success( diff --git a/src/app/pages/audit/audit.component.ts b/src/app/pages/audit/audit.component.ts index d6370387526..47e9294c78d 100644 --- a/src/app/pages/audit/audit.component.ts +++ b/src/app/pages/audit/audit.component.ts @@ -57,7 +57,7 @@ import { selectIsHaLicensed } from 'app/store/ha-info/ha-info.selectors'; export class AuditComponent implements OnInit, OnDestroy { protected dataProvider: AuditApiDataProvider; - protected readonly masterDetailView = viewChild(MasterDetailViewComponent); + protected readonly masterDetailView = viewChild.required(MasterDetailViewComponent); protected readonly controllerTypeControl = new FormControl(ControllerType.Active); protected readonly controllerTypeOptions$ = of(mapToOptions(controllerTypeLabels, this.translate)); protected readonly controllerType = toSignal(this.controllerTypeControl.value$); diff --git a/src/app/pages/audit/components/audit-list/audit-list.component.html b/src/app/pages/audit/components/audit-list/audit-list.component.html index 81daab28cf4..163c553bc86 100644 --- a/src/app/pages/audit/components/audit-list/audit-list.component.html +++ b/src/app/pages/audit/components/audit-list/audit-list.component.html @@ -20,7 +20,7 @@ detailsRowIdentifier="audit_id" [columns]="columns" [dataProvider]="dataProvider()" - [isLoading]="dataProvider().isLoading$ | async" + [isLoading]="!!(dataProvider().isLoading$ | async)" (expanded)="expanded($event)" > (auditServiceLabels.has(row.service) - ? this.translate.instant(auditServiceLabels.get(row.service)) - : row.service || '-'), + getValue: (row) => { + const service = auditServiceLabels.get(row.service); + return service ? this.translate.instant(service) : row.service || '-'; + }, }), textColumn({ title: this.translate.instant('User'), @@ -75,9 +76,10 @@ export class AuditListComponent { textColumn({ title: this.translate.instant('Event'), propertyName: 'event', - getValue: (row) => (auditEventLabels.has(row.event) - ? this.translate.instant(auditEventLabels.get(row.event)) - : row.event || '-'), + getValue: (row) => { + const event = auditEventLabels.get(row.event); + return event ? this.translate.instant(event) : row.event || '-'; + }, }), textColumn({ title: this.translate.instant('Event Data'), @@ -101,7 +103,7 @@ export class AuditListComponent { getUserAvatarForLog(row: AuditEntry): SafeHtml { // eslint-disable-next-line sonarjs/no-angular-bypass-sanitization - return this.sanitizer.bypassSecurityTrustHtml(toSvg(row.username, this.isMobileView ? 15 : 35)); + return this.sanitizer.bypassSecurityTrustHtml(toSvg(row.username, this.isMobileView() ? 15 : 35)); } expanded(row: AuditEntry): void { diff --git a/src/app/pages/audit/components/audit-search/audit-search.component.html b/src/app/pages/audit/components/audit-search/audit-search.component.html index e1c23580e1b..38a883062d5 100644 --- a/src/app/pages/audit/components/audit-search/audit-search.component.html +++ b/src/app/pages/audit/components/audit-search/audit-search.component.html @@ -1,7 +1,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > control.parent && this.isManualSetup, + (control) => Boolean(control?.parent) && this.isManualSetup, Validators.required, )], port: [22, this.validatorsService.validateOnCondition( - (control) => control.parent && this.isManualSetup, + (control) => Boolean(control?.parent) && this.isManualSetup, Validators.required, )], remote_host_key: [''], url: ['', this.validatorsService.validateOnCondition( - (control) => control.parent && !this.isManualSetup, + (control) => Boolean(control?.parent) && !this.isManualSetup, Validators.required, )], username: ['root', Validators.required], admin_username: ['root'], password: ['', this.validatorsService.validateOnCondition( - (control) => control.parent && !this.isManualSetup, + (control) => Boolean(control?.parent) && !this.isManualSetup, Validators.required, )], sudo: [false], diff --git a/src/app/pages/credentials/backup-credentials/ssh-keypair-card/ssh-keypair-card.component.html b/src/app/pages/credentials/backup-credentials/ssh-keypair-card/ssh-keypair-card.component.html index 3e317ecf71a..b8b27f1a0e0 100644 --- a/src/app/pages/credentials/backup-credentials/ssh-keypair-card/ssh-keypair-card.component.html +++ b/src/app/pages/credentials/backup-credentials/ssh-keypair-card/ssh-keypair-card.component.html @@ -29,7 +29,7 @@

{{ 'SSH Keypairs' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > { }); loader = TestbedHarnessEnvironment.loader(spectator.fixture); api = spectator.inject(ApiService); - spectator.component.setKeypairForEditing(); }); it('shows current values when form is being edited', async () => { diff --git a/src/app/pages/credentials/backup-credentials/ssh-keypair-form/ssh-keypair-form.component.ts b/src/app/pages/credentials/backup-credentials/ssh-keypair-form/ssh-keypair-form.component.ts index 4ae9e4f46b7..d37545b5fca 100644 --- a/src/app/pages/credentials/backup-credentials/ssh-keypair-form/ssh-keypair-form.component.ts +++ b/src/app/pages/credentials/backup-credentials/ssh-keypair-form/ssh-keypair-form.component.ts @@ -69,7 +69,7 @@ export class SshKeypairFormComponent implements OnInit { isFormLoading = false; - protected editingKeypair: KeychainSshKeyPair; + protected editingKeypair: KeychainSshKeyPair | undefined; form = this.fb.group({ name: ['', Validators.required], @@ -107,16 +107,17 @@ export class SshKeypairFormComponent implements OnInit { } ngOnInit(): void { - if (this.editingKeypair) { - this.setKeypairForEditing(); + const keypair = this.editingKeypair; + if (keypair) { + this.setKeypairForEditing(keypair); } } - setKeypairForEditing(): void { + private setKeypairForEditing(keypair: KeychainSshKeyPair): void { this.form.patchValue({ - name: this.editingKeypair.name, - private_key: this.editingKeypair.attributes.private_key, - public_key: this.editingKeypair.attributes.public_key, + name: keypair.name, + private_key: keypair.attributes.private_key, + public_key: keypair.attributes.public_key, }); } diff --git a/src/app/pages/credentials/certificates-dash/acme-dns-authenticator-list/acme-dns-authenticator-list.component.html b/src/app/pages/credentials/certificates-dash/acme-dns-authenticator-list/acme-dns-authenticator-list.component.html index 9ccf8089b3a..c9f72fe845b 100644 --- a/src/app/pages/credentials/certificates-dash/acme-dns-authenticator-list/acme-dns-authenticator-list.component.html +++ b/src/app/pages/credentials/certificates-dash/acme-dns-authenticator-list/acme-dns-authenticator-list.component.html @@ -29,7 +29,7 @@

{{ 'ACME DNS-Authenticators' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > , + public slideInRef: SlideInRef, ) { this.slideInRef.requireConfirmationWhen(() => { return of(this.form.dirty); @@ -109,7 +109,7 @@ export class CertificateAcmeAddComponent implements OnInit { } onSubmit(): void { - const formValues = this.form.value; + const formValues = this.form.getRawValue(); const dnsMapping = this.domains.reduce((mapping, domain, i) => { return { diff --git a/src/app/pages/credentials/certificates-dash/certificate-authority-add/certificate-authority-add.component.ts b/src/app/pages/credentials/certificates-dash/certificate-authority-add/certificate-authority-add.component.ts index 57da4e21949..4f0ef5acb5d 100644 --- a/src/app/pages/credentials/certificates-dash/certificate-authority-add/certificate-authority-add.component.ts +++ b/src/app/pages/credentials/certificates-dash/certificate-authority-add/certificate-authority-add.component.ts @@ -106,7 +106,7 @@ export class CertificateAuthorityAddComponent implements AfterViewInit { public slideInRef: SlideInRef, ) { this.slideInRef.requireConfirmationWhen(() => { - return of(this.identifierAndType()?.form?.dirty); + return of(Boolean(this.identifierAndType()?.form?.dirty)); }); } diff --git a/src/app/pages/credentials/certificates-dash/certificate-edit/certificate-edit.component.ts b/src/app/pages/credentials/certificates-dash/certificate-edit/certificate-edit.component.ts index a4d93c232f6..f3085442d12 100644 --- a/src/app/pages/credentials/certificates-dash/certificate-edit/certificate-edit.component.ts +++ b/src/app/pages/credentials/certificates-dash/certificate-edit/certificate-edit.component.ts @@ -81,7 +81,7 @@ export class CertificateEditComponent implements OnInit { private cdr: ChangeDetectorRef, private errorHandler: FormErrorHandlerService, private matDialog: MatDialog, - public slideInRef: SlideInRef, + public slideInRef: SlideInRef, ) { this.slideInRef.requireConfirmationWhen(() => { return of(this.form.dirty); diff --git a/src/app/pages/credentials/certificates-dash/certificate-list/certificate-list.component.html b/src/app/pages/credentials/certificates-dash/certificate-list/certificate-list.component.html index 396f14d9af5..9599340e52e 100644 --- a/src/app/pages/credentials/certificates-dash/certificate-list/certificate-list.component.html +++ b/src/app/pages/credentials/certificates-dash/certificate-list/certificate-list.component.html @@ -29,7 +29,7 @@

{{ 'Certificates' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > , ) { this.slideInRef.requireConfirmationWhen(() => { - return of(this.identifierAndType()?.form?.dirty); + return of(Boolean(this.identifierAndType()?.form?.dirty)); }); } diff --git a/src/app/pages/credentials/certificates-dash/forms/acmedns-form/acmedns-form.component.ts b/src/app/pages/credentials/certificates-dash/forms/acmedns-form/acmedns-form.component.ts index 4e30298ea5e..b35e10f0707 100644 --- a/src/app/pages/credentials/certificates-dash/forms/acmedns-form/acmedns-form.component.ts +++ b/src/app/pages/credentials/certificates-dash/forms/acmedns-form/acmedns-form.component.ts @@ -95,7 +95,7 @@ export class AcmednsFormComponent implements OnInit { } authenticatorOptions$: Observable; - private editingAcmedns: DnsAuthenticator; + private editingAcmedns: DnsAuthenticator | undefined; constructor( private translate: TranslateService, diff --git a/src/app/pages/credentials/certificates-dash/forms/certificate-add/certificate-add.component.ts b/src/app/pages/credentials/certificates-dash/forms/certificate-add/certificate-add.component.ts index 5745b1a8fd5..05e66f6410e 100644 --- a/src/app/pages/credentials/certificates-dash/forms/certificate-add/certificate-add.component.ts +++ b/src/app/pages/credentials/certificates-dash/forms/certificate-add/certificate-add.component.ts @@ -99,7 +99,7 @@ export class CertificateAddComponent { public slideInRef: SlideInRef, ) { this.slideInRef.requireConfirmationWhen(() => { - return of(this.identifierAndType()?.form?.dirty); + return of(Boolean(this.identifierAndType()?.form?.dirty)); }); } diff --git a/src/app/pages/credentials/certificates-dash/forms/certificate-add/steps/certificate-identifier-and-type/certificate-identifier-and-type.component.ts b/src/app/pages/credentials/certificates-dash/forms/certificate-add/steps/certificate-identifier-and-type/certificate-identifier-and-type.component.ts index 4529680a528..0a0ab2520e1 100644 --- a/src/app/pages/credentials/certificates-dash/forms/certificate-add/steps/certificate-identifier-and-type/certificate-identifier-and-type.component.ts +++ b/src/app/pages/credentials/certificates-dash/forms/certificate-add/steps/certificate-identifier-and-type/certificate-identifier-and-type.component.ts @@ -99,11 +99,11 @@ export class CertificateIdentifierAndTypeComponent implements OnInit, SummaryPro } getSummary(): SummarySection { - const values = this.form.value; + const values = this.form.getRawValue(); const summary = [ { label: this.translate.instant('Name'), value: values.name }, - { label: this.translate.instant('Type'), value: this.createTypes.get(values.create_type) }, + { label: this.translate.instant('Type'), value: this.createTypes.get(values.create_type) || values.create_type }, ]; if (values.profile) { diff --git a/src/app/pages/credentials/certificates-dash/forms/common-steps/certificate-constraints/certificate-constraints.component.ts b/src/app/pages/credentials/certificates-dash/forms/common-steps/certificate-constraints/certificate-constraints.component.ts index 48d07af45d3..58fe6696d28 100644 --- a/src/app/pages/credentials/certificates-dash/forms/common-steps/certificate-constraints/certificate-constraints.component.ts +++ b/src/app/pages/credentials/certificates-dash/forms/common-steps/certificate-constraints/certificate-constraints.component.ts @@ -60,22 +60,22 @@ import { ErrorHandlerService } from 'app/services/error-handler.service'; export class CertificateConstraintsComponent implements OnInit, SummaryProvider { hasAuthorityKeyIdentifier = input(false); - form = this.formBuilder.group({ - BasicConstraints: this.formBuilder.group({ + form = this.formBuilder.nonNullable.group({ + BasicConstraints: this.formBuilder.nonNullable.group({ enabled: [false], - path_length: [null as number], + path_length: [null as number | null], BasicConstraints: [[] as BasicConstraint[]], }), - AuthorityKeyIdentifier: this.formBuilder.group({ + AuthorityKeyIdentifier: this.formBuilder.nonNullable.group({ enabled: [false], AuthorityKeyIdentifier: [[] as AuthorityKeyIdentifier[]], }), - ExtendedKeyUsage: this.formBuilder.group({ + ExtendedKeyUsage: this.formBuilder.nonNullable.group({ enabled: [false], usages: [[] as ExtendedKeyUsageFlag[]], extension_critical: [false], }), - KeyUsage: this.formBuilder.group({ + KeyUsage: this.formBuilder.nonNullable.group({ enabled: [false], KeyUsage: [[] as string[]], }), diff --git a/src/app/pages/credentials/certificates-dash/forms/common-steps/certificate-options/certificate-options.component.ts b/src/app/pages/credentials/certificates-dash/forms/common-steps/certificate-options/certificate-options.component.ts index de6d25c593c..125e7ba803c 100644 --- a/src/app/pages/credentials/certificates-dash/forms/common-steps/certificate-options/certificate-options.component.ts +++ b/src/app/pages/credentials/certificates-dash/forms/common-steps/certificate-options/certificate-options.component.ts @@ -102,7 +102,10 @@ export class CertificateOptionsComponent implements OnInit, OnChanges, SummaryPr } summary.push( - { label: this.translate.instant('Key Type'), value: certificateKeyTypeLabels.get(values.key_type) }, + { + label: this.translate.instant('Key Type'), + value: certificateKeyTypeLabels.get(values.key_type) || values.key_type, + }, this.isRsa ? { label: this.translate.instant('Key Length'), value: String(values.key_length) } : { label: this.translate.instant('EC Curve'), value: String(values.ec_curve) }, diff --git a/src/app/pages/credentials/groups/group-form/group-form.component.ts b/src/app/pages/credentials/groups/group-form/group-form.component.ts index 64b5570c7c9..8cd22feae8d 100644 --- a/src/app/pages/credentials/groups/group-form/group-form.component.ts +++ b/src/app/pages/credentials/groups/group-form/group-form.component.ts @@ -72,10 +72,10 @@ export class GroupFormComponent implements OnInit { privilegesList: Privilege[]; initialGroupRelatedPrivilegesList: Privilege[] = []; - protected editingGroup: Group; + protected editingGroup: Group | undefined; form = this.fb.group({ - gid: [null as number, [Validators.required, Validators.pattern(/^\d+$/)]], + gid: [null as number | null, [Validators.required, Validators.pattern(/^\d+$/)]], name: ['', [Validators.required, Validators.pattern(UserService.namePattern)]], sudo_commands: [[] as string[]], sudo_commands_all: [false], diff --git a/src/app/pages/credentials/groups/group-list/group-list.component.html b/src/app/pages/credentials/groups/group-list/group-list.component.html index a953c1206d7..d6ded045136 100644 --- a/src/app/pages/credentials/groups/group-list/group-list.component.html +++ b/src/app/pages/credentials/groups/group-list/group-list.component.html @@ -45,7 +45,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="isLoading$ | async" + [isLoading]="!!(isLoading$ | async)" > ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/cloud-backup/cloud-backup-list/cloud-backup-list.component.html b/src/app/pages/data-protection/cloud-backup/cloud-backup-list/cloud-backup-list.component.html index f8bfc3fc2c2..08626f51e0a 100644 --- a/src/app/pages/data-protection/cloud-backup/cloud-backup-list/cloud-backup-list.component.html +++ b/src/app/pages/data-protection/cloud-backup/cloud-backup-list/cloud-backup-list.component.html @@ -32,7 +32,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" (expanded)="expanded($event)" > diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts b/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts index 844b0a3344c..79eb1e65b4e 100644 --- a/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts +++ b/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts @@ -213,7 +213,7 @@ export class CloudSyncFormComponent implements OnInit { fileNodeProvider: TreeNodeProvider; bucketNodeProvider: TreeNodeProvider; - private editingTask: CloudSyncTaskUi; + private editingTask: CloudSyncTaskUi | null; constructor( private translate: TranslateService, diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-task-card/cloudsync-task-card.component.html b/src/app/pages/data-protection/cloudsync/cloudsync-task-card/cloudsync-task-card.component.html index 57636b8c04a..70084cf1da4 100644 --- a/src/app/pages/data-protection/cloudsync/cloudsync-task-card/cloudsync-task-card.component.html +++ b/src/app/pages/data-protection/cloudsync/cloudsync-task-card/cloudsync-task-card.component.html @@ -33,7 +33,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/replication/replication-form/replication-form.component.ts b/src/app/pages/data-protection/replication/replication-form/replication-form.component.ts index f7becd2548b..f42b73c8e93 100644 --- a/src/app/pages/data-protection/replication/replication-form/replication-form.component.ts +++ b/src/app/pages/data-protection/replication/replication-form/replication-form.component.ts @@ -92,7 +92,7 @@ export class ReplicationFormComponent implements OnInit { readonly requiredRoles = [Role.ReplicationTaskWrite, Role.ReplicationTaskWritePull]; - protected existingReplication: ReplicationTask; + protected existingReplication: ReplicationTask | undefined; constructor( private api: ApiService, diff --git a/src/app/pages/data-protection/replication/replication-list/replication-list.component.html b/src/app/pages/data-protection/replication/replication-list/replication-list.component.html index 56eed5ab256..7d47be99e52 100644 --- a/src/app/pages/data-protection/replication/replication-list/replication-list.component.html +++ b/src/app/pages/data-protection/replication/replication-list/replication-list.component.html @@ -30,7 +30,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/replication/replication-task-card/replication-task-card.component.html b/src/app/pages/data-protection/replication/replication-task-card/replication-task-card.component.html index 2ab8c2084cd..ca339fbd9b5 100644 --- a/src/app/pages/data-protection/replication/replication-task-card/replication-task-card.component.html +++ b/src/app/pages/data-protection/replication/replication-task-card/replication-task-card.component.html @@ -33,7 +33,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/rsync-task/rsync-task-card/rsync-task-card.component.html b/src/app/pages/data-protection/rsync-task/rsync-task-card/rsync-task-card.component.html index dcb1c347aa1..a39c86916fe 100644 --- a/src/app/pages/data-protection/rsync-task/rsync-task-card/rsync-task-card.component.html +++ b/src/app/pages/data-protection/rsync-task/rsync-task-card/rsync-task-card.component.html @@ -33,7 +33,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/rsync-task/rsync-task-list/rsync-task-list.component.html b/src/app/pages/data-protection/rsync-task/rsync-task-list/rsync-task-list.component.html index b9b4b67bb18..d9c353f700e 100644 --- a/src/app/pages/data-protection/rsync-task/rsync-task-list/rsync-task-list.component.html +++ b/src/app/pages/data-protection/rsync-task/rsync-task-list/rsync-task-list.component.html @@ -29,7 +29,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/scrub-task/scrub-list/scrub-list.component.html b/src/app/pages/data-protection/scrub-task/scrub-list/scrub-list.component.html index e54712df1b1..2dcc9ced4fc 100644 --- a/src/app/pages/data-protection/scrub-task/scrub-list/scrub-list.component.html +++ b/src/app/pages/data-protection/scrub-task/scrub-list/scrub-list.component.html @@ -28,7 +28,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/scrub-task/scrub-task-card/scrub-task-card.component.html b/src/app/pages/data-protection/scrub-task/scrub-task-card/scrub-task-card.component.html index 6fbd6d48c11..9ba5ef22e28 100644 --- a/src/app/pages/data-protection/scrub-task/scrub-task-card/scrub-task-card.component.html +++ b/src/app/pages/data-protection/scrub-task/scrub-task-card/scrub-task-card.component.html @@ -43,7 +43,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/smart-task/smart-task-card/smart-task-card.component.html b/src/app/pages/data-protection/smart-task/smart-task-card/smart-task-card.component.html index e5860853af2..44620e5b4b3 100644 --- a/src/app/pages/data-protection/smart-task/smart-task-card/smart-task-card.component.html +++ b/src/app/pages/data-protection/smart-task/smart-task-card/smart-task-card.component.html @@ -31,7 +31,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/smart-task/smart-task-list/smart-task-list.component.html b/src/app/pages/data-protection/smart-task/smart-task-list/smart-task-list.component.html index 592a75babb2..bea5bbcacb6 100644 --- a/src/app/pages/data-protection/smart-task/smart-task-list/smart-task-list.component.html +++ b/src/app/pages/data-protection/smart-task/smart-task-list/smart-task-list.component.html @@ -30,7 +30,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/snapshot-task/snapshot-task-card/snapshot-task-card.component.html b/src/app/pages/data-protection/snapshot-task/snapshot-task-card/snapshot-task-card.component.html index c3b19663e54..1b552ac767a 100644 --- a/src/app/pages/data-protection/snapshot-task/snapshot-task-card/snapshot-task-card.component.html +++ b/src/app/pages/data-protection/snapshot-task/snapshot-task-card/snapshot-task-card.component.html @@ -33,7 +33,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/snapshot-task/snapshot-task-list/snapshot-task-list.component.html b/src/app/pages/data-protection/snapshot-task/snapshot-task-list/snapshot-task-list.component.html index 9cb1e72c18a..5180a47c764 100644 --- a/src/app/pages/data-protection/snapshot-task/snapshot-task-list/snapshot-task-list.component.html +++ b/src/app/pages/data-protection/snapshot-task/snapshot-task-list/snapshot-task-list.component.html @@ -35,7 +35,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/data-protection/vmware-snapshot/vmware-snapshot-list/vmware-snapshot-list.component.html b/src/app/pages/data-protection/vmware-snapshot/vmware-snapshot-list/vmware-snapshot-list.component.html index 164c2215798..3d2cea6e09a 100644 --- a/src/app/pages/data-protection/vmware-snapshot/vmware-snapshot-list/vmware-snapshot-list.component.html +++ b/src/app/pages/data-protection/vmware-snapshot/vmware-snapshot-list/vmware-snapshot-list.component.html @@ -20,7 +20,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > (); - readonly form = this.formBuilder.group({ - refquota: [null as number, this.validators.withMessage( + readonly form = this.formBuilder.nonNullable.group({ + refquota: [null as number | null, this.validators.withMessage( Validators.min(GiB), this.translate.instant(helptextDatasetForm.dataset_form_quota_too_small), )], @@ -46,8 +46,8 @@ export class QuotasSectionComponent implements OnInit { refquota_warning_inherit: [true], refquota_critical: [critical, [Validators.min(0), Validators.max(100)]], refquota_critical_inherit: [true], - refreservation: [null as number], - quota: [null as number, this.validators.withMessage( + refreservation: [null as number | null], + quota: [null as number | null, this.validators.withMessage( Validators.min(GiB), this.translate.instant(helptextDatasetForm.dataset_form_quota_too_small), )], @@ -55,7 +55,7 @@ export class QuotasSectionComponent implements OnInit { quota_warning_inherit: [true], quota_critical: [critical, [Validators.min(0), Validators.max(100)]], quota_critical_inherit: [true], - reservation: [null as number], + reservation: [null as number | null], }); readonly helptext = helptextDatasetForm; diff --git a/src/app/pages/datasets/components/dataset-form/utils/zfs-property.utils.ts b/src/app/pages/datasets/components/dataset-form/utils/zfs-property.utils.ts index cce4d0948e1..e7b50772e80 100644 --- a/src/app/pages/datasets/components/dataset-form/utils/zfs-property.utils.ts +++ b/src/app/pages/datasets/components/dataset-form/utils/zfs-property.utils.ts @@ -3,7 +3,7 @@ import { ZfsPropertySource } from 'app/enums/zfs-property-source.enum'; import { Dataset } from 'app/interfaces/dataset.interface'; import { ZfsProperty } from 'app/interfaces/zfs-property.interface'; -export function getFieldValue(property: ZfsProperty, parent?: Dataset): T | typeof inherit { +export function getFieldValue(property: ZfsProperty, parent?: Dataset): T | typeof inherit | undefined { if (parent) { return valueOrInherit(property); } @@ -11,7 +11,7 @@ export function getFieldValue(property: ZfsProperty, parent?: Dataset): T return property.value; } -export function valueOrInherit(property: ZfsProperty): T | typeof inherit { +export function valueOrInherit(property: ZfsProperty): T | typeof inherit | undefined { if (!property) { return undefined; } diff --git a/src/app/pages/datasets/components/zvol-form/zvol-form.component.ts b/src/app/pages/datasets/components/zvol-form/zvol-form.component.ts index 3cefc1a876e..cb3c1a13517 100644 --- a/src/app/pages/datasets/components/zvol-form/zvol-form.component.ts +++ b/src/app/pages/datasets/components/zvol-form/zvol-form.component.ts @@ -112,7 +112,7 @@ export class ZvolFormComponent implements OnInit { isLoading = false; inheritEncryptPlaceholder: string = helptextZvol.dataset_form_encryption.inherit_checkbox_placeholder; namesInUse: string[] = []; - volBlockSizeWarning: string; + volBlockSizeWarning: string | null; protected slideInData: { isNew: boolean; parentId: string } | null = null; protected encryptedParent = false; diff --git a/src/app/pages/datasets/modules/snapshots/snapshot-list/snapshot-list.component.html b/src/app/pages/datasets/modules/snapshots/snapshot-list/snapshot-list.component.html index f167bc64c09..30fa6d0c01b 100644 --- a/src/app/pages/datasets/modules/snapshots/snapshot-list/snapshot-list.component.html +++ b/src/app/pages/datasets/modules/snapshots/snapshot-list/snapshot-list.component.html @@ -64,7 +64,7 @@ detailsRowIdentifier="name" [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="isLoading$ | async" + [isLoading]="!!(isLoading$ | async)" > diff --git a/src/app/pages/directory-service/components/idmap-list/idmap-list.component.html b/src/app/pages/directory-service/components/idmap-list/idmap-list.component.html index 9118dad0075..067bc5632b2 100644 --- a/src/app/pages/directory-service/components/idmap-list/idmap-list.component.html +++ b/src/app/pages/directory-service/components/idmap-list/idmap-list.component.html @@ -46,7 +46,7 @@

{{ 'Idmap' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/directory-service/components/kerberos-keytabs/kerberos-keytabs-list/kerberos-keytabs-list.component.html b/src/app/pages/directory-service/components/kerberos-keytabs/kerberos-keytabs-list/kerberos-keytabs-list.component.html index 1d44d8e2bae..534ca75b19f 100644 --- a/src/app/pages/directory-service/components/kerberos-keytabs/kerberos-keytabs-list/kerberos-keytabs-list.component.html +++ b/src/app/pages/directory-service/components/kerberos-keytabs/kerberos-keytabs-list/kerberos-keytabs-list.component.html @@ -46,7 +46,7 @@

{{ 'Kerberos Keytab' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/directory-service/components/kerberos-realms/kerberos-realms-list.component.html b/src/app/pages/directory-service/components/kerberos-realms/kerberos-realms-list.component.html index 3a5cd89f283..d6e57142f11 100644 --- a/src/app/pages/directory-service/components/kerberos-realms/kerberos-realms-list.component.html +++ b/src/app/pages/directory-service/components/kerberos-realms/kerberos-realms-list.component.html @@ -46,7 +46,7 @@

{{ 'Kerberos Realms' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/network/components/ipmi-card/ipmi-card.component.html b/src/app/pages/network/components/ipmi-card/ipmi-card.component.html index b5216abc9c4..2d558095350 100644 --- a/src/app/pages/network/components/ipmi-card/ipmi-card.component.html +++ b/src/app/pages/network/components/ipmi-card/ipmi-card.component.html @@ -20,7 +20,7 @@

IPMI

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/network/components/static-routes-card/static-routes-card.component.html b/src/app/pages/network/components/static-routes-card/static-routes-card.component.html index a677c84717d..0ea10bc3aec 100644 --- a/src/app/pages/network/components/static-routes-card/static-routes-card.component.html +++ b/src/app/pages/network/components/static-routes-card/static-routes-card.component.html @@ -29,7 +29,7 @@

{{ 'Static Routes' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > {{ 'Reporting Exporters' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="isLoading$ | async" + [isLoading]="!!(isLoading$ | async)" > >(null); - fetchReport$ = new BehaviorSubject(null); + fetchReport$ = new BehaviorSubject(null); autoRefreshTimer: Subscription; autoRefreshEnabled: boolean; isReady = false; @@ -105,11 +105,11 @@ export class ReportComponent implements OnInit, OnChanges { stepBackDisabled = false; timezone: string; lastEndDateForCurrentZoomLevel = { - '60m': null as number, - '24h': null as number, - '7d': null as number, - '1M': null as number, - '6M': null as number, + '60m': null as number | null, + '24h': null as number | null, + '7d': null as number | null, + '1M': null as number | null, + '6M': null as number | null, }; currentStartDate: number; diff --git a/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.html b/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.html index 8c9da5877c0..405e69bed05 100644 --- a/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.html +++ b/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.html @@ -46,7 +46,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/components/shares-dashboard/nfs-card/nfs-card.component.html b/src/app/pages/sharing/components/shares-dashboard/nfs-card/nfs-card.component.html index 9795c174d89..db4c53e705d 100644 --- a/src/app/pages/sharing/components/shares-dashboard/nfs-card/nfs-card.component.html +++ b/src/app/pages/sharing/components/shares-dashboard/nfs-card/nfs-card.component.html @@ -46,7 +46,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.html b/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.html index 34dae68ec41..146d40576d9 100644 --- a/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.html +++ b/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.html @@ -46,7 +46,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.html b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.html index 28ff91105b2..d57ba136efb 100644 --- a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.html +++ b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.html @@ -1,5 +1,5 @@ - +

{{ 'Authorized Access' | translate }}

@@ -26,7 +26,7 @@

{{ 'Authorized Access' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/iscsi/extent/extent-list/extent-list.component.html b/src/app/pages/sharing/iscsi/extent/extent-list/extent-list.component.html index 4935b00d771..52bce92efbc 100644 --- a/src/app/pages/sharing/iscsi/extent/extent-list/extent-list.component.html +++ b/src/app/pages/sharing/iscsi/extent/extent-list/extent-list.component.html @@ -1,5 +1,5 @@ - +

{{ 'Extents' | translate }}

@@ -27,7 +27,7 @@

{{ 'Extents' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/iscsi/global-target-configuration/global-target-configuration.component.ts b/src/app/pages/sharing/iscsi/global-target-configuration/global-target-configuration.component.ts index fdbd9bfb17e..6b973796127 100644 --- a/src/app/pages/sharing/iscsi/global-target-configuration/global-target-configuration.component.ts +++ b/src/app/pages/sharing/iscsi/global-target-configuration/global-target-configuration.component.ts @@ -58,7 +58,7 @@ export class GlobalTargetConfigurationComponent implements OnInit { protected isLoading = signal(false); isHaSystem = false; - form = this.fb.group({ + form = this.fb.nonNullable.group({ basename: ['', Validators.required], isns_servers: [[] as string[]], pool_avail_threshold: [null as number | null], @@ -139,7 +139,7 @@ export class GlobalTargetConfigurationComponent implements OnInit { } if (isHa && !this.form.controls.alua) { - this.form.addControl('alua', new FormControl(false)); + this.form.addControl('alua', new FormControl(false, { nonNullable: true })); } this.cdr.markForCheck(); diff --git a/src/app/pages/sharing/iscsi/initiator/initiator-form/initiator-form.component.ts b/src/app/pages/sharing/iscsi/initiator/initiator-form/initiator-form.component.ts index a4a8208e131..b2c741c78a9 100644 --- a/src/app/pages/sharing/iscsi/initiator/initiator-form/initiator-form.component.ts +++ b/src/app/pages/sharing/iscsi/initiator/initiator-form/initiator-form.component.ts @@ -61,7 +61,7 @@ export class InitiatorFormComponent implements OnInit { isFormLoading = false; pk: number; - form = this.fb.group({ + form = this.fb.nonNullable.group({ all: [false], comment: [''], new_initiator: [''], @@ -79,7 +79,7 @@ export class InitiatorFormComponent implements OnInit { }); get isAllowAll(): boolean { - return this.form.value.all; + return this.form.getRawValue().all; } readonly helptext = helptextSharingIscsi; @@ -119,7 +119,7 @@ export class InitiatorFormComponent implements OnInit { onSubmit(): void { const payload = { - comment: this.form.value.comment, + comment: this.form.getRawValue().comment, initiators: this.isAllowAll ? [] : this.selectedInitiators().map((item) => item.id), }; diff --git a/src/app/pages/sharing/iscsi/initiator/initiator-list/initiator-list.component.html b/src/app/pages/sharing/iscsi/initiator/initiator-list/initiator-list.component.html index adf54387d12..628e96f8176 100644 --- a/src/app/pages/sharing/iscsi/initiator/initiator-list/initiator-list.component.html +++ b/src/app/pages/sharing/iscsi/initiator/initiator-list/initiator-list.component.html @@ -1,5 +1,5 @@ - +

{{ 'Initiators Groups' | translate }}

@@ -26,7 +26,7 @@

{{ 'Initiators Groups' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/iscsi/iscsi-wizard/iscsi-wizard.component.ts b/src/app/pages/sharing/iscsi/iscsi-wizard/iscsi-wizard.component.ts index c331b7b442e..39112d91f0a 100644 --- a/src/app/pages/sharing/iscsi/iscsi-wizard/iscsi-wizard.component.ts +++ b/src/app/pages/sharing/iscsi/iscsi-wizard/iscsi-wizard.component.ts @@ -94,12 +94,12 @@ export class IscsiWizardComponent implements OnInit { toStop = false; namesInUse: string[] = []; - createdZvol: Dataset; - createdExtent: IscsiExtent; - createdPortal: IscsiPortal; - createdInitiator: IscsiInitiatorGroup; - createdTarget: IscsiTarget; - createdTargetExtent: IscsiTargetExtent; + createdZvol: Dataset | undefined; + createdExtent: IscsiExtent | undefined; + createdPortal: IscsiPortal | undefined; + createdInitiator: IscsiInitiatorGroup | undefined; + createdTarget: IscsiTarget | undefined; + createdTargetExtent: IscsiTargetExtent | undefined; form = this.fb.group({ target: this.fb.group({ @@ -115,18 +115,18 @@ export class IscsiWizardComponent implements OnInit { type: [IscsiExtentType.Disk, [Validators.required]], path: [mntPath, [Validators.required]], filesize: [0, [Validators.required]], - disk: [null as string, [Validators.required]], + disk: [null as string | null, [Validators.required]], dataset: ['', [Validators.required]], - volsize: [null as number, [Validators.required]], + volsize: [null as number | null, [Validators.required]], usefor: [IscsiExtentUsefor.Vmware, [Validators.required]], }), options: this.fb.group({ - portal: [null as typeof newOption | number, [Validators.required]], + portal: [null as typeof newOption | number | null, [Validators.required]], listen: this.fb.array([]), initiators: [[] as string[]], fcport: this.fb.group({ port: [nullOption as string, [Validators.required]], - host_id: [null as number, [Validators.required]], + host_id: [null as number | null, [Validators.required]], }), }), }, { @@ -220,7 +220,7 @@ export class IscsiWizardComponent implements OnInit { } get targetPayload(): IscsiTargetUpdate { - const value = this.form.value; + const value = this.form.getRawValue(); return { name: value.extent.name, diff --git a/src/app/pages/sharing/iscsi/portal/portal-list/portal-list.component.html b/src/app/pages/sharing/iscsi/portal/portal-list/portal-list.component.html index 0baf67ca55f..4680c6db5a3 100644 --- a/src/app/pages/sharing/iscsi/portal/portal-list/portal-list.component.html +++ b/src/app/pages/sharing/iscsi/portal/portal-list/portal-list.component.html @@ -1,5 +1,5 @@ - +

{{ 'Portals' | translate }}

@@ -26,7 +26,7 @@

{{ 'Portals' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/iscsi/target/all-targets/target-list/target-list.component.html b/src/app/pages/sharing/iscsi/target/all-targets/target-list/target-list.component.html index ecc3dd20261..92e79601e4a 100644 --- a/src/app/pages/sharing/iscsi/target/all-targets/target-list/target-list.component.html +++ b/src/app/pages/sharing/iscsi/target/all-targets/target-list/target-list.component.html @@ -1,5 +1,5 @@ - +

{{ 'Targets' | translate }}

@@ -25,7 +25,7 @@

{{ 'Targets' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider()" - [isLoading]="dataProvider().isLoading$ | async" + [isLoading]="!!(dataProvider().isLoading$ | async)" (expanded)="expanded($event)" > diff --git a/src/app/pages/sharing/iscsi/target/target-form/target-form.component.ts b/src/app/pages/sharing/iscsi/target/target-form/target-form.component.ts index 7ceb2a313a9..974914f4bd0 100644 --- a/src/app/pages/sharing/iscsi/target/target-form/target-form.component.ts +++ b/src/app/pages/sharing/iscsi/target/target-form/target-form.component.ts @@ -146,7 +146,7 @@ export class TargetFormComponent implements OnInit { fcForm = this.formBuilder.group({ port: [nullOption as string, [Validators.required]], - host_id: [null as number, [Validators.required]], + host_id: [null as number | null, [Validators.required]], }); constructor( @@ -187,7 +187,7 @@ export class TargetFormComponent implements OnInit { } onSubmit(): void { - const values = this.form.value; + const values = this.form.getRawValue(); this.isLoading = true; this.cdr.markForCheck(); diff --git a/src/app/pages/sharing/nfs/nfs-list/nfs-list.component.html b/src/app/pages/sharing/nfs/nfs-list/nfs-list.component.html index 2185e090606..2edd6cb7c18 100644 --- a/src/app/pages/sharing/nfs/nfs-list/nfs-list.component.html +++ b/src/app/pages/sharing/nfs/nfs-list/nfs-list.component.html @@ -1,5 +1,5 @@ - +

{{ 'NFS' | translate }}

@@ -37,7 +37,7 @@

{{ 'NFS' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/nfs/nfs-session-list/nfs-session-list.component.html b/src/app/pages/sharing/nfs/nfs-session-list/nfs-session-list.component.html index 41147050513..5dae8a639e6 100644 --- a/src/app/pages/sharing/nfs/nfs-session-list/nfs-session-list.component.html +++ b/src/app/pages/sharing/nfs/nfs-session-list/nfs-session-list.component.html @@ -61,7 +61,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/smb/smb-acl/smb-acl.component.ts b/src/app/pages/sharing/smb/smb-acl/smb-acl.component.ts index e042bccc2d4..dcb6670eaf6 100644 --- a/src/app/pages/sharing/smb/smb-acl/smb-acl.component.ts +++ b/src/app/pages/sharing/smb/smb-acl/smb-acl.component.ts @@ -127,7 +127,7 @@ export class SmbAclComponent implements OnInit { private errorHandler: FormErrorHandlerService, private translate: TranslateService, private userService: UserService, - public slideInRef: SlideInRef, + public slideInRef: SlideInRef, ) { this.shareName = slideInRef.getData(); } @@ -150,8 +150,8 @@ export class SmbAclComponent implements OnInit { both: [null as never], user: [null as never], group: [null as never], - ae_perm: [null as SmbSharesecPermission], - ae_type: [null as SmbSharesecType], + ae_perm: [null as SmbSharesecPermission | null], + ae_type: [null as SmbSharesecType | null], }), ); } diff --git a/src/app/pages/sharing/smb/smb-form/smb-form.component.ts b/src/app/pages/sharing/smb/smb-form/smb-form.component.ts index 68826fb5517..9b7222623c4 100644 --- a/src/app/pages/sharing/smb/smb-form/smb-form.component.ts +++ b/src/app/pages/sharing/smb/smb-form/smb-form.component.ts @@ -5,7 +5,9 @@ import { Component, OnInit, } from '@angular/core'; -import { Validators, FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { + Validators, ReactiveFormsModule, NonNullableFormBuilder, +} from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { MatDialog } from '@angular/material/dialog'; @@ -88,8 +90,8 @@ import { selectService } from 'app/store/services/services.selectors'; ], }) export class SmbFormComponent implements OnInit, AfterViewInit { - private existingSmbShare: SmbShare | null; - defaultSmbShare: SmbShare; + private existingSmbShare: SmbShare | undefined; + defaultSmbShare: SmbShare | undefined; isLoading = false; isAdvancedMode = false; @@ -193,7 +195,7 @@ export class SmbFormComponent implements OnInit, AfterViewInit { form = this.formBuilder.group({ path: ['', Validators.required], name: ['', Validators.required], - purpose: [null as SmbPresetType], + purpose: [null as SmbPresetType | null], comment: [''], enabled: [true], acl: [false], @@ -205,7 +207,7 @@ export class SmbFormComponent implements OnInit, AfterViewInit { hostsdeny: [[] as string[]], home: [false], timemachine: [false], - timemachine_quota: [null as number], + timemachine_quota: [null as number | null], afp: [false], shadowcopy: [false], recyclebin: [false], @@ -225,7 +227,7 @@ export class SmbFormComponent implements OnInit, AfterViewInit { constructor( public formatter: IxFormatterService, private cdr: ChangeDetectorRef, - private formBuilder: FormBuilder, + private formBuilder: NonNullableFormBuilder, private api: ApiService, private matDialog: MatDialog, private dialogService: DialogService, @@ -375,9 +377,9 @@ export class SmbFormComponent implements OnInit, AfterViewInit { /** * @returns Observable to allow setting warnings for values changes once default or previous preset is applied */ - setupAndApplyPurposePresets(): Observable { + setupAndApplyPurposePresets(): Observable { return this.api.call('sharing.smb.presets').pipe( - switchMap((presets) => { + tap((presets) => { const nonClusterPresets = Object.entries(presets).reduce( (acc, [presetName, preset]) => { if (!preset.cluster) { @@ -399,7 +401,6 @@ export class SmbFormComponent implements OnInit, AfterViewInit { : this.existingSmbShare?.purpose, ); this.cdr.markForCheck(); - return of(null); }), ); } diff --git a/src/app/pages/sharing/smb/smb-list/smb-list.component.html b/src/app/pages/sharing/smb/smb-list/smb-list.component.html index b1f7ea46a65..545bf7b2022 100644 --- a/src/app/pages/sharing/smb/smb-list/smb-list.component.html +++ b/src/app/pages/sharing/smb/smb-list/smb-list.component.html @@ -1,5 +1,5 @@ - +

{{ 'SMB' | translate }}

{{ 'SMB' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/smb/smb-status/components/smb-lock-list/smb-lock-list.component.html b/src/app/pages/sharing/smb/smb-status/components/smb-lock-list/smb-lock-list.component.html index d6a42abeef8..9a5b491bd60 100644 --- a/src/app/pages/sharing/smb/smb-status/components/smb-lock-list/smb-lock-list.component.html +++ b/src/app/pages/sharing/smb/smb-status/components/smb-lock-list/smb-lock-list.component.html @@ -28,7 +28,7 @@

{{ 'Locks' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/smb/smb-status/components/smb-notification-list/smb-notification-list.component.html b/src/app/pages/sharing/smb/smb-status/components/smb-notification-list/smb-notification-list.component.html index 7737ed1caa6..c5a158b3c31 100644 --- a/src/app/pages/sharing/smb/smb-status/components/smb-notification-list/smb-notification-list.component.html +++ b/src/app/pages/sharing/smb/smb-status/components/smb-notification-list/smb-notification-list.component.html @@ -27,7 +27,7 @@

{{ 'Notifications' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/smb/smb-status/components/smb-open-files/smb-open-files.component.html b/src/app/pages/sharing/smb/smb-status/components/smb-open-files/smb-open-files.component.html index bf7f5654ef4..8612c1049a2 100644 --- a/src/app/pages/sharing/smb/smb-status/components/smb-open-files/smb-open-files.component.html +++ b/src/app/pages/sharing/smb/smb-status/components/smb-open-files/smb-open-files.component.html @@ -17,7 +17,7 @@

{{ 'Open Files' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/smb/smb-status/components/smb-session-list/smb-session-list.component.html b/src/app/pages/sharing/smb/smb-status/components/smb-session-list/smb-session-list.component.html index fee4238ec34..fa03881b19a 100644 --- a/src/app/pages/sharing/smb/smb-status/components/smb-session-list/smb-session-list.component.html +++ b/src/app/pages/sharing/smb/smb-status/components/smb-session-list/smb-session-list.component.html @@ -27,7 +27,7 @@

{{ 'Sessions' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/sharing/smb/smb-status/components/smb-share-list/smb-share-list.component.html b/src/app/pages/sharing/smb/smb-status/components/smb-share-list/smb-share-list.component.html index d41bd1df503..cdcc1576186 100644 --- a/src/app/pages/sharing/smb/smb-status/components/smb-share-list/smb-share-list.component.html +++ b/src/app/pages/sharing/smb/smb-status/components/smb-share-list/smb-share-list.component.html @@ -27,7 +27,7 @@

{{ 'Shares' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/storage/modules/disks/components/disk-list/disk-list.component.html b/src/app/pages/storage/modules/disks/components/disk-list/disk-list.component.html index 35bc86672e0..739387fbec5 100644 --- a/src/app/pages/storage/modules/disks/components/disk-list/disk-list.component.html +++ b/src/app/pages/storage/modules/disks/components/disk-list/disk-list.component.html @@ -1,4 +1,4 @@ - + @@ -49,7 +49,7 @@ detailsRowIdentifier="identifier" [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/storage/modules/disks/components/smart-test-result-list/smart-test-result-list.component.html b/src/app/pages/storage/modules/disks/components/smart-test-result-list/smart-test-result-list.component.html index 4bc78c20bac..3dc9b019915 100644 --- a/src/app/pages/storage/modules/disks/components/smart-test-result-list/smart-test-result-list.component.html +++ b/src/app/pages/storage/modules/disks/components/smart-test-result-list/smart-test-result-list.component.html @@ -17,7 +17,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/system/advanced/cron/cron-card/cron-card.component.html b/src/app/pages/system/advanced/cron/cron-card/cron-card.component.html index 1cb3da1cdd0..a6cce1f6ad6 100644 --- a/src/app/pages/system/advanced/cron/cron-card/cron-card.component.html +++ b/src/app/pages/system/advanced/cron/cron-card/cron-card.component.html @@ -31,7 +31,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/system/advanced/init-shutdown/init-shutdown-card/init-shutdown-card.component.html b/src/app/pages/system/advanced/init-shutdown/init-shutdown-card/init-shutdown-card.component.html index 4dacf991631..436588ab5a2 100644 --- a/src/app/pages/system/advanced/init-shutdown/init-shutdown-card/init-shutdown-card.component.html +++ b/src/app/pages/system/advanced/init-shutdown/init-shutdown-card/init-shutdown-card.component.html @@ -31,7 +31,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/system/advanced/sysctl/sysctl-card/sysctl-card.component.html b/src/app/pages/system/advanced/sysctl/sysctl-card/sysctl-card.component.html index 62b8790ef94..1f236e8ecf5 100644 --- a/src/app/pages/system/advanced/sysctl/sysctl-card/sysctl-card.component.html +++ b/src/app/pages/system/advanced/sysctl/sysctl-card/sysctl-card.component.html @@ -33,7 +33,7 @@

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/system/advanced/sysctl/tunable-list/tunable-list.component.html b/src/app/pages/system/advanced/sysctl/tunable-list/tunable-list.component.html index b1b6f246e91..2c9c79d01f8 100644 --- a/src/app/pages/system/advanced/sysctl/tunable-list/tunable-list.component.html +++ b/src/app/pages/system/advanced/sysctl/tunable-list/tunable-list.component.html @@ -21,7 +21,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/system/alert-service/alert-service-list/alert-service-list.component.html b/src/app/pages/system/alert-service/alert-service-list/alert-service-list.component.html index b8d705ee54d..be32dd556de 100644 --- a/src/app/pages/system/alert-service/alert-service-list/alert-service-list.component.html +++ b/src/app/pages/system/alert-service/alert-service-list/alert-service-list.component.html @@ -33,7 +33,7 @@

{{ 'Alert Services' | translate }}

ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/system/bootenv/bootenv-list/bootenv-list.component.html b/src/app/pages/system/bootenv/bootenv-list/bootenv-list.component.html index 2e4062c5b52..a81031ed32f 100644 --- a/src/app/pages/system/bootenv/bootenv-list/bootenv-list.component.html +++ b/src/app/pages/system/bootenv/bootenv-list/bootenv-list.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/pages/system/enclosure/components/jbof-list/jbof-list.component.html b/src/app/pages/system/enclosure/components/jbof-list/jbof-list.component.html index f7cfb8de4e6..a0574ede011 100644 --- a/src/app/pages/system/enclosure/components/jbof-list/jbof-list.component.html +++ b/src/app/pages/system/enclosure/components/jbof-list/jbof-list.component.html @@ -28,7 +28,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > diff --git a/src/app/pages/vm/devices/device-list/device-list/device-list.component.html b/src/app/pages/vm/devices/device-list/device-list/device-list.component.html index 3e126548efb..6c1c61f0b87 100644 --- a/src/app/pages/vm/devices/device-list/device-list/device-list.component.html +++ b/src/app/pages/vm/devices/device-list/device-list/device-list.component.html @@ -20,7 +20,7 @@ ix-table-body [columns]="columns" [dataProvider]="dataProvider" - [isLoading]="dataProvider.isLoading$ | async" + [isLoading]="!!(dataProvider.isLoading$ | async)" > { diff --git a/src/assets/i18n/es-ar.json b/src/assets/i18n/es-ar.json index b3130ca0776..51b8187dea6 100644 --- a/src/assets/i18n/es-ar.json +++ b/src/assets/i18n/es-ar.json @@ -2,12 +2,6 @@ "": "", "Archs": "", "Flash Identify Light": "", - "Force-remove iXVolumes": "Forzar la eliminación de iXVolumes", - "Generic dataset suitable for any share type.": "Conjunto de datos genérico adecuado para cualquier tipo de compartición", - "Group Bind Path": "Ruta de enlace de grupo", - "Group Execute": "Ejecución grupal", - "Group Quota Manager": "Administrador de cuotas de grupo", - "KDC": "KDC", "Kerberos Keytabs": "", "Kerberos Principal": "", "Kerberos Realms": "", @@ -20,7 +14,6 @@ "LACPDU Rate": "", "LBA of First Error": "", "LDAP Realm": "", - "LDAP Timeout": "Tiempo de espera de LDAP", "LDAP User DN": "", "LDAP User DN Password": "", "LDAP configuration updated": "", @@ -28,35 +21,20 @@ "LUN ID": "", "LUN RPM": "", "Lan": "", - "Last Resilver": "Último resilver", - "Leaving": "Saliendo", "Licensed Serials": "", - "Light status is unknown.": "Estado de la luz desconocido", "Locks": "", - "Log Excerpt": "Extracto del registro", - "Log In To Outlook": "Iniciar sesión en Outlook", - "Log VDEVs": "VDEVs de registro", "Log in to {oauthType} to set up Oauth credentials.": "", "Logged In To Outlook": "", - "Login To Jira To Submit": "Iniciar sesión en Jira para enviar", - "Login error. Please try again.": "Error de inicio de sesión. Probá de nuevo.", - "Login was canceled. Please try again if you want to connect your account.": "Se canceló el inicio de sesión. Probá de nuevo si deseás conectar tu cuenta.", "MOTD": "", "Machine Time: {machineTime} \n Browser Time: {browserTime}": "", - "Macvlan NICs": "NICs de macvlan", "Mail Server Port": "", - "Major": "Importante", "Make the currently active TrueNAS controller the default when both TrueNAS controllers are online and HA is enabled. To change the default TrueNAS controller, unset this option on the default TrueNAS controller and allow the system to fail over. This briefly interrupts system services.": "", "Makes the group available for permissions editors over SMB protocol (and the share ACL editor). It is not used for SMB authentication or determining the user session token or internal permissions checks.": "", "Manage Global SED Password": "", "Max Concurrent Calls": "", "Max concurrent calls limit reached.\n There are more than 20 calls queued.\n See queued calls in the browser's console logs": "", - "Memory device": "Dispositivo de memoria", - "Memory usage of app": "Uso de memoria de la app", "Metadata (Special) Small Block Size": "", - "Metadata VDEVs": "VDEVs de metadatos", "Method Call": "", - "Method of snapshot transfer:
": "Método de transferencia de instantáneas:
  • SSH es compatible con la mayoría de los sistemas. Requiere una conexión creada previamente en Sistema > Conexiones SSH.
  • SSH+NETCAT utiliza SSH para establecer una conexión con el sistema de destino y luego utiliza py-libzfs para enviar un flujo de datos sin cifrar para lograr velocidades de transferencia más altas. Esto solo funciona cuando se replica a un TrueNAS u otro sistema con py-libzfs instalado.
  • LOCAL replica de manera eficiente las instantáneas a otro conjunto de datos en el mismo sistema sin usar la red.
  • HEREDADO utiliza el motor de replicación heredado de FreeNAS 11.2 y versiones anteriores.
", "Mixing disks of different sizes in a vdev is not recommended.": "", "Modern OS: Extent block size 4k, TPC enabled, no Xen compat mode, SSD speed": "", "NAA": "", @@ -83,7 +61,6 @@ "New Virtual Machine": "", "No Enclosure Dispersal Strategy": "", "No Isolated GPU Device(s) configured": "", - "No connections": "Sin conexiones", "Offload Read": "", "Offload Write": "", "One-Time Password (if necessary)": "", @@ -98,7 +75,6 @@ "Other node is currently configuring the system dataset.": "", "Other node is currently processing a failover event.": "", "Outgoing Mail Server": "", - "Outlook": "Outlook", "PCI Passthrough Device": "", "PCI device does not have a reset mechanism defined and you may experience inconsistent/degraded behavior when starting/stopping the VM.": "", "Parent": "", @@ -155,24 +131,6 @@ "Quota Fill Warning (in %)": "", "Quota settings updated": "", "Quotas added": "", - "Quotas set for {n, plural, one {# group} other {# groups} }": "Cuotas establecidas para {n, plural, one {# grupo} other {# grupos} }", - "Quotas set for {n, plural, one {# user} other {# users} }": "Cuotas establecidas para {n, plural, one {# usuario} other {# usuarios} }", - "Quotas updated": "Cuotas actualizadas", - "REQUIRE": "REQUIERE", - "Read ACL": "Leer ACL", - "Read Attributes": "Leer propiedades", - "Read Data": "Leer datos", - "Read Named Attributes": "Leer propiedades nombradas", - "Rear": "Trasero", - "Reboot Local": "Reiniciar local", - "Reboot of the other node is required for FIPS changes.": "Es necesario reiniciar el otro nodo para realizar cambios de FIPS.", - "Reboot of this node is required for FIPS changes.": "Es necesario reiniciar este nodo para realizar cambios de FIPS.", - "Received: {received}/s Sent: {sent}/s": "Recibido: {received}/s Enviado: {sent}/s", - "Redfish administrative password.": "Contraseña administrativa de Redfish.", - "Redfish administrative username.": "Nombre de usuario administrativo de Redfish.", - "Register": "Registro", - "Register Default Gateway": "Registrar puerta de enlace predeterminada", - "Regularly scheduled system checks and updates.": "Comprobaciones y actualizaciones del sistema programadas periódicamente.", "Release": "", "Replication Task Config Read": "", "Replication Task Config Write": "", @@ -181,27 +139,8 @@ "Replication Task Wizard": "", "Replication Task Write": "", "Replication Task Write Pull": "", - "Replication «{name}» has started.": "Se inició la replicación «{name}».", "Reporting Exporter": "", "Reporting Exporters": "", - "Reporting Read": "Informe de lectura", - "Reporting Write": "Informe de escritura", - "Requested action performed for selected Applications": "Acción solicitada realizada para aplicaciones seleccionadas", - "Requested action performed for selected Instances": "Acción solicitada realizada para instancias seleccionadas", - "Require IDENT Authentication": "Requerir autenticación IDENT", - "Required reset to fix system operation issues.": "Reinicio necesario para solucionar problemas de funcionamiento del sistema.", - "Required restart after new software installation.": "Es necesario reiniciar después de instalar un nuevo software.", - "Required – always encrypt transport (rejecting access if client does not support encryption – incompatible with SMB1 server enable_smb1)": "Obligatorio: siempre cifrar el transporte (rechazar el acceso si el cliente no admite el cifrado; incompatible con el servidor SMB1 enable_smb1)", - "Reset Default Config": "Restablecer config predeterminada", - "Reset Defaults": "Restablecer valores predeterminados", - "Reset Search": "Restablecer búsqueda", - "Resilver configuration saved": "Configuración de Resilver guardada", - "Resilvering": "Resilverización", - "Resilvering Status": "Estado de la resilverización", - "Resilvering pool: ": "Resilverización del pool", - "Resilvering:": "Resilverización:", - "Retry": "Reintentar", - "Rolling back...": "Retrocediendo...", "Root TCP Socket": "", "SAN": "", "SMB - Operation Close": "", @@ -218,15 +157,12 @@ "SMB Lock": "", "SMB Locks": "", "SMB Open File": "", - "SMB multichannel allows servers to use multiple network connections simultaneously by combining the bandwidth of several network interface cards (NICs) for better performance. SMB multichannel does not function if you combine NICs into a LAGG. Read more in docs": "El multicanal SMB permite a los servidores utilizar varias conexiones de red simultáneamente combinando el ancho de banda de varias tarjetas de interfaz de red (NIC) para lograr un mejor rendimiento. El multicanal SMB no funciona si se combinan las NIC en un LAGG. Más información en la documentación", - "SMB preset sets most optimal settings for SMB sharing.": "Los ajustes preestablecidos de SMB establecen las configuraciones más óptimas para compartir SMB.", "SSSD Compat": "", "Script": "", "Scrub Boot Pool": "", "Search UI": "", "Serial Shell": "", "Service = \"SMB\" AND Event = \"CLOSE\"": "", - "Set an expiration date-time for the API key.": "Establecer una fecha y hora de vencimiento para la clave de API.", "Set to automatically configure the IPv6. Only one interface can be configured this way.": "", "Set to boot a debug kernel after the next system restart.": "", "Set to enable Samba to do DNS updates when joining a domain.": "", @@ -282,19 +218,6 @@ "Tests are only performed when Never is selected.": "", "Thank you for sharing your feedback with us! Your insights are valuable in helping us improve our product.": "", "Thank you. Ticket was submitted succesfully.": "", - "The {name} dataset and all snapshots stored with it will be permanently deleted.": "El conjunto de datos {name} y todas las instantáneas almacenadas con él se van a eliminar permanentemente.", - "The {name} zvol and all snapshots stored with it will be permanently deleted.": "El zvol {name} y todas las instantáneas almacenadas con él se van a eliminar permanentemente.", - "The SMB share ACL defines access rights for users of this SMB share up to, but not beyond, the access granted by filesystem ACLs.": "Las ACL del recurso compartido SMB define los derechos de acceso para los usuarios de este recurso compartido SMB hasta, pero no más allá, del acceso otorgado por las ACL del sistema de archivos.", - "The TrueNAS controllers do not have the same quantity of disks.": "Los controladores TrueNAS no tienen la misma cantidad de discos.", - "The VM could not start because the current configuration could potentially require more RAM than is available on the system. Memory overcommitment allows multiple VMs to be launched when there is not enough free memory for configured RAM of all VMs. Use with caution. Would you like to overcommit memory?": "La VM no pudo iniciarse porque la configuración actual podría requerir más RAM de la que está disponible en el sistema. La sobreasignación de memoria permite que se inicien varias VMs cuando no hay suficiente memoria libre para la RAM configurada de todas las VMs. Usá con precaución. ¿Deseás sobreasignar memoria?", - "The chosen preset ACL will REPLACE the ACL currently displayed in the form and delete any unsaved changes.": "La ACL preestablecida elegida va a REEMPLAZAR la ACL que se muestra actualmente en el formulario y va a eliminar cualquier cambio no guardado.", - "The credentials are valid.": "Las credenciales son válidas.", - "The current pool layout is not recommended. Override the following errors?": "El diseño actual de pool no es el recomendado. ¿Deseás anular los siguientes errores?", - "The default \"Checksum\" value for datasets with deduplication used to be SHA256.\n Our testing has shown that SHA512 performs better for such datasets.\n We've changed the checksum value from SHA256 to SHA512. You can change it back in \"Advanced Options\".": "El valor predeterminado de \"Suma de comprobación\" para los conjuntos de datos con deduplicación solía ser SHA256.\n Nuestras pruebas demostraron que SHA512 funciona mejor para dichos conjuntos de datos.\n Canbiamos el valor de suma de comprobación de SHA256 a SHA512. Podés volver a cambiarlo en \"Opciones avanzadas\".", - "The directory base suffix to use for SID/uid/gid mapping entries. Example: dc=test,dc=org. When undefined, idmap_ldap defaults to using the ldap idmap suffix option from smb.conf.": "El sufijo base del directorio que se va a usar para las entradas de mapeo de SID/uid/gid. Ejemplo: dc=test,dc=org. Cuando no está definido, idmap_ldap utiliza de manera predeterminada la opción de sufijo idmap de ldap de smb.conf.", - "The domain for local users is the NetBIOS name of the TrueNAS server.": "El dominio para los usuarios locales es el nombre NetBIOS del servidor TrueNAS.", - "The expanded vdev uses the pre-expanded parity ratio, which reduces the total vdev capacity. To reset the vdev parity ratio and fully use the new capacity, manually rewrite all data in the vdev. This process takes time and is irreversible.": "El vdev ampliado utiliza la proporción de paridad preexpandida, lo que reduce la capacidad total del vdev. Para restablecer la relación de paridad del vdev y utilizar por completo la nueva capacidad, reescribí manualmente todos los datos en el vdev. Este proceso lleva tiempo y es irreversible.", - "The filesystem {filesystemName} is {filesystemDescription}, but datastore {datastoreName} is {datastoreDescription}. Is this correct?": "El sistema de archivos {filesystemName} es {filesystemDescription}, pero el almacén de datos {datastoreName} es {datastoreDescription}. ¿Es correcto?", "The following { n, plural, one {boot environment} other {# boot environments} } will be deleted. Are you sure you want to proceed?": "", "Tunable": "", "UI Search Result: {result}": "", @@ -2273,6 +2196,7 @@ "Force the VM to stop if it has not already stopped within the specified shutdown timeout. Without this option selected, the VM will receive the shutdown signal, but may or may not complete the shutdown process.": "Obliga a la VM a detenerse si aún no se detuvo dentro del tiempo de espera de apagado especificado. Sin esta opción seleccionada, la VM recibirá la señal de apagado, pero puede o no completar el proceso de apagado.", "Force unmount": "Forzar desmontaje", "Force using Signature Version 2 to sign API requests. Set this only if your AWS provider does not support default version 4 signatures.": "Forzar el uso de Signature Version 2 para firmar solicitudes de API. Establecé esta opción solo si tu proveedor de AWS no admite firmas de la versión 4 predeterminadas.", + "Force-remove iXVolumes": "Forzar la eliminación de iXVolumes", "Forces the addition of the NTP server, even if it is currently unreachable.": "Fuerza la adición del servidor NTP, incluso si actualmente no es accesible.", "Forcing the other TrueNAS controller to become active requires a failover. This will temporarily interrupt system services. After confirmation, SAVE AND FAILOVER must be clicked on the previous screen.": "Para forzar la activación del otro controlador TrueNAS es necesario realizar una conmutación por error. Esto interrumpirá temporalmente los servicios del sistema. Después de la confirmación, se debe hacer clic en GUARDAR Y CONMUTACIÓN POR ERROR en la pantalla anterior.", "Forums": "Foros", @@ -2322,6 +2246,7 @@ "Generate an alert when the pool has this percent space remaining. This is typically configured at the pool level when using zvols or at the extent level for both file and device based extents.": "Genere una alerta cuando el pool tenga este porcentaje de espacio restante. Esto generalmente se configura a nivel de pool cuando se usan zvols o en el nivel de extensión para extensiones basadas en archivos y dispositivos.", "Generate idmap low range based on same algorithm that SSSD uses by default.": "Genere un rango bajo de idmap basado en el mismo algoritmo que SSSD usa de manera predeterminada.", "Generic": "Genérico", + "Generic dataset suitable for any share type.": "Conjunto de datos genérico adecuado para cualquier tipo de compartición", "Get Support": "Conseguir ayuda", "Give your exporter configuration a name": "Dale un nombre a tu configuración de exportador", "Gives control of how much of the new device is made available to ZFS. Set to use the entire capacity of the new device.": "Da el control de cuánto del nuevo dispositivo está disponible para ZFS. Configurado para usar toda la capacidad del nuevo dispositivo.", @@ -2352,13 +2277,16 @@ "Google Drive": "Google Drive", "Google Photos": "Google Fotos", "Group": "Grupo", + "Group Bind Path": "Ruta de enlace de grupo", "Group Configuration": "Configuración del grupo", "Group Data Quota ": "Cuota de datos de grupo ", + "Group Execute": "Ejecución grupal", "Group ID": "ID de Grupo", "Group ID created in Authorized Access. Required when the Discovery Authentication Method is set to CHAP or Mutual CHAP.": "ID del grupo creado en Acceso Autorizado. Requerido cuando el Método de Autenticación de Descubrimiento se establece en CHAP o CHAP Mutuo.", "Group Members": "Miembros del grupo", "Group Obj": "Obj de grupo", "Group Object Quota": "Cuota de objetos de grupo", + "Group Quota Manager": "Administrador de cuotas de grupo", "Group Quotas": "Cuotas de grupo", "Group Read": "Lectura del grupo", "Group Write": "Escritura del grupo", @@ -2693,6 +2621,7 @@ "Joining": "Uniendo", "Jul": "Julio", "Jun": "Junio", + "KDC": "KDC", "KMIP": "KMIP", "KMIP Key Status": "Estado de clave KMIP", "KMIP Read": "Lectura de KMIP", @@ -2729,6 +2658,7 @@ "LDAP - Primary Domain": "LDAP: Dominio Primario", "LDAP Domain": "Dominio LDAP", "LDAP Server": "Servidor LDAP", + "LDAP Timeout": "Tiempo de espera de LDAP", "LDAP server hostnames or IP addresses. Separate entries with an empty space. Multiple hostnames or IP addresses can be entered to create an LDAP failover priority list. If a host does not respond, the next host in the list is tried until a new connection is established.": "Nombres de host del servidor LDAP o direcciones IP. Entradas separadas con un espacio vacío. Se pueden ingresar varios nombres de host o direcciones IP para crear una lista de prioridades de conmutación por error LDAP. Si un host no responde, se prueba el siguiente host de la lista hasta que se establezca una nueva conexión.", "LDAP server to use for SID/uid/gid map entries. When undefined, idmap_ldap uses *ldap://localhost/*. Example: ldap://ldap.netscape.com/o=Airius.com.": "Servidor LDAP para usar para entradas de mapa SID/uid/gid. Cuando no está definido, idmap_ldap usa * ldap://localhost/*. Ejemplo: ldap: //ldap.netscape.com/o=Airius.com.", "LDAP timeout in seconds. Increase this value if a Kerberos ticket timeout occurs.": "Tiempo de espera de LDAP en segundos. Aumente este valor si se produce un tiempo de espera de ticket Kerberos.", @@ -2743,6 +2673,7 @@ "Last 24 hours": "Ultimas 24 horas", "Last 3 days": "Últimos 3 días", "Last Page": "Última página", + "Last Resilver": "Último resilver", "Last Run": "Última ejecución", "Last Scan": "Último escaneo", "Last Scan Duration": "Duración del último escaneo", @@ -2766,6 +2697,7 @@ "Leave empty or select number of existing portal to use.": "Dejá en blanco o seleccioná el número de portal existente para usar.", "Leave empty to allow all host CPUs to be used.": "Dejá vacío para permitir el uso de todas las CPUs del host.", "Leave empty to not limit instance memory.": "Dejá vacío para no limitar la memoria de la instancia.", + "Leaving": "Saliendo", "Leaving the domain requires sufficient privileges. Enter your credentials below.": "Dejar el dominio requiere suficientes privilegios. Ingresá tus credenciales a continuación.", "Legacy": "Heredado", "Legacy AFP Compatibility": "Compatibilidad con AFP heredada", @@ -2784,6 +2716,7 @@ "License": "Licencia", "License Update": "Actualización de licencia", "Lifetime": "Toda la vida", + "Light status is unknown.": "Estado de la luz desconocido", "Limit": "Limitar", "Limit Pool To A Single Enclosure": "Limitar pool a un solo recinto", "Limit To {name} Enclosure": "Limitar al recinto {name}", @@ -2830,12 +2763,15 @@ "Locking Dataset": "Bloqueo de conjunto de datos", "Log": "Registro", "Log Details": "Detalles del registro", + "Log Excerpt": "Extracto del registro", "Log In": "Iniciar sesión", "Log In To Gmail": "Iniciar sesión en Gmail", + "Log In To Outlook": "Iniciar sesión en Outlook", "Log In To Provider": "Iniciar sesión en el proveedor", "Log Level": "Nivel de registro", "Log Out": "Cerrar sesión", "Log Path": "Ruta de registro", + "Log VDEVs": "VDEVs de registro", "Logged In To Gmail": "Inició sesión en Gmail", "Logged In To Jira": "Inició sesión en Jira", "Logged In To Provider": "Inició sesión en el proveedor", @@ -2844,6 +2780,9 @@ "Logical Block Size": "Tamaño del bloque lógico", "Login Attempts": "Intentos de acceso", "Login Banner": "Pancarta de inicio de sesión", + "Login To Jira To Submit": "Iniciar sesión en Jira para enviar", + "Login error. Please try again.": "Error de inicio de sesión. Probá de nuevo.", + "Login was canceled. Please try again if you want to connect your account.": "Se canceló el inicio de sesión. Probá de nuevo si deseás conectar tu cuenta.", "Logoff": "Desconectarse", "Logout": "Cerrar sesión", "Logs": "Registros", @@ -2865,8 +2804,10 @@ "MTU": "MTU", "Mac Address": "Dirección Mac", "Machine": "Máquina", + "Macvlan NICs": "NICs de macvlan", "Main menu": "Menú principal", "Maintenance Window": "Ventana de mantenimiento", + "Major": "Importante", "Make Destination Dataset Read-only?": "¿Hacer que el conjunto de datos de destino sea de solo lectura?", "Manage": "Gestionar", "Manage Advanced Settings": "Administrar configuraciones avanzadas", @@ -2948,10 +2889,14 @@ "Memory Stats": "Estadísticas de memoria", "Memory Usage": "Uso de memoria", "Memory Utilization": "Utilización de memoria", + "Memory device": "Dispositivo de memoria", + "Memory usage of app": "Uso de memoria de la app", "Message": "Mensaje", "Message verbosity level in the replication task log.": "Nivel de verbosidad del mensaje en el registro de tareas de replicación.", "Metadata": "Metadados", + "Metadata VDEVs": "VDEVs de metadatos", "Method": "Método", + "Method of snapshot transfer:
  • SSH is supported by most systems. It requires a previously created connection in System > SSH Connections.
  • SSH+NETCAT uses SSH to establish a connection to the destination system, then uses py-libzfs to send an unencrypted data stream for higher transfer speeds. This only works when replicating to a TrueNAS, or other system with py-libzfs installed.
  • LOCAL efficiently replicates snapshots to another dataset on the same system without using the network.
  • LEGACY uses the legacy replication engine from FreeNAS 11.2 and earlier.
": "Método de transferencia de instantáneas:
  • SSH es compatible con la mayoría de los sistemas. Requiere una conexión creada previamente en Sistema > Conexiones SSH.
  • SSH+NETCAT utiliza SSH para establecer una conexión con el sistema de destino y luego utiliza py-libzfs para enviar un flujo de datos sin cifrar para lograr velocidades de transferencia más altas. Esto solo funciona cuando se replica a un TrueNAS u otro sistema con py-libzfs instalado.
  • LOCAL replica de manera eficiente las instantáneas a otro conjunto de datos en el mismo sistema sin usar la red.
  • HEREDADO utiliza el motor de replicación heredado de FreeNAS 11.2 y versiones anteriores.
", "Metrics": "Métricas", "MiB": "MiB", "MiB. Units smaller than MiB are not allowed.": "MiB. Las unidades más pequeñas que MiB no están permitidas.", @@ -3170,6 +3115,7 @@ "No VDEVs added.": "No se agregaron VDEVs.", "No arguments are passed": "No se pasan argumentos", "No available licensed Expansion Shelves ": "No hay estantes de expansión con licencia disponibles", + "No connections": "Sin conexiones", "No containers are available.": "No hay contenedores disponibles.", "No descriptor provided": "No se proporcionó ninguna descripción", "No devices added.": "No hay dispositivos añadidos.", @@ -3324,6 +3270,7 @@ "Outbound Network": "Red saliente", "Outbound Network:": "Red de salida", "Outgoing [{networkInterfaceName}]": "Saliente [{networkInterfaceName}]", + "Outlook": "Outlook", "Override Admin Email": "Anular correo del administrador", "Overrides default directory creation mask of 0777 which grants directory read, write and execute access for everybody.": "Reemplaza la máscara de creación de directorios predeterminada de 0777 que concede acceso de lectura, escritura y ejecución de directorios para todos.", "Overrides default file creation mask of 0666 which creates files with read and write access for everybody.": "Reemplaza la máscara de creación de archivos predeterminada de 0666 que crea archivos con acceso de lectura y escritura para todos.", @@ -3500,11 +3447,15 @@ "Quota for this dataset and all children": "Cuota para este conjunto de datos y todos los secundarios", "Quota size is too small, enter a value of 1 GiB or larger.": "El tamaño de la cuota es demasiado pequeño, ingresá un valor de 1 GiB o mayor.", "Quota warning alert at, %": "Alerta de advertencia de cuota en, %", + "Quotas set for {n, plural, one {# group} other {# groups} }": "Cuotas establecidas para {n, plural, one {# grupo} other {# grupos} }", + "Quotas set for {n, plural, one {# user} other {# users} }": "Cuotas establecidas para {n, plural, one {# usuario} other {# usuarios} }", + "Quotas updated": "Cuotas actualizadas", "RAIDZ1": "RAIDZ1", "RAIDZ2": "RAIDZ2", "RAIDZ3": "RAIDZ3", "RAM": "RAM", "REMOTE": "REMOTO", + "REQUIRE": "REQUIERE", "Randomly generate an encryption key for securing this dataset. Disabling requires manually defining the encryption key.
WARNING: the encryption key is the only means to decrypt the information stored in this dataset. Store the encryption key in a secure location.": "Genere aleatoriamente una clave de cifrado para proteger este conjunto de datos. La desactivación requiere la definición manual de la clave de cifrado. ADVERTENCIA: la clave de cifrado es el único medio para descifrar la información almacenada en este conjunto de datos. Almacene la clave de cifrado en una ubicación segura.", "Range High": "Rango alto", "Range Low": "Rango bajo", @@ -3516,16 +3467,25 @@ "Re-Open": "Reabrir", "Re-Open All Alerts": "Reabrir todas las alertas", "Read": "Leer", + "Read ACL": "Leer ACL", + "Read Attributes": "Leer propiedades", + "Read Data": "Leer datos", "Read Errors": "Errores de lectura", + "Read Named Attributes": "Leer propiedades nombradas", "Read Only": "Solo lectura", "Read-only": "Sólo lectura", "Readonly Admin": "Administrador de solo lectura", "Realm": "Reino", + "Rear": "Trasero", "Reason": "Motivo", "Reboot": "Reiniciar", + "Reboot Local": "Reiniciar local", "Reboot Remote": "Reinicio remoto", "Reboot Required": "Reinicio requerido", + "Reboot of the other node is required for FIPS changes.": "Es necesario reiniciar el otro nodo para realizar cambios de FIPS.", + "Reboot of this node is required for FIPS changes.": "Es necesario reiniciar este nodo para realizar cambios de FIPS.", "Rebuild Directory Service Cache": "Reconstruir caché del servicio de directorio", + "Received: {received}/s Sent: {sent}/s": "Recibido: {received}/s Enviado: {sent}/s", "Recent Searches": "Búsquedas recientes", "Recommended Apps": "Apps recomendadas", "Recommended block size based on pool topology:": "Tamaño de bloque recomendado según la topología del pool:", @@ -3534,6 +3494,8 @@ "Reconnect": "Reconectar", "Record Size": "Tamaño del registro", "Recursive": "Recursivo", + "Redfish administrative password.": "Contraseña administrativa de Redfish.", + "Redfish administrative username.": "Nombre de usuario administrativo de Redfish.", "Redirect HTTP connections to HTTPS. A GUI SSL Certificate is required for HTTPS. Activating this also sets the HTTP Strict Transport Security (HSTS) maximum age to 31536000 seconds (one year). This means that after a browser connects to the web interface for the first time, the browser continues to use HTTPS and renews this setting every year.": "Redireccioar las conexiones HTTP a HTTPS. Se requiere un certificado SSL de GUI para HTTPS. Activar esto también establece la seguridad de transporte estricta HTTP (HSTS) edad máxima a 31536000 segundos (un año). Esto significa que después de que un navegador se conecta a la interfaz web por primera vez, el navegador continúa usando HTTPS y renueva esta configuración cada año.", "Reenter Password": "Reingresá la contraseña", "Referenced": "Referenciado", @@ -3544,6 +3506,9 @@ "Region": "Región", "Region Name": "Nombre de región", "Region name - optional (rclone documentation).": "Nombre de la región: opcional (documentación de rclone) .", + "Register": "Registro", + "Register Default Gateway": "Registrar puerta de enlace predeterminada", + "Regularly scheduled system checks and updates.": "Comprobaciones y actualizaciones del sistema programadas periódicamente.", "Reject": "Rechazar", "Release Notes": "Notas de la versión", "Reload now": "Recargar ahora", @@ -3611,14 +3576,23 @@ "Replication from scratch": "Replicación desde cero", "Replication task created.": "Tarea de replicación creada.", "Replication task saved.": "Tarea de replicación guardada.", + "Replication «{name}» has started.": "Se inició la replicación «{name}».", "Report Bug": "Informar error", "Report a bug": "Informar un error", "Report if drive temperature is at or above this temperature in Celsius. 0 disables the report.": "Informa si la temperatura de la unidad es igual o superior a esta temperatura en grados Celsius. 0 deshabilita el informe.", "Report if the temperature of a drive has changed by this many degrees Celsius since the last report. 0 disables the report.": "Informa si la temperatura de una unidad ha cambiado tantos grados Celsius desde el último informe. 0 deshabilita el informe.", "Reporting": "Informes", + "Reporting Read": "Informe de lectura", + "Reporting Write": "Informe de escritura", "Reports": "Informes", + "Requested action performed for selected Applications": "Acción solicitada realizada para aplicaciones seleccionadas", + "Requested action performed for selected Instances": "Acción solicitada realizada para instancias seleccionadas", + "Require IDENT Authentication": "Requerir autenticación IDENT", "Require Kerberos for NFSv4": "Requerir Kerberos para NFSv4", + "Required reset to fix system operation issues.": "Reinicio necesario para solucionar problemas de funcionamiento del sistema.", + "Required restart after new software installation.": "Es necesario reiniciar después de instalar un nuevo software.", "Required unless Enable password login is No. Passwords cannot contain a ?.": "Se requiere a menos que Habilitar inicio de sesión con contraseña sea No . Las contraseñas no pueden contener un ? .", + "Required – always encrypt transport (rejecting access if client does not support encryption – incompatible with SMB1 server enable_smb1)": "Obligatorio: siempre cifrar el transporte (rechazar el acceso si el cliente no admite el cifrado; incompatible con el servidor SMB1 enable_smb1)", "Reservation": "Reserva", "Reservation (in GiB)": "Reserva (en GiB)", "Reserved for Dataset": "Reservado para el conjunto de datos", @@ -3628,6 +3602,9 @@ "Reset": "Reestablecer", "Reset Config": "Restablecer Config", "Reset Configuration": "Restablecer configuración", + "Reset Default Config": "Restablecer config predeterminada", + "Reset Defaults": "Restablecer valores predeterminados", + "Reset Search": "Restablecer búsqueda", "Reset Step": "Restablecer paso", "Reset Zoom": "Restablecer zoom", "Reset configuration": "Restablecer configuración", @@ -3639,6 +3616,11 @@ "Resetting system configuration to default settings. The system will restart.": "Restablecer la configuración del sistema a la configuración predeterminada. El sistema se va a reiniciar.", "Resetting. Please wait...": "Reiniciando. Por favor esperá...", "Resilver Priority": "Prioridad de resilver", + "Resilver configuration saved": "Configuración de Resilver guardada", + "Resilvering": "Resilverización", + "Resilvering Status": "Estado de la resilverización", + "Resilvering pool: ": "Resilverización del pool", + "Resilvering:": "Resilverización:", "Resolution": "Resolución", "Restart": "Reiniciar", "Restart After Update": "Reiniciar después actualizar", @@ -3679,6 +3661,7 @@ "Resume Scrub": "Continuar limpieza", "Retention": "Retención", "Retention (in days)": "Retención (en días)", + "Retry": "Reintentar", "Return to pool list": "Volver a la lista de pools", "Revert Changes": "Revertir cambios", "Revert Network Interface Changes": "Revertir cambios en la interfaz de red", @@ -3695,6 +3678,7 @@ "Roll back snapshots": "Revertir instantáneas", "Roll back snapshots of ix_volumes": "Revertir instantáneas de ix_volumes", "Rollback": "Retroceder", + "Rolling back...": "Retrocediendo...", "Root dataset ACL cannot be edited.": "El conjunto de datos root ACL no se puede editar.", "Rotation Rate": "Velocidad de rotación", "Rotation Rate (RPM)": "Velocidad de rotación (RPM)", @@ -3764,6 +3748,8 @@ "SMB Shares": "Comparticiones SMB", "SMB Status": "Estado de SMB", "SMB User": "Usuario SMB", + "SMB multichannel allows servers to use multiple network connections simultaneously by combining the bandwidth of several network interface cards (NICs) for better performance. SMB multichannel does not function if you combine NICs into a LAGG. Read more in docs": "El multicanal SMB permite a los servidores utilizar varias conexiones de red simultáneamente combinando el ancho de banda de varias tarjetas de interfaz de red (NIC) para lograr un mejor rendimiento. El multicanal SMB no funciona si se combinan las NIC en un LAGG. Más información en la documentación", + "SMB preset sets most optimal settings for SMB sharing.": "Los ajustes preestablecidos de SMB establecen las configuraciones más óptimas para compartir SMB.", "SMB/NFSv4": "SMB/NFSv4", "SMTP": "SMTP", "SMTP Authentication": "Autenticación SMTP", @@ -4040,6 +4026,7 @@ "Set Quota": "Establecer cuota", "Set Quotas": "Establecer cuotas", "Set Warning Level": "Establecer nivel de advertencia", + "Set an expiration date-time for the API key.": "Establecer una fecha y hora de vencimiento para la clave de API.", "Set email": "Establecer correo", "Set enable sending messages to the address defined in the Email field.": "Configure habilitar el envío de mensajes a la dirección definida en el campo Email.", "Set font size": "Establecer tamaño de fuente", @@ -4472,22 +4459,35 @@ "Tests the server connection and verifies the chosen Certificate chain. To test, configure the Server and Port values, select a Certificate and Certificate Authority, enable this setting, and click SAVE.": "Prueba la conexión del servidor y verifica la cadena Certificado elegida. Para probar, configurá los valores de Servidor y Puerto, seleccioná un Certificado y Autoridad de certificación, habilitá esta configuración y hacé clic en GUARDAR.", "The TrueNAS Community Forums are the best place to ask questions and interact with fellow TrueNAS users.": "Los foros de la comunidad TrueNAS son el mejor lugar para hacer preguntas e interactuar con otros usuarios de TrueNAS.", "The TrueNAS Documentation Site is a collaborative website with helpful guides and information about your new storage system.": "El sitio de documentación de TrueNAS es un sitio web colaborativo con guías útiles e información sobre tu nuevo sistema de almacenamiento.", + "The {name} dataset and all snapshots stored with it will be permanently deleted.": "El conjunto de datos {name} y todas las instantáneas almacenadas con él se van a eliminar permanentemente.", + "The {name} zvol and all snapshots stored with it will be permanently deleted.": "El zvol {name} y todas las instantáneas almacenadas con él se van a eliminar permanentemente.", "The Alias field can either be left empty or have an alias defined for each path in the share.": "El campo Alias puede dejarse vacío o tener un alias definido para cada ruta en el recurso compartido.", "The Use Apple-style character encoding value has changed. This parameter affects how file names are read from and written to storage. Changes to this parameter after data is written can prevent accessing or deleting files containing mangled characters.": "El valor de Usar codificación de caracteres estilo Apple ha cambiado. Este parámetro afecta cómo se leen y escriben los nombres de archivo en el almacenamiento. Los cambios a este parámetro después de que se escriben los datos pueden evitar el acceso o la eliminación de archivos que contienen caracteres alterados.", "The Group ID (GID) is a unique number used to identify a Unix group. Enter a number above 1000 for a group with user accounts. Groups used by a service must have an ID that matches the default port number used by the service.": "La ID de grupo (GID) es un número único utilizado para identificar un grupo Unix. Ingresá un número superior a 1000 para un grupo con cuentas de usuario. Los grupos utilizados por un servicio deben tener una ID que coincida con el número de puerto predeterminado utilizado por el servicio.", "The Idmap cache should be cleared after finalizing idmap changes. Click \"Continue\" to clear the cache.": "El caché de Idmap debe borrarse después de finalizar los cambios de idmap. Hacé clic en \"Continuar\" para borrar el caché.", "The OU in which new computer accounts are created. The OU string is read from top to bottom without RDNs. Slashes (\"/\") are used as delimiters, like Computers/Servers/NAS. The backslash (\"\\\") is used to escape characters but not as a separator. Backslashes are interpreted at multiple levels and might require doubling or even quadrupling to take effect. When this field is blank, new computer accounts are created in the Active Directory default OU.": "La unidad organizativa en la que se crean nuevas cuentas de computadora. La cadena OU se lee de arriba a abajo sin RDN. Las barras inclinadas (\"/\") se usan como delimitadores, como Computers / Servers / NAS . La barra invertida (\"\\\\\") se usa para escapar caracteres pero no como separador. Las barras invertidas se interpretan en múltiples niveles y pueden requerir duplicarse o incluso cuadruplicarse para que surtan efecto. Cuando este campo está en blanco, se crean nuevas cuentas de computadora en la unidad organizativa predeterminada de Active Directory.", "The SMB service has been restarted.": "El servicio SMB fue iniciado.", + "The SMB share ACL defines access rights for users of this SMB share up to, but not beyond, the access granted by filesystem ACLs.": "Las ACL del recurso compartido SMB define los derechos de acceso para los usuarios de este recurso compartido SMB hasta, pero no más allá, del acceso otorgado por las ACL del sistema de archivos.", "The SSL certificate to be used for TLS FTP connections. To create a certificate, use System --> Certificates.": "El certificado SSL que se utilizará para las conexiones FTP de TLS. Para crear un certificado, use Sistema -> Certificados .", + "The TrueNAS controllers do not have the same quantity of disks.": "Los controladores TrueNAS no tienen la misma cantidad de discos.", "The URI used to provision an OTP. The URI (which contains the secret) is encoded in a QR Code. To set up an OTP app like Google Authenticator, use the app to scan the QR code or enter the secret manually into the app. The URI is produced by the system when Two-Factor Authentication is first activated.": "El URI solía aprovisionar una OTP. El URI (que contiene el secreto) está codificado en un código QR. Para configurar una aplicación OTP como Google Authenticator, use la aplicación para escanear el código QR o ingresá el secreto manualmente en la aplicación. El sistema produce el URI cuando la autenticación de dos factores se activa por primera vez.", + "The VM could not start because the current configuration could potentially require more RAM than is available on the system. Memory overcommitment allows multiple VMs to be launched when there is not enough free memory for configured RAM of all VMs. Use with caution. Would you like to overcommit memory?": "La VM no pudo iniciarse porque la configuración actual podría requerir más RAM de la que está disponible en el sistema. La sobreasignación de memoria permite que se inicien varias VMs cuando no hay suficiente memoria libre para la RAM configurada de todas las VMs. Usá con precaución. ¿Deseás sobreasignar memoria?", "The base name is automatically prepended if the target name does not start with iqn. Lowercase alphanumeric characters plus dot (.), dash (-), and colon (:) are allowed. See the Constructing iSCSI names using the iqn.format section of RFC3721.": "El nombre base se antepone automáticamente si el nombre del objetivo no comienza con iqn . Se permiten caracteres alfanuméricos en minúscula más punto (.), Guión (-) y dos puntos (:). Vea la Construcción de nombres iSCSI utilizando la sección iqn.format de RFC3721 .", "The cache has been cleared.": "La caché ya se borró.", "The cache is being rebuilt.": "La caché se está reconstruyendo.", "The certificate's public key is used to encipher user data only during key agreement operations. Requires that Key Agreement is also set.": "La clave pública del certificado se utiliza para cifrar datos de usuario solo durante las operaciones de acuerdo de claves. Requiere que Acuerdo de clave también esté establecido.", + "The chosen preset ACL will REPLACE the ACL currently displayed in the form and delete any unsaved changes.": "La ACL preestablecida elegida va a REEMPLAZAR la ACL que se muestra actualmente en el formulario y va a eliminar cualquier cambio no guardado.", "The contents of all added disks will be erased.": "Se va a borrar el contenido de todos los discos agregados.", + "The credentials are valid.": "Las credenciales son válidas.", "The cryptographic algorithm to use. The default SHA256 only needs to be changed if the organization requires a different algorithm.": "El algoritmo criptográfico a usar. El SHA256 predeterminado solo necesita cambiarse si la organización requiere un algoritmo diferente.", + "The current pool layout is not recommended. Override the following errors?": "El diseño actual de pool no es el recomendado. ¿Deseás anular los siguientes errores?", + "The default \"Checksum\" value for datasets with deduplication used to be SHA256.\n Our testing has shown that SHA512 performs better for such datasets.\n We've changed the checksum value from SHA256 to SHA512. You can change it back in \"Advanced Options\".": "El valor predeterminado de \"Suma de comprobación\" para los conjuntos de datos con deduplicación solía ser SHA256.\n Nuestras pruebas demostraron que SHA512 funciona mejor para dichos conjuntos de datos.\n Canbiamos el valor de suma de comprobación de SHA256 a SHA512. Podés volver a cambiarlo en \"Opciones avanzadas\".", + "The directory base suffix to use for SID/uid/gid mapping entries. Example: dc=test,dc=org. When undefined, idmap_ldap defaults to using the ldap idmap suffix option from smb.conf.": "El sufijo base del directorio que se va a usar para las entradas de mapeo de SID/uid/gid. Ejemplo: dc=test,dc=org. Cuando no está definido, idmap_ldap utiliza de manera predeterminada la opción de sufijo idmap de ldap de smb.conf.", + "The domain for local users is the NetBIOS name of the TrueNAS server.": "El dominio para los usuarios locales es el nombre NetBIOS del servidor TrueNAS.", "The domain to access the Active Directory server when using the LDAP server inside the Active Directory server.": "El dominio para acceder al servidor de Active Directory cuando se utiliza el servidor LDAP dentro del servidor de Active Directory.", + "The expanded vdev uses the pre-expanded parity ratio, which reduces the total vdev capacity. To reset the vdev parity ratio and fully use the new capacity, manually rewrite all data in the vdev. This process takes time and is irreversible.": "El vdev ampliado utiliza la proporción de paridad preexpandida, lo que reduce la capacidad total del vdev. Para restablecer la relación de paridad del vdev y utilizar por completo la nueva capacidad, reescribí manualmente todos los datos en el vdev. Este proceso lleva tiempo y es irreversible.", "The file used to manually update the system. Browse to the update file stored on the system logged into the web interface to upload and apply. Update file names end with -manual-update-unsigned.tar": "El archivo utilizado para actualizar manualmente el sistema. Buscá el archivo de actualización almacenado en el sistema conectado a la interfaz web para cargar y aplicar. Los nombres de los archivos de actualización terminan con -manual-update-unsigned.tar", + "The filesystem {filesystemName} is {filesystemDescription}, but datastore {datastoreName} is {datastoreDescription}. Is this correct?": "El sistema de archivos {filesystemName} es {filesystemDescription}, pero el almacén de datos {datastoreName} es {datastoreDescription}. ¿Es correcto?", "The following changes to this SMB Share require the SMB Service to be restarted before they can take effect.": "Los siguientes cambios en este recurso compartido SMB requieren que se reinicie el servicio SMB antes de que puedan tener efecto.", "The following datasets cannot be unlocked.": "Los siguientes conjuntos de datos no se pueden desbloquear.", "The following disks have exported pools on them.\n Using those disks will make existing pools on them unable to be imported.\n You will lose any and all data in selected disks.": "Los siguientes discos tienen pools exportados en ellos.\n El uso de esos discos va a hacer que los pools existentes en ellos no se puedan importar.\n Vas a perder todos los datos en los discos seleccionados.", @@ -5315,4 +5315,4 @@ "{used} of {total} ({used_pct})": "{used} de {total} ({used_pct})", "{version} is available!": "¡{version} ya está disponible!", "{view} on {enclosure}": "{view} en {enclosure}" -} +} \ No newline at end of file From 58f308a77b9e825446ffa90a2104c908856dd09b Mon Sep 17 00:00:00 2001 From: Alex Karpov Date: Sat, 4 Jan 2025 11:58:55 +0200 Subject: [PATCH 05/18] NAS-131275 / 25.04 / UI Search behavior: side menu does not close when search autonavigates to a different screen (#11257) * NAS-131275: UI Search behavior: side menu does not close when search autonavigates to a different screen * NAS-131275: UI Search behavior: side menu does not close when search autonavigates to a different screen * NAS-131275: PR Update --- .../ui-search/extract-ui-search-elements.ts | 4 + scripts/ui-search/parse-ui-search-elements.ts | 6 +- .../global-search-results.component.html | 2 +- .../global-search-results.component.spec.ts | 1 + .../global-search-results.component.ts | 2 - .../ui-searchable-element.interface.ts | 16 +- .../available-apps/available-apps.elements.ts | 1 + .../docker-images-list.elements.ts | 4 +- .../installed-apps/installed-apps.elements.ts | 1 + src/app/pages/audit/audit.elements.ts | 1 + .../backup-credentials.elements.ts | 4 +- .../certificates-dash.elements.ts | 4 +- .../groups/group-list/group-list.elements.ts | 1 + .../privilege-list/privilege-list.elements.ts | 4 +- .../pages/credentials/kmip/kmip.elements.ts | 4 +- .../users/user-list/user-list.elements.ts | 1 + .../dashboard/dashboard.elements.ts | 1 + .../cloud-backup-list.elements.ts | 1 + .../cloudsync-list/cloudsync-list.elements.ts | 1 + .../data-protection-dashboard.elements.ts | 1 + .../replication-list.elements.ts | 1 + .../rsync-task-list.elements.ts | 1 + .../resilver-config.elements.ts | 4 +- .../scrub-list/scrub-list.elements.ts | 1 + .../smart-task-list.elements.ts | 1 + .../snapshot-task-list.elements.ts | 1 + .../vmware-snapshot-list.elements.ts | 4 +- .../dataset-management.elements.ts | 4 +- .../idmap-list/idmap-list.elements.ts | 4 +- .../kerberos-keytabs-list.elements.ts | 4 +- .../kerberos-realms-list.elements.ts | 4 +- .../directory-services.elements.ts | 4 +- src/app/pages/jobs/jobs-list.elements.ts | 4 +- src/app/pages/network/network.elements.ts | 4 +- .../reporting-exporters-list.elements.ts | 4 +- .../reports-dashboard.elements.ts | 4 +- src/app/pages/services/services.elements.ts | 4 +- .../shares-dashboard.elements.ts | 1 + .../authorized-access-list.elements.ts | 4 +- .../extent-list/extent-list.elements.ts | 4 +- .../fibre-channel-ports.elements.ts | 4 +- .../initiator-form/initiator-form.elements.ts | 1 + .../initiator-list/initiator-list.elements.ts | 4 +- .../portal-list/portal-list.elements.ts | 4 +- .../target-list/target-list.elements.ts | 4 +- .../sharing/nfs/nfs-list/nfs-list.elements.ts | 4 +- .../nfs-session-list.elements.ts | 4 +- .../sharing/smb/smb-list/smb-list.elements.ts | 4 +- src/app/pages/shell/shell.elements.ts | 4 +- .../disk-list/disk-list.elements.ts | 1 + .../pages/storage/pools-dashboard.elements.ts | 1 + .../advanced/advanced-settings.elements.ts | 1 + .../cron/cron-list/cron-list.elements.ts | 4 +- .../init-shutdown-list.elements.ts | 4 +- .../tunable-list/tunable-list.elements.ts | 4 +- .../alert-service-list.elements.ts | 4 +- .../bootenv-list/bootenv-list.elements.ts | 4 +- .../bootenv-status/bootenv-status.elements.ts | 4 +- .../jbof-list/jbof-list.elements.ts | 4 +- .../failover-settings.elements.ts | 4 +- .../general-settings.elements.ts | 1 + .../support/eula/eula.elements.ts | 4 +- .../support-card/support-card.elements.ts | 4 +- .../manual-update-form.elements.ts | 4 +- .../two-factor-auth/two-factor.elements.ts | 4 +- src/app/pages/vm/vm-list.elements.ts | 1 + src/assets/i18n/ko.json | 186 +++++++++--------- src/assets/ui-searchable-elements.json | 114 +++++------ 68 files changed, 311 insertions(+), 197 deletions(-) diff --git a/scripts/ui-search/extract-ui-search-elements.ts b/scripts/ui-search/extract-ui-search-elements.ts index 226a766dc75..630ac066993 100644 --- a/scripts/ui-search/extract-ui-search-elements.ts +++ b/scripts/ui-search/extract-ui-search-elements.ts @@ -8,6 +8,10 @@ * 2️⃣. Create .elements.ts config file near the .component.html file ~ [pools-dashboard.elements.ts] * * Example of creating a new searchable element: + * + * !! It's required to add anchor to the element where you do not specify hierarchy explicitly !! + * You will get TS error if it's not provided correctly + * export const customSearchableElements = { hierarchy: [T('System'), T('Advanced Settings'), T('Access')], diff --git a/scripts/ui-search/parse-ui-search-elements.ts b/scripts/ui-search/parse-ui-search-elements.ts index 1ad6bf73c65..399761a6e9f 100644 --- a/scripts/ui-search/parse-ui-search-elements.ts +++ b/scripts/ui-search/parse-ui-search-elements.ts @@ -61,14 +61,16 @@ export function parseUiSearchElements( function createUiSearchElement( cheerioRoot$: (selector: CheerioElement | string) => { attr: (attr: string) => string }, element: CheerioElement, - elementConfig: UiSearchableElement, + elementConfig: Record, parentKey: keyof UiSearchableElement, childKey: keyof UiSearchableElement, componentProperties: Record, ): UiSearchableElement { try { const parent = (elementConfig?.[parentKey] || elementConfig) as UiSearchableElement; - const child = parent?.elements?.[childKey] || parent?.manualRenderElements?.[childKey] || {}; + const child = parent?.elements?.[childKey] + || parent?.manualRenderElements?.[childKey] + || {} as UiSearchableElement; const hierarchy = [...parent?.hierarchy || [], ...child?.hierarchy || []]; const visibleTokens = [...parent?.visibleTokens || [], ...child?.visibleTokens || []]; diff --git a/src/app/modules/global-search/components/global-search-results/global-search-results.component.html b/src/app/modules/global-search/components/global-search-results/global-search-results.component.html index 7459c816b5e..3b38bde1bc4 100644 --- a/src/app/modules/global-search/components/global-search-results/global-search-results.component.html +++ b/src/app/modules/global-search/components/global-search-results/global-search-results.component.html @@ -7,7 +7,7 @@

@if (getElementsBySection(section.value)?.length) {
@if (getLimitedSectionResults(section.value); as sectionResults) { - @for (result of sectionResults; track trackById(i, result); let i = $index) { + @for (result of sectionResults; track result.anchor; let i = $index) {
{ const mockResults: UiSearchableElement[] = [ ...['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'].map((adjustment) => ({ ...mockedUiElement, + anchor: adjustment, hierarchy: [...mockedUiElement.hierarchy, adjustment], })), ]; diff --git a/src/app/modules/global-search/components/global-search-results/global-search-results.component.ts b/src/app/modules/global-search/components/global-search-results/global-search-results.component.ts index d278c02a69b..348a562e5d4 100644 --- a/src/app/modules/global-search/components/global-search-results/global-search-results.component.ts +++ b/src/app/modules/global-search/components/global-search-results/global-search-results.component.ts @@ -12,7 +12,6 @@ import { Option } from 'app/interfaces/option.interface'; import { IxSimpleChanges } from 'app/interfaces/simple-changes.interface'; import { AuthService } from 'app/modules/auth/auth.service'; import { GlobalSearchSection } from 'app/modules/global-search/enums/global-search-section.enum'; -import { generateIdFromHierarchy } from 'app/modules/global-search/helpers/generate-id-from-hierarchy'; import { processHierarchy } from 'app/modules/global-search/helpers/process-hierarchy'; import { UiSearchableElement } from 'app/modules/global-search/interfaces/ui-searchable-element.interface'; import { GlobalSearchSectionsProvider } from 'app/modules/global-search/services/global-search-sections.service'; @@ -46,7 +45,6 @@ export class GlobalSearchResultsComponent implements OnChanges { readonly GlobalSearchSection = GlobalSearchSection; readonly initialResultsLimit = this.globalSearchSectionsProvider.globalSearchInitialLimit; readonly trackBySection: TrackByFunction> = (_, section) => section.value; - readonly trackById: TrackByFunction = (_, item) => generateIdFromHierarchy(item.hierarchy); processHierarchy = processHierarchy; diff --git a/src/app/modules/global-search/interfaces/ui-searchable-element.interface.ts b/src/app/modules/global-search/interfaces/ui-searchable-element.interface.ts index 2ecdd7a8411..d398b15c611 100644 --- a/src/app/modules/global-search/interfaces/ui-searchable-element.interface.ts +++ b/src/app/modules/global-search/interfaces/ui-searchable-element.interface.ts @@ -2,11 +2,9 @@ import { Role } from 'app/enums/role.enum'; import { GlobalSearchSection } from 'app/modules/global-search/enums/global-search-section.enum'; import { GlobalSearchVisibleToken } from 'app/modules/global-search/enums/global-search-visible-token.enum'; -export interface UiSearchableElement { - hierarchy?: string[]; +interface UiSearchableElementBase { section?: GlobalSearchSection; anchorRouterLink?: string[]; - anchor?: string; triggerAnchor?: string; synonyms?: string[]; requiredRoles?: Role[]; @@ -16,3 +14,15 @@ export interface UiSearchableElement { manualRenderElements?: Record; visibleTokens?: GlobalSearchVisibleToken[]; } + +export type UiSearchableElementWithHierarchy = UiSearchableElementBase & { + hierarchy: string[]; + anchor?: string; +}; + +export type UiSearchableElementWithAnchor = UiSearchableElementBase & { + hierarchy?: never; + anchor: string; +}; + +export type UiSearchableElement = UiSearchableElementWithHierarchy | UiSearchableElementWithAnchor; diff --git a/src/app/pages/apps/components/available-apps/available-apps.elements.ts b/src/app/pages/apps/components/available-apps/available-apps.elements.ts index 1467759ecad..9a314fd3496 100644 --- a/src/app/pages/apps/components/available-apps/available-apps.elements.ts +++ b/src/app/pages/apps/components/available-apps/available-apps.elements.ts @@ -7,6 +7,7 @@ export const availableAppsElements = { anchorRouterLink: ['/apps', 'available'], elements: { available: { + anchor: 'available-apps-list', synonyms: [T('Apps'), T('Applications')], }, }, diff --git a/src/app/pages/apps/components/docker-images/docker-images-list/docker-images-list.elements.ts b/src/app/pages/apps/components/docker-images/docker-images-list/docker-images-list.elements.ts index ba6189e99aa..5e78e0985b4 100644 --- a/src/app/pages/apps/components/docker-images/docker-images-list/docker-images-list.elements.ts +++ b/src/app/pages/apps/components/docker-images/docker-images-list/docker-images-list.elements.ts @@ -5,7 +5,9 @@ export const dockerImagesListElements = { hierarchy: [T('Applications'), T('Manage Container Images')], anchorRouterLink: ['/apps', 'manage-container-images'], elements: { - dockerImagesList: {}, + dockerImagesList: { + anchor: 'docker-images-list', + }, pullImage: { hierarchy: [T('Pull Image')], synonyms: [T('Add Image')], diff --git a/src/app/pages/apps/components/installed-apps/installed-apps.elements.ts b/src/app/pages/apps/components/installed-apps/installed-apps.elements.ts index 92194f65037..2db96a53d1c 100644 --- a/src/app/pages/apps/components/installed-apps/installed-apps.elements.ts +++ b/src/app/pages/apps/components/installed-apps/installed-apps.elements.ts @@ -7,6 +7,7 @@ export const installedAppsElements = { anchorRouterLink: ['/apps', 'installed'], elements: { installed: { + anchor: 'installed-apps-list', synonyms: [T('Apps'), T('Applications')], }, }, diff --git a/src/app/pages/audit/audit.elements.ts b/src/app/pages/audit/audit.elements.ts index e45a10af2f4..0edd064f6dd 100644 --- a/src/app/pages/audit/audit.elements.ts +++ b/src/app/pages/audit/audit.elements.ts @@ -6,6 +6,7 @@ export const auditElements = { anchorRouterLink: ['/system', 'audit'], elements: { audit: { + anchor: 'audit-list', synonyms: [T('Logs')], }, }, diff --git a/src/app/pages/credentials/backup-credentials/backup-credentials.elements.ts b/src/app/pages/credentials/backup-credentials/backup-credentials.elements.ts index 3bf5b1ea62e..6b2acd99b92 100644 --- a/src/app/pages/credentials/backup-credentials/backup-credentials.elements.ts +++ b/src/app/pages/credentials/backup-credentials/backup-credentials.elements.ts @@ -5,6 +5,8 @@ export const backupCredentialsElements = { hierarchy: [T('Credentials'), T('Backup Credentials')], anchorRouterLink: ['/credentials', 'backup-credentials'], elements: { - backupCredentials: {}, + backupCredentials: { + anchor: 'backup-credentials', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/credentials/certificates-dash/certificates-dash.elements.ts b/src/app/pages/credentials/certificates-dash/certificates-dash.elements.ts index e519ace3c6f..3f23b5411cb 100644 --- a/src/app/pages/credentials/certificates-dash/certificates-dash.elements.ts +++ b/src/app/pages/credentials/certificates-dash/certificates-dash.elements.ts @@ -5,6 +5,8 @@ export const certificatesDashElements = { hierarchy: [T('Credentials'), T('Certificates')], anchorRouterLink: ['/credentials', 'certificates'], elements: { - certificatesDash: {}, + certificatesDash: { + anchor: 'certificates-dash', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/credentials/groups/group-list/group-list.elements.ts b/src/app/pages/credentials/groups/group-list/group-list.elements.ts index 5786c8ccb5e..bc2118e5ab4 100644 --- a/src/app/pages/credentials/groups/group-list/group-list.elements.ts +++ b/src/app/pages/credentials/groups/group-list/group-list.elements.ts @@ -6,6 +6,7 @@ export const groupListElements = { anchorRouterLink: ['/credentials', 'groups'], elements: { list: { + anchor: 'groups-list', synonyms: [T('Local Groups')], }, add: { diff --git a/src/app/pages/credentials/groups/privilege/privilege-list/privilege-list.elements.ts b/src/app/pages/credentials/groups/privilege/privilege-list/privilege-list.elements.ts index 420b2e95c68..78d7ba70357 100644 --- a/src/app/pages/credentials/groups/privilege/privilege-list/privilege-list.elements.ts +++ b/src/app/pages/credentials/groups/privilege/privilege-list/privilege-list.elements.ts @@ -5,7 +5,9 @@ export const privilegesListElements = { hierarchy: [T('Credentials'), T('Groups'), T('Privileges')], anchorRouterLink: ['/credentials', 'groups', 'privileges'], elements: { - list: {}, + list: { + anchor: 'privileges-list', + }, add: { hierarchy: [T('Add Privilege')], synonyms: [T('Add Privilege'), T('New Privilege'), T('Create Privilege'), T('Privilege')], diff --git a/src/app/pages/credentials/kmip/kmip.elements.ts b/src/app/pages/credentials/kmip/kmip.elements.ts index e76c2906f82..1624651154d 100644 --- a/src/app/pages/credentials/kmip/kmip.elements.ts +++ b/src/app/pages/credentials/kmip/kmip.elements.ts @@ -5,6 +5,8 @@ export const kmipElements = { hierarchy: [T('Credentials'), T('KMIP')], anchorRouterLink: ['/credentials', 'kmip'], elements: { - kmip: {}, + kmip: { + anchor: 'kmip', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/credentials/users/user-list/user-list.elements.ts b/src/app/pages/credentials/users/user-list/user-list.elements.ts index dc14ea485fb..f6f50c43ae1 100644 --- a/src/app/pages/credentials/users/user-list/user-list.elements.ts +++ b/src/app/pages/credentials/users/user-list/user-list.elements.ts @@ -6,6 +6,7 @@ export const userListElements = { anchorRouterLink: ['/credentials', 'users'], elements: { list: { + anchor: 'users-list', synonyms: [ T('Local Users'), T('Users'), diff --git a/src/app/pages/dashboard/components/dashboard/dashboard.elements.ts b/src/app/pages/dashboard/components/dashboard/dashboard.elements.ts index de0e3e1ccf2..340c14e9a3a 100644 --- a/src/app/pages/dashboard/components/dashboard/dashboard.elements.ts +++ b/src/app/pages/dashboard/components/dashboard/dashboard.elements.ts @@ -6,6 +6,7 @@ export const dashboardElements = { anchorRouterLink: ['/dashboard'], elements: { dashboard: { + anchor: 'main-dashboard', synonyms: [T('Widgets')], }, configure: { diff --git a/src/app/pages/data-protection/cloud-backup/cloud-backup-list/cloud-backup-list.elements.ts b/src/app/pages/data-protection/cloud-backup/cloud-backup-list/cloud-backup-list.elements.ts index 128abf3e0a8..fc24ce65cdd 100644 --- a/src/app/pages/data-protection/cloud-backup/cloud-backup-list/cloud-backup-list.elements.ts +++ b/src/app/pages/data-protection/cloud-backup/cloud-backup-list/cloud-backup-list.elements.ts @@ -6,6 +6,7 @@ export const cloudBackupListElements = { anchorRouterLink: ['/data-protection', 'cloud-backup'], elements: { tasks: { + anchor: 'cloud-backup-tasks', synonyms: [T('Data Protection'), T('Tasks'), T('Cloud Backup')], }, add: { diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-list/cloudsync-list.elements.ts b/src/app/pages/data-protection/cloudsync/cloudsync-list/cloudsync-list.elements.ts index eca3dda15b9..a8f0800ac52 100644 --- a/src/app/pages/data-protection/cloudsync/cloudsync-list/cloudsync-list.elements.ts +++ b/src/app/pages/data-protection/cloudsync/cloudsync-list/cloudsync-list.elements.ts @@ -6,6 +6,7 @@ export const cloudSyncListElements = { anchorRouterLink: ['/data-protection', 'cloudsync'], elements: { tasks: { + anchor: 'cloudsync-tasks', synonyms: [ T('Data Protection'), T('Tasks'), diff --git a/src/app/pages/data-protection/data-protection-dashboard.elements.ts b/src/app/pages/data-protection/data-protection-dashboard.elements.ts index 3a5828ad12d..a1f9a4c5023 100644 --- a/src/app/pages/data-protection/data-protection-dashboard.elements.ts +++ b/src/app/pages/data-protection/data-protection-dashboard.elements.ts @@ -6,6 +6,7 @@ export const dataProtectionDashboardElements = { anchorRouterLink: ['/data-protection'], elements: { dashboard: { + anchor: 'data-protection-dashboard', synonyms: [T('Tasks')], }, }, diff --git a/src/app/pages/data-protection/replication/replication-list/replication-list.elements.ts b/src/app/pages/data-protection/replication/replication-list/replication-list.elements.ts index 7d91310d51c..fe61644a1ca 100644 --- a/src/app/pages/data-protection/replication/replication-list/replication-list.elements.ts +++ b/src/app/pages/data-protection/replication/replication-list/replication-list.elements.ts @@ -6,6 +6,7 @@ export const replicationListElements = { anchorRouterLink: ['/data-protection', 'replication'], elements: { tasks: { + anchor: 'replication-tasks', synonyms: [T('Data Protection'), T('Tasks')], }, add: { diff --git a/src/app/pages/data-protection/rsync-task/rsync-task-list/rsync-task-list.elements.ts b/src/app/pages/data-protection/rsync-task/rsync-task-list/rsync-task-list.elements.ts index 33e52becfeb..42346e26110 100644 --- a/src/app/pages/data-protection/rsync-task/rsync-task-list/rsync-task-list.elements.ts +++ b/src/app/pages/data-protection/rsync-task/rsync-task-list/rsync-task-list.elements.ts @@ -6,6 +6,7 @@ export const rsyncTaskListElements = { anchorRouterLink: ['/data-protection', 'rsync'], elements: { tasks: { + anchor: 'rsync-tasks', synonyms: [T('Data Protection'), T('Tasks')], }, add: { diff --git a/src/app/pages/data-protection/scrub-task/resilver-config/resilver-config.elements.ts b/src/app/pages/data-protection/scrub-task/resilver-config/resilver-config.elements.ts index e9bfb805dfa..64ccdabea9e 100644 --- a/src/app/pages/data-protection/scrub-task/resilver-config/resilver-config.elements.ts +++ b/src/app/pages/data-protection/scrub-task/resilver-config/resilver-config.elements.ts @@ -6,6 +6,8 @@ export const resilverConfigElements = { synonyms: [T('Data Protection'), T('Scrub Tasks')], anchorRouterLink: ['/data-protection', 'scrub', 'priority'], elements: { - priority: {}, + priority: { + anchor: 'scrub-priority', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/data-protection/scrub-task/scrub-list/scrub-list.elements.ts b/src/app/pages/data-protection/scrub-task/scrub-list/scrub-list.elements.ts index 5a89946bd88..66725b2611e 100644 --- a/src/app/pages/data-protection/scrub-task/scrub-list/scrub-list.elements.ts +++ b/src/app/pages/data-protection/scrub-task/scrub-list/scrub-list.elements.ts @@ -6,6 +6,7 @@ export const scrubListElements = { anchorRouterLink: ['/data-protection', 'scrub'], elements: { tasks: { + anchor: 'scrub-tasks', synonyms: [T('Data Protection'), T('Tasks')], }, add: { diff --git a/src/app/pages/data-protection/smart-task/smart-task-list/smart-task-list.elements.ts b/src/app/pages/data-protection/smart-task/smart-task-list/smart-task-list.elements.ts index 31ad7cc61b3..d0047245126 100644 --- a/src/app/pages/data-protection/smart-task/smart-task-list/smart-task-list.elements.ts +++ b/src/app/pages/data-protection/smart-task/smart-task-list/smart-task-list.elements.ts @@ -6,6 +6,7 @@ export const smartTaskListElements = { anchorRouterLink: ['/data-protection', 'smart'], elements: { tasks: { + anchor: 'smart-tasks', synonyms: [ T('Data Protection'), T('Tasks'), diff --git a/src/app/pages/data-protection/snapshot-task/snapshot-task-list/snapshot-task-list.elements.ts b/src/app/pages/data-protection/snapshot-task/snapshot-task-list/snapshot-task-list.elements.ts index c9773fc51c2..d6dac7b2009 100644 --- a/src/app/pages/data-protection/snapshot-task/snapshot-task-list/snapshot-task-list.elements.ts +++ b/src/app/pages/data-protection/snapshot-task/snapshot-task-list/snapshot-task-list.elements.ts @@ -6,6 +6,7 @@ export const snapshotTaskListElements = { anchorRouterLink: ['/data-protection', 'snapshot'], elements: { tasks: { + anchor: 'snapshot-tasks', synonyms: [T('Data Protection'), T('Tasks')], }, add: { diff --git a/src/app/pages/data-protection/vmware-snapshot/vmware-snapshot-list/vmware-snapshot-list.elements.ts b/src/app/pages/data-protection/vmware-snapshot/vmware-snapshot-list/vmware-snapshot-list.elements.ts index 9c85d499b52..2d90371b92c 100644 --- a/src/app/pages/data-protection/vmware-snapshot/vmware-snapshot-list/vmware-snapshot-list.elements.ts +++ b/src/app/pages/data-protection/vmware-snapshot/vmware-snapshot-list/vmware-snapshot-list.elements.ts @@ -5,6 +5,8 @@ export const vmwareSnapshotListElements = { hierarchy: [T('Data Protection'), T('VMware Snapshots')], anchorRouterLink: ['/data-protection', 'vmware-snapshots'], elements: { - vmwareSnapshots: {}, + vmwareSnapshots: { + anchor: 'vmware-snapshots', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/datasets/components/dataset-management/dataset-management.elements.ts b/src/app/pages/datasets/components/dataset-management/dataset-management.elements.ts index 4b475bff41b..fd2fa98ccff 100644 --- a/src/app/pages/datasets/components/dataset-management/dataset-management.elements.ts +++ b/src/app/pages/datasets/components/dataset-management/dataset-management.elements.ts @@ -6,6 +6,8 @@ export const datasetManagementElements = { anchorRouterLink: ['/datasets'], synonyms: [T('Manage Datasets')], elements: { - datasets: {}, + datasets: { + anchor: 'datasets', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/directory-service/components/idmap-list/idmap-list.elements.ts b/src/app/pages/directory-service/components/idmap-list/idmap-list.elements.ts index 6da67e60c6d..99f3c50d8cd 100644 --- a/src/app/pages/directory-service/components/idmap-list/idmap-list.elements.ts +++ b/src/app/pages/directory-service/components/idmap-list/idmap-list.elements.ts @@ -5,7 +5,9 @@ export const idMapElements = { hierarchy: [T('Directory Services'), T('Idmap')], anchorRouterLink: ['/directoryservice', 'idmap'], elements: { - idMap: {}, + idMap: { + anchor: 'idmap', + }, add: { hierarchy: [T('Add Idmap')], anchor: 'add-idmap', diff --git a/src/app/pages/directory-service/components/kerberos-keytabs/kerberos-keytabs-list/kerberos-keytabs-list.elements.ts b/src/app/pages/directory-service/components/kerberos-keytabs/kerberos-keytabs-list/kerberos-keytabs-list.elements.ts index c06a04ac64a..d036ea8fc57 100644 --- a/src/app/pages/directory-service/components/kerberos-keytabs/kerberos-keytabs-list/kerberos-keytabs-list.elements.ts +++ b/src/app/pages/directory-service/components/kerberos-keytabs/kerberos-keytabs-list/kerberos-keytabs-list.elements.ts @@ -5,7 +5,9 @@ export const kerberosKeytabsListElements = { hierarchy: [T('Directory Services'), T('Kerberos Keytabs')], anchorRouterLink: ['/directoryservice', 'kerberoskeytabs'], elements: { - kerberosKeytabs: {}, + kerberosKeytabs: { + anchor: 'kerberoskeytabs', + }, add: { hierarchy: [T('Add Kerberos Keytab')], anchor: 'add-kerberos-keytab', diff --git a/src/app/pages/directory-service/components/kerberos-realms/kerberos-realms-list.elements.ts b/src/app/pages/directory-service/components/kerberos-realms/kerberos-realms-list.elements.ts index 150a84638bf..a0ba9ca2a7e 100644 --- a/src/app/pages/directory-service/components/kerberos-realms/kerberos-realms-list.elements.ts +++ b/src/app/pages/directory-service/components/kerberos-realms/kerberos-realms-list.elements.ts @@ -5,7 +5,9 @@ export const kerberosRealmsListElements = { hierarchy: [T('Directory Services'), T('Kerberos Realms')], anchorRouterLink: ['/directoryservice', 'kerberosrealms'], elements: { - kerberosRealms: {}, + kerberosRealms: { + anchor: 'kerberosrealms', + }, add: { hierarchy: [T('Add Kerberos Realm')], anchor: 'add-kerberos-realm', diff --git a/src/app/pages/directory-service/directory-services.elements.ts b/src/app/pages/directory-service/directory-services.elements.ts index 43383bfd23e..85414bf471d 100644 --- a/src/app/pages/directory-service/directory-services.elements.ts +++ b/src/app/pages/directory-service/directory-services.elements.ts @@ -5,7 +5,9 @@ export const directoryServicesElements = { hierarchy: [T('Credentials'), T('Directory Services')], anchorRouterLink: ['/credentials', 'directory-services'], elements: { - directoryServices: {}, + directoryServices: { + anchor: 'directory-services', + }, configureActiveDirectory: { hierarchy: [T('Configure Active Directory')], synonyms: [T('Active Directory')], diff --git a/src/app/pages/jobs/jobs-list.elements.ts b/src/app/pages/jobs/jobs-list.elements.ts index 04ed0a1e176..33e0c88df7e 100644 --- a/src/app/pages/jobs/jobs-list.elements.ts +++ b/src/app/pages/jobs/jobs-list.elements.ts @@ -6,6 +6,8 @@ export const jobsListElements = { anchorRouterLink: ['/jobs'], synonyms: [T('Jobs History'), T('Completed Jobs'), T('Failed Jobs'), T('Jobs in progress'), T('History')], elements: { - jobs: {}, + jobs: { + anchor: 'jobs', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/network/network.elements.ts b/src/app/pages/network/network.elements.ts index 731dcf481ea..440952b18aa 100644 --- a/src/app/pages/network/network.elements.ts +++ b/src/app/pages/network/network.elements.ts @@ -5,6 +5,8 @@ export const networkElements = { hierarchy: [T('Network')], anchorRouterLink: ['/network'], elements: { - network: {}, + network: { + anchor: 'network-dashboard', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.elements.ts b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.elements.ts index 9b56e7e440c..c39f37aebdf 100644 --- a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.elements.ts +++ b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.elements.ts @@ -5,7 +5,9 @@ export const reportingExportersElements = { hierarchy: [T('Reporting'), T('Reporting Exporters')], anchorRouterLink: ['/reportsdashboard', 'exporters'], elements: { - exporters: {}, + exporters: { + anchor: 'exporters', + }, add: { hierarchy: [T('Add Reporting Exporter')], anchor: 'add-reporting-exporter', diff --git a/src/app/pages/reports-dashboard/reports-dashboard.elements.ts b/src/app/pages/reports-dashboard/reports-dashboard.elements.ts index a8b9d25d566..fcda59ec0c3 100644 --- a/src/app/pages/reports-dashboard/reports-dashboard.elements.ts +++ b/src/app/pages/reports-dashboard/reports-dashboard.elements.ts @@ -6,6 +6,8 @@ export const reportingElements = { synonyms: [T('Stats')], anchorRouterLink: ['/reportsdashboard'], elements: { - reporting: {}, + reporting: { + anchor: 'reports-dashboard', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/services/services.elements.ts b/src/app/pages/services/services.elements.ts index 721a0cf75b8..57830a176cd 100644 --- a/src/app/pages/services/services.elements.ts +++ b/src/app/pages/services/services.elements.ts @@ -5,7 +5,9 @@ export const servicesElements = { hierarchy: [T('System'), T('Services')], anchorRouterLink: ['/system', 'services'], elements: { - services: {}, + services: { + anchor: 'services', + }, }, manualRenderElements: { smb: { diff --git a/src/app/pages/sharing/components/shares-dashboard/shares-dashboard.elements.ts b/src/app/pages/sharing/components/shares-dashboard/shares-dashboard.elements.ts index 9166af52e8c..b15e83990c4 100644 --- a/src/app/pages/sharing/components/shares-dashboard/shares-dashboard.elements.ts +++ b/src/app/pages/sharing/components/shares-dashboard/shares-dashboard.elements.ts @@ -7,6 +7,7 @@ export const sharesDashboardElements = { anchorRouterLink: ['/sharing'], elements: { sharing: { + anchor: 'shares-dashboard', synonyms: [ T('Shares'), T('Add Share'), diff --git a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.elements.ts b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.elements.ts index ab29be003c7..f73243d617d 100644 --- a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.elements.ts +++ b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.elements.ts @@ -5,6 +5,8 @@ export const authorizedAccessListElements = { hierarchy: [T('Shares'), T('iSCSI'), T('Authorized Access')], anchorRouterLink: ['/sharing', 'iscsi', 'authorized-access'], elements: { - list: {}, + list: { + anchor: 'authorized-access-list', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/sharing/iscsi/extent/extent-list/extent-list.elements.ts b/src/app/pages/sharing/iscsi/extent/extent-list/extent-list.elements.ts index a357123b33c..9418aca6cb7 100644 --- a/src/app/pages/sharing/iscsi/extent/extent-list/extent-list.elements.ts +++ b/src/app/pages/sharing/iscsi/extent/extent-list/extent-list.elements.ts @@ -5,6 +5,8 @@ export const extentListElements = { hierarchy: [T('Shares'), T('iSCSI'), T('Extents')], anchorRouterLink: ['/sharing', 'iscsi', 'extents'], elements: { - list: {}, + list: { + anchor: 'extent-list', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/sharing/iscsi/fibre-channel-ports/fibre-channel-ports.elements.ts b/src/app/pages/sharing/iscsi/fibre-channel-ports/fibre-channel-ports.elements.ts index 668c399d92e..ae57ee1cc1d 100644 --- a/src/app/pages/sharing/iscsi/fibre-channel-ports/fibre-channel-ports.elements.ts +++ b/src/app/pages/sharing/iscsi/fibre-channel-ports/fibre-channel-ports.elements.ts @@ -5,6 +5,8 @@ export const fibreChannelPortsElements = { hierarchy: [T('Shares'), T('iSCSI'), T('Fibre Channel Ports')], anchorRouterLink: ['/sharing', 'iscsi', 'fibre-channel-ports'], elements: { - list: {}, + list: { + anchor: 'fibre-channel-ports-list', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/sharing/iscsi/initiator/initiator-form/initiator-form.elements.ts b/src/app/pages/sharing/iscsi/initiator/initiator-form/initiator-form.elements.ts index ec644285a9b..54ef5d0557c 100644 --- a/src/app/pages/sharing/iscsi/initiator/initiator-form/initiator-form.elements.ts +++ b/src/app/pages/sharing/iscsi/initiator/initiator-form/initiator-form.elements.ts @@ -6,6 +6,7 @@ export const initiatorFormElements = { anchorRouterLink: ['/sharing', 'iscsi', 'initiators', 'add'], elements: { addInitiator: { + anchor: 'add-initiator', synonyms: [T('Initiators')], }, }, diff --git a/src/app/pages/sharing/iscsi/initiator/initiator-list/initiator-list.elements.ts b/src/app/pages/sharing/iscsi/initiator/initiator-list/initiator-list.elements.ts index 00b4f5ccc27..b4e117c664c 100644 --- a/src/app/pages/sharing/iscsi/initiator/initiator-list/initiator-list.elements.ts +++ b/src/app/pages/sharing/iscsi/initiator/initiator-list/initiator-list.elements.ts @@ -6,6 +6,8 @@ export const initiatorListElements = { synonyms: [T('Initiator Group')], anchorRouterLink: ['/sharing', 'iscsi', 'initiators'], elements: { - list: {}, + list: { + anchor: 'initiator-list', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/sharing/iscsi/portal/portal-list/portal-list.elements.ts b/src/app/pages/sharing/iscsi/portal/portal-list/portal-list.elements.ts index 180ff6e61b5..1dd19b8d5c5 100644 --- a/src/app/pages/sharing/iscsi/portal/portal-list/portal-list.elements.ts +++ b/src/app/pages/sharing/iscsi/portal/portal-list/portal-list.elements.ts @@ -5,6 +5,8 @@ export const portalListElements = { hierarchy: [T('Shares'), T('iSCSI'), T('Portals')], anchorRouterLink: ['/sharing', 'iscsi', 'portals'], elements: { - list: {}, + list: { + anchor: 'portal-list', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/sharing/iscsi/target/all-targets/target-list/target-list.elements.ts b/src/app/pages/sharing/iscsi/target/all-targets/target-list/target-list.elements.ts index 339039f55bc..f3ebfe1415c 100644 --- a/src/app/pages/sharing/iscsi/target/all-targets/target-list/target-list.elements.ts +++ b/src/app/pages/sharing/iscsi/target/all-targets/target-list/target-list.elements.ts @@ -5,6 +5,8 @@ export const targetListElements = { hierarchy: [T('Shares'), T('iSCSI'), T('Targets')], anchorRouterLink: ['/sharing', 'iscsi', 'targets'], elements: { - list: {}, + list: { + anchor: 'target-list', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/sharing/nfs/nfs-list/nfs-list.elements.ts b/src/app/pages/sharing/nfs/nfs-list/nfs-list.elements.ts index bb62f6894c0..65e0b9a0a9d 100644 --- a/src/app/pages/sharing/nfs/nfs-list/nfs-list.elements.ts +++ b/src/app/pages/sharing/nfs/nfs-list/nfs-list.elements.ts @@ -5,7 +5,9 @@ export const nfsListElements = { hierarchy: [T('Shares'), T('NFS')], anchorRouterLink: ['/sharing', 'nfs'], elements: { - nfs: {}, + nfs: { + anchor: 'nfs-list', + }, createNfsShare: { hierarchy: [T('Add NFS Share')], synonyms: [ diff --git a/src/app/pages/sharing/nfs/nfs-session-list/nfs-session-list.elements.ts b/src/app/pages/sharing/nfs/nfs-session-list/nfs-session-list.elements.ts index b312c8c166a..d62ac197930 100644 --- a/src/app/pages/sharing/nfs/nfs-session-list/nfs-session-list.elements.ts +++ b/src/app/pages/sharing/nfs/nfs-session-list/nfs-session-list.elements.ts @@ -5,6 +5,8 @@ export const nfsSessionListElements = { hierarchy: [T('Shares'), T('NFS'), T('NFS Sessions')], anchorRouterLink: ['/sharing', 'nfs', 'sessions'], elements: { - nfsSessions: {}, + nfsSessions: { + anchor: 'nfs-session-list', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/sharing/smb/smb-list/smb-list.elements.ts b/src/app/pages/sharing/smb/smb-list/smb-list.elements.ts index 9e53939eb8c..6dc461a3b35 100644 --- a/src/app/pages/sharing/smb/smb-list/smb-list.elements.ts +++ b/src/app/pages/sharing/smb/smb-list/smb-list.elements.ts @@ -6,7 +6,9 @@ export const smbListElements = { anchorRouterLink: ['/sharing', 'smb'], synonyms: [T('Samba')], elements: { - smbList: {}, + smbList: { + anchor: 'smb-list', + }, createSmbShare: { hierarchy: [T('Add SMB Share')], synonyms: diff --git a/src/app/pages/shell/shell.elements.ts b/src/app/pages/shell/shell.elements.ts index 6050b469a51..c635e4dcecd 100644 --- a/src/app/pages/shell/shell.elements.ts +++ b/src/app/pages/shell/shell.elements.ts @@ -6,6 +6,8 @@ export const shellElements = { anchorRouterLink: ['/system', 'shell'], synonyms: [T('CLI'), T('Terminal'), T('Console'), T('Command Line Interface'), T('Prompt')], elements: { - shell: {}, + shell: { + anchor: 'shell', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/storage/modules/disks/components/disk-list/disk-list.elements.ts b/src/app/pages/storage/modules/disks/components/disk-list/disk-list.elements.ts index 0d84f6beb54..27bd0937294 100644 --- a/src/app/pages/storage/modules/disks/components/disk-list/disk-list.elements.ts +++ b/src/app/pages/storage/modules/disks/components/disk-list/disk-list.elements.ts @@ -6,6 +6,7 @@ export const diskListElements = { anchorRouterLink: ['/storage', 'disks'], elements: { disks: { + anchor: 'disk-list', synonyms: [T('Manage Disks')], }, }, diff --git a/src/app/pages/storage/pools-dashboard.elements.ts b/src/app/pages/storage/pools-dashboard.elements.ts index 9ac5df08cae..992e8e04c0d 100644 --- a/src/app/pages/storage/pools-dashboard.elements.ts +++ b/src/app/pages/storage/pools-dashboard.elements.ts @@ -6,6 +6,7 @@ export const storageElements = { hierarchy: [T('Storage')], elements: { storageDashboard: { + anchor: 'storage-dashboard', synonyms: [ T('Pools'), T('Storage Dashboard'), diff --git a/src/app/pages/system/advanced/advanced-settings.elements.ts b/src/app/pages/system/advanced/advanced-settings.elements.ts index bbc7e7ddee7..59aa0e6d31e 100644 --- a/src/app/pages/system/advanced/advanced-settings.elements.ts +++ b/src/app/pages/system/advanced/advanced-settings.elements.ts @@ -6,6 +6,7 @@ export const advancedSettingsElements = { anchorRouterLink: ['/system', 'advanced'], elements: { advanced: { + anchor: 'advanced-settings', synonyms: [T('Settings')], }, }, diff --git a/src/app/pages/system/advanced/cron/cron-list/cron-list.elements.ts b/src/app/pages/system/advanced/cron/cron-list/cron-list.elements.ts index e3fe18ec942..e3d9f00bcdc 100644 --- a/src/app/pages/system/advanced/cron/cron-list/cron-list.elements.ts +++ b/src/app/pages/system/advanced/cron/cron-list/cron-list.elements.ts @@ -5,6 +5,8 @@ export const cronElements = { hierarchy: [T('System'), T('Cron Jobs')], anchorRouterLink: ['/system', 'cron'], elements: { - cron: {}, + cron: { + anchor: 'cron', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/system/advanced/init-shutdown/init-shutdown-list/init-shutdown-list.elements.ts b/src/app/pages/system/advanced/init-shutdown/init-shutdown-list/init-shutdown-list.elements.ts index 970d48f70d1..7dbc27e9604 100644 --- a/src/app/pages/system/advanced/init-shutdown/init-shutdown-list/init-shutdown-list.elements.ts +++ b/src/app/pages/system/advanced/init-shutdown/init-shutdown-list/init-shutdown-list.elements.ts @@ -5,6 +5,8 @@ export const initShudownListElements = { hierarchy: [T('System'), T('Init/Shutdown Scripts')], anchorRouterLink: ['/system', 'initshutdown'], elements: { - initShutDown: {}, + initShutDown: { + anchor: 'init-shutdown', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/system/advanced/sysctl/tunable-list/tunable-list.elements.ts b/src/app/pages/system/advanced/sysctl/tunable-list/tunable-list.elements.ts index ac8e94630ab..cc594416b7c 100644 --- a/src/app/pages/system/advanced/sysctl/tunable-list/tunable-list.elements.ts +++ b/src/app/pages/system/advanced/sysctl/tunable-list/tunable-list.elements.ts @@ -5,6 +5,8 @@ export const tunableListElements = { hierarchy: [T('System'), T('Tunables')], anchorRouterLink: ['/system', 'tunable'], elements: { - tunables: {}, + tunables: { + anchor: 'tunable', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/system/alert-service/alert-service-list/alert-service-list.elements.ts b/src/app/pages/system/alert-service/alert-service-list/alert-service-list.elements.ts index 4c785f90442..18ed2c25642 100644 --- a/src/app/pages/system/alert-service/alert-service-list/alert-service-list.elements.ts +++ b/src/app/pages/system/alert-service/alert-service-list/alert-service-list.elements.ts @@ -6,7 +6,9 @@ export const alertServiceListElements = { anchorRouterLink: ['/system', 'alert-settings', 'services'], synonyms: [T('Alerts'), T('Configure Notifications'), T('Configure Alerts')], elements: { - alertServiceList: {}, + alertServiceList: { + anchor: 'alert-service-list', + }, add: { hierarchy: [T('Add Alert')], anchor: 'add-alert-service', diff --git a/src/app/pages/system/bootenv/bootenv-list/bootenv-list.elements.ts b/src/app/pages/system/bootenv/bootenv-list/bootenv-list.elements.ts index 99e904fd58d..b00a3cf0ce9 100644 --- a/src/app/pages/system/bootenv/bootenv-list/bootenv-list.elements.ts +++ b/src/app/pages/system/bootenv/bootenv-list/bootenv-list.elements.ts @@ -6,7 +6,9 @@ export const bootListElements = { anchorRouterLink: ['/system', 'boot'], synonyms: [T('Create boot environment')], elements: { - boot: {}, + boot: { + anchor: 'boot-list', + }, stats: { hierarchy: [T('Stats/Settings')], }, diff --git a/src/app/pages/system/bootenv/bootenv-status/bootenv-status.elements.ts b/src/app/pages/system/bootenv/bootenv-status/bootenv-status.elements.ts index afe76e04b59..ff261ab52a7 100644 --- a/src/app/pages/system/bootenv/bootenv-status/bootenv-status.elements.ts +++ b/src/app/pages/system/bootenv/bootenv-status/bootenv-status.elements.ts @@ -5,6 +5,8 @@ export const bootEnvStatusElements = { hierarchy: [T('System'), T('Boot'), T('Boot Pool Status')], anchorRouterLink: ['/system', 'boot', 'status'], elements: { - bootStatus: {}, + bootStatus: { + anchor: 'boot-status', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/system/enclosure/components/jbof-list/jbof-list.elements.ts b/src/app/pages/system/enclosure/components/jbof-list/jbof-list.elements.ts index ac2b345f171..d8ed9d1faa0 100644 --- a/src/app/pages/system/enclosure/components/jbof-list/jbof-list.elements.ts +++ b/src/app/pages/system/enclosure/components/jbof-list/jbof-list.elements.ts @@ -6,7 +6,9 @@ export const jbofListElements = { hierarchy: [T('System'), T('View Enclosure'), T('NVMe-oF Expansion Shelves')], anchorRouterLink: ['/system', 'viewenclosure', 'jbof'], elements: { - jbof: {}, + jbof: { + anchor: 'jbof', + }, }, visibleTokens: [GlobalSearchVisibleToken.Enclosure], } satisfies UiSearchableElement; diff --git a/src/app/pages/system/failover-settings/failover-settings.elements.ts b/src/app/pages/system/failover-settings/failover-settings.elements.ts index 3adb3bb9f87..e63e5185af9 100644 --- a/src/app/pages/system/failover-settings/failover-settings.elements.ts +++ b/src/app/pages/system/failover-settings/failover-settings.elements.ts @@ -6,7 +6,9 @@ export const failoverElements = { hierarchy: [T('System'), T('Failover')], anchorRouterLink: ['/system', 'failover'], elements: { - failover: {}, + failover: { + anchor: 'failover', + }, syncToPeer: { hierarchy: [T('Sync To Peer')], }, diff --git a/src/app/pages/system/general-settings/general-settings.elements.ts b/src/app/pages/system/general-settings/general-settings.elements.ts index 8977c68955a..2f5724636f4 100644 --- a/src/app/pages/system/general-settings/general-settings.elements.ts +++ b/src/app/pages/system/general-settings/general-settings.elements.ts @@ -6,6 +6,7 @@ export const generalSettingsElements = { anchorRouterLink: ['/system', 'general'], elements: { general: { + anchor: 'general-settings', synonyms: [T('Settings')], }, }, diff --git a/src/app/pages/system/general-settings/support/eula/eula.elements.ts b/src/app/pages/system/general-settings/support/eula/eula.elements.ts index 0fa89935564..5e82dfb69b2 100644 --- a/src/app/pages/system/general-settings/support/eula/eula.elements.ts +++ b/src/app/pages/system/general-settings/support/eula/eula.elements.ts @@ -5,6 +5,8 @@ export const eulaElements = { hierarchy: [T('System'), T('Support'), T('Eula')], anchorRouterLink: ['/system', 'support', 'eula'], elements: { - eula: {}, + eula: { + anchor: 'eula', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/system/general-settings/support/support-card/support-card.elements.ts b/src/app/pages/system/general-settings/support/support-card/support-card.elements.ts index e1e0205aa20..90f84451109 100644 --- a/src/app/pages/system/general-settings/support/support-card/support-card.elements.ts +++ b/src/app/pages/system/general-settings/support/support-card/support-card.elements.ts @@ -5,7 +5,9 @@ export const supportCardElements = { hierarchy: [T('System'), T('Support')], anchorRouterLink: ['/system', 'support'], elements: { - support: {}, + support: { + anchor: 'support', + }, updateLicense: { hierarchy: [T('License')], synonyms: [ diff --git a/src/app/pages/system/update/components/manual-update-form/manual-update-form.elements.ts b/src/app/pages/system/update/components/manual-update-form/manual-update-form.elements.ts index 8857f48aaa1..ebe5665a36e 100644 --- a/src/app/pages/system/update/components/manual-update-form/manual-update-form.elements.ts +++ b/src/app/pages/system/update/components/manual-update-form/manual-update-form.elements.ts @@ -6,6 +6,8 @@ export const systemManualUpdateFormElements = { anchorRouterLink: ['/system', 'update', 'manualupdate'], synonyms: [T('Install Manual Update File'), T('Manual Update'), T('Manual Upgrade'), T('Upload Manual Update File')], elements: { - manualUpdate: {}, + manualUpdate: { + anchor: 'manual-update', + }, }, } satisfies UiSearchableElement; diff --git a/src/app/pages/two-factor-auth/two-factor.elements.ts b/src/app/pages/two-factor-auth/two-factor.elements.ts index 8a7d8232027..5b710f1bf83 100644 --- a/src/app/pages/two-factor-auth/two-factor.elements.ts +++ b/src/app/pages/two-factor-auth/two-factor.elements.ts @@ -5,7 +5,9 @@ export const twoFactorElements = { hierarchy: [T('Credentials'), T('Two-Factor Authentication')], anchorRouterLink: ['/credentials', 'two-factor'], elements: { - twoFactor: {}, + twoFactor: { + anchor: 'two-factor', + }, configure2FaSecret: { hierarchy: [T('Configure 2FA Secret')], anchor: 'configure-2fa-secret', diff --git a/src/app/pages/vm/vm-list.elements.ts b/src/app/pages/vm/vm-list.elements.ts index 39d8cb3ea59..ee79d63a8f9 100644 --- a/src/app/pages/vm/vm-list.elements.ts +++ b/src/app/pages/vm/vm-list.elements.ts @@ -7,6 +7,7 @@ export const vmListElements = { anchorRouterLink: ['/vm'], elements: { vm: { + anchor: 'vm-list', synonyms: [T('VM'), T('Virtualization')], }, add: { diff --git a/src/assets/i18n/ko.json b/src/assets/i18n/ko.json index 24d8be1229b..7f614eab07e 100644 --- a/src/assets/i18n/ko.json +++ b/src/assets/i18n/ko.json @@ -207,7 +207,6 @@ "LBA of First Error": "", "LDAP server hostnames or IP addresses. Separate entries with an empty space. Multiple hostnames or IP addresses can be entered to create an LDAP failover priority list. If a host does not respond, the next host in the list is tried until a new connection is established.": "", "LDAP server to use for SID/uid/gid map entries. When undefined, idmap_ldap uses *ldap://localhost/*. Example: ldap://ldap.netscape.com/o=Airius.com.": "", - "LONG": "LONG", "Leave at the default of 512 unless the initiator requires a different block size.": "", "Leave blank to allow all or enter a list of initiator hostnames. Separate entries by pressing Enter.": "", "Leave empty for default (OpsGenie API)": "", @@ -244,7 +243,6 @@ "Method": "", "Method Call": "", "Metrics": "", - "MiB": "MiB", "Microsoft Azure": "", "Microsoft Onedrive Access Token. Log in to the Microsoft account to add an access token.": "", "Minimum Passive Port": "", @@ -254,26 +252,7 @@ "Mutual secret password. Required when Peer User is set. Must be different than the Secret.": "", "NIC modifications are currently restricted due to pending network changes.": "", "NIC selection is currently restricted due to pending network changes.": "", - "NIC was added": "NIC 추가함", - "Name must start and end with a lowercase alphanumeric character. Hyphen is allowed in the middle e.g abc123, abc, abcd-1232": "이름은 반드시 알파벳 소문자로 시작하고 끝나야 합니다. 하이픈(-)은 중간에 사용할 수 있습니다(예: abc123, abc, abcd-1232).", - "Name of the WebDAV site, service, or software being used.": "사용중인 WebDAV 사이트, 서비스 또는 소프트웨어의 이름입니다.", - "Name of the new alert service.": "새로운 경고 서비스의 이름입니다.", - "Name of the new cloned boot environment. Alphanumeric characters, dashes (-), underscores (_), and periods (.) are allowed.": "새롭게 복제한 시작 환경의 이름입니다. 알파벳과 숫자, 대시(-), 밑줄(_), 온점(.)을 사용할 수 있습니다.", - "Name of the new dataset created from the cloned snapshot.": "복제한 스냅샷으로부터 생성된 새로운 데이터셋의 이름입니다.", - "Name of the pool is required": "풀 이름 필요", - "Name of the pool must be correct": "올바르지 않은 풀 이름", - "Name of the zvol is required": "ZVOL 이름 필요", - "Name of the zvol must be correct": "올바르지 않은 ZVOL 이름", - "Name of this SSH connection. SSH connection names must be unique.": "이 SSH 연결의 이름입니다. SSH 연결 이름은 고유해야 합니다.", - "Name of this replication configuration.": "이 복제 구성의 이름입니다.", - "Name or Naming Schema must be provided.": "이름 또는 이름짓기 방식을 제공해야 합니다.", "Name ~ \"admin\"": "", - "Negotiate – only encrypt transport if explicitly requested by the SMB client": "협상 – SMB 클라이언트가 명시적으로 요청하는 경우에만 전송 암호화", - "Netcat Active Side": "Netcat 액티브 사이드", - "Netcat Active Side Connect Address": "Netcat 액티브 사이드 연결 주소", - "Netcat Active Side Listen Address": "Netcat 액티브 사이드 수신 주소", - "Netcat Active Side Max Port": "Netcat 액티브 사이드 최대 포트", - "Netcat Active Side Min Port": "Netcat 액티브 사이드 최소 포트", "Network addresses allowed to use this initiator. Leave blank to allow all networks or list network addresses with a CIDR mask. Separate entries by pressing Enter.": "", "Network addresses allowed use this initiator. Each address can include an optional CIDR netmask. Click + to add the network address to the list. Example: 192.168.2.0/24.": "", "Network community string. The community string acts like a user ID or password. A user with the correct community string has access to network information. The default is public. For more information, see What is an SNMP Community String?.": "", @@ -301,9 +280,6 @@ "Number of virtual CPUs to allocate to the virtual machine. The VM operating system might have operational or licensing restrictions on the number of CPUs.": "", "OQ % Used": "", "OQ Used": "", - "Off by default. When set, smbd(8) attempts to authenticate users with the insecure and vulnerable NTLMv1 encryption. This setting allows backward compatibility with older versions of Windows, but is not recommended and should not be used on untrusted networks.": "기본으로 꺼져있습니다. 설정하면 smbd(8)은(는) 안전하지 않고 취약한 NTLMv1 암호화로 사용자를 인증하려고 시도합니다. 이 설정은 이전 버전의 Windows와의 하위 호환성을 허용하지만 권장하지 않으며, 신뢰할 수 없는 네트워크에서는 사용해서는 안 됩니다.", - "Offload Read": "오프로드 읽기", - "Offload Write": "오프로드 쓰기", "Once an enclosure is selected, all other VDEV creation steps will limit disk selection options to disks in the selected enclosure. If the enclosure selection is changed, all disk selections will be reset.": "", "Once enabled, users will be required to set up two factor authentication next time they login.": "", "One half widget and two quarter widgets below": "", @@ -322,73 +298,14 @@ "Only one can be active at a time.": "", "Only override the default if the initiator requires a different block size.": "", "Only replicate snapshots that match a defined creation time. To specify which snapshots will be replicated, set this checkbox and define the snapshot creation times that will be replicated. For example, setting this time frame to Hourly will only replicate snapshots that were created at the beginning of each hour.": "", - "Open ticket": "티켓 발행", "OpenStack Swift": "", "Opened at": "", - "Optional IP": "선택적 IP", - "Optional IP of 2nd Redfish management interface.": "2번째 Redfish 관리 인터페이스의 선택적 IP입니다.", - "Optional description. Portals are automatically assigned a numeric group.": "선택적 설명입니다. 포털에는 자동으로 숫자 그룹이 지정됩니다.", - "Optional user-friendly name.": "선택적 사용자 친화 이름입니다.", - "Optional. Enter a server description.": "선택사항입니다. 서버 설명을 입력합니다.", - "Optional. Only needed for private images.": "선택사항입니다. 개인 이미지에만 필요합니다.", - "Optional: CPU Set (Examples: 0-3,8-11)": "선택사항: CPU 세트 (예시: 0-3,8-11)", - "Optional: Choose installation media image": "선택사항: 설치 매체 이미지 선택", - "Optional: NUMA nodeset (Example: 0-1)": "선택사항: NUMA 노드세트 (예시: 0-1)", - "Options": "선택사항", - "Options cannot be loaded": "선택사항 불러올 수 없음", - "Options for encrypting the LDAP connection:
  • OFF: do not encrypt the LDAP connection.
  • ON: encrypt the LDAP connection with SSL on port 636.
  • START_TLS: encrypt the LDAP connection with STARTTLS on the default LDAP port 389.
": "LDAP 연결 암호화 선택사항:
  • 꺼짐: LDAP 연결을 암호화하지 않습니다.
  • 켜짐: 636번 포트에서 SSL을 사용하여 LDAP 연결을 암호화합니다.
  • START_TLS: 기본 LDAP 포트인 389번 포트에서 STARTTLS를 사용하여 LDAP 연결을 암호화합니다.
", "Other Execute": "", - "Other node is currently processing a failover event.": "다른 노드가 장애조치 진행중입니다.", - "Others": "기타", - "Outgoing Mail Server": "발송 메일 서버", - "Outlook": "OUTLOOK", - "Overrides default directory creation mask of 0777 which grants directory read, write and execute access for everybody.": "모든 사용자에게 디렉터리 읽기, 쓰기, 실행 권한을 부여하는 기본 디렉터리 생성 마스크인 0777을 대체합니다.", - "Overrides default file creation mask of 0666 which creates files with read and write access for everybody.": "모든 사용자가 읽고 쓸 수 있는 파일을 생성하는 기본 파일 생성 마스크인 0666을 대체합니다.", - "PCI Passthrough Device": "PCI 통과 장치", - "PCI device does not have a reset mechanism defined and you may experience inconsistent/degraded behavior when starting/stopping the VM.": "PCI 장치에는 재설정 메커니즘이 정의되어 있지 않으므로, 가상머신을 시작/중지할 때 일관되지 않거나 저하된 동작이 발생할 수 있습니다.", - "POSIX": "POSIX", - "POSIX Permissions": "POSIX 권한", "PULL": "", "PUSH": "", - "PagerDuty client name.": "PagerDuty 클라이언트 이름입니다.", - "Pair this certificate's public key with the Certificate Authority private key used to sign this certificate.": "이 인증서의 공개키를 이 인증서에 서명하는 데 사용한 인증기관의 개인 키와 묶습니다.", - "Passive Controller": "패시브 컨트롤러", - "Passthrough": "통과", - "Password associated with the LDAP User DN.": "LDAP 사용자 DN과 연결된 비밀번호입니다.", - "Password for the Active Directory administrator account. Required the first time a domain is configured. After initial configuration, the password is not needed to edit, start, or stop the service.": "액티브 디렉터리 관리자 계정의 비밀번호입니다. 도메인을 처음 구성할 때 필요합니다. 초기 구성이 끝나면 서비스를 수정, 시작 또는 멈추는 데 비밀번호가 필요하지 않습니다.", - "Password for the Bind DN.": "Bind DN의 비밀번호입니다.", - "Password to encrypt and decrypt remote data. Warning: Always securely back up this password! Losing the encryption password will result in data loss.": "원격 데이터를 암호화 및 복호화하는 비밀번호입니다. 위험: 항상 이 비밀번호를 안전한 곳에 보관하십시오! 암호화 비밀번호를 잃으면 데이터도 잃습니다.", - "Passwords cannot contain a ?. Passwords should be at least eight characters and contain a mix of lower and upper case, numbers, and special characters.": "비밀번호에 물음표(?)를 포함할 수 없습니다. 비밀번호는 최소 8자 이상이어야 하고 대소문자, 숫자, 특수문자를 포함해야 합니다.", - "Paste either or both public and private keys. If only a public key is entered, it will be stored alone. If only a private key is pasted, the public key will be automatically calculated and entered in the public key field. Click Generate Keypair to create a new keypair. Encrypted keypairs or keypairs with passphrases are not supported.": "공개 키와 개인 키를 붙여넣습니다. 공개 키만 입력한 경우 단독으로 저장합니다. 개인 키만 입력한 경우 자동으로 계산한 공개 키가 입력됩니다. 키쌍 생성을 누르면 새로운 키쌍을 생성합니다. 암호화된 키쌍 또는 비밀구절이 있는 키쌍은 지원하지 않습니다.", - "Paste the incoming webhook URL associated with this service.": "이 서비스와 연결된 웹훅 수신 URL을 붙여넣습니다.", - "Paste the certificate for the CA.": "인증기관의 인증서를 붙여넣습니다.", - "Paste the contents of your Certificate Signing Request here.": "인증서 서명 요청의 내용을 여기에 붙여넣습니다.", - "Paste the private key associated with the Certificate when available. Please provide a key at least 1024 bits long.": "가능한 경우 인증서와 연결된 개인 키를 붙여넣습니다. 키 길이는 최소 1024비트여야 합니다.", - "Pattern of naming custom snapshots to include in the replication with the periodic snapshot schedule. Enter the strftime(3) strings that match the snapshots to include in the replication.

When a periodic snapshot is not linked to the replication, enter the naming schema for manually created snapshots. Has the same %Y, %m, %d, %H, and %M string requirements as the Naming Schema in a Periodic Snapshot Task. Separate entries by pressing Enter.": "주기적인 스냅샷 일정에 따라 복제할 사용자 정의 스냅샷의 이름짓기 패턴입니다. 복제에 포함할 스냅샷과 일치하는 strftime(3) 문자열을 입력합니다.

주기적인 스냅샷 작업이 복제 작업과 연결되어있지 않은 경우, 수동으로 생성한 스냅샷의 이름짓기 방식을 입력하십시오. 주기적인 스냅샷 작업이름짓기 방식과 같이 %Y, %m, %d, %H%M 문자열이 필요합니다. Enter키를 눌러 항목을 구분합니다.", - "Pattern of naming custom snapshots to be replicated. Enter the name and strftime(3) %Y, %m, %d, %H, and %M strings that match the snapshots to include in the replication. Separate entries by pressing Enter. The number of snapshots matching the patterns are shown.": "복제할 사용자 정의 스냅샷의 이름짓기 패턴입니다. 복제에 포함할 스냅샷과 일치하는 이름과 strftime(3) %Y, %m, %d, %H%M 문자열을 입력합니다. Enter키를 눌러 항목을 구분합니다. 패턴과 일치하는 스냅샷의 수가 표시됩니다.", - "Peer Secret": "다른 지점 시크릿", - "Peer Secret (Confirm)": "다른 지점 시크릿 (수락)", - "Peer User": "다른 지점 사용자", - "Percentage used of dataset quota at which to generate a critical alert.": "심각 경고를 생성할 데이터셋 할당량의 사용율입니다.", - "Percentage used of dataset quota at which to generate a warning alert.": "위험 경고를 생성할 데이터셋 할당량의 사용율입니다.", - "Perform Reverse DNS Lookups": "역방향 DNS 조회 수행", - "Performs authentication from an LDAP server.": "LDAP 서버에서 인증을 수행합니다.", - "Photo Library API client secret generated from the Google API Console": "Google API 콘솔에서 생성한 사진 라이브러리 API 클라이언트 시크릿", "Pin vcpus": "", - "Please accept the terms of service for the given ACME Server.": "해당 ACME 서버의 서비스 약관에 동의해 주십시오.", - "Please click the button below to create a pool.": "풀을 생성하려면 아래 버튼을 눌러 주십시오.", - "Please specify the name of the image to pull. Format for the name is \"registry/repo/image\"": "가져올 이미지의 이름을 지정해 주십시오. 이름의 형식은 \"registry/repo/image\"입니다.", - "Please specify trains from which UI should retrieve available applications for the catalog.": "UI가 카탈로그에 사용 가능한 애플리케이션을 검색할 버전 구분을 지정해 주십시오.", - "Pool is using more than {maxPct}% of available space": "풀이 사용 가능한 공간의 {maxPct}% 이상을 사용함", "Power On Hours Ago": "", - "Predefined certificate extensions. Choose a profile that best matches your certificate usage scenario.": "사전 정의된 인증서 확장입니다. 인증서의 용도에 가장 부합하는 프로파일을 선택하십시오.", - "Predefined permission combinations:
Read: Read access and Execute permission on the object (RX).
Change: Read access, Execute permission, Write access, and Delete object (RXWD).
Full: Read access, Execute permission, Write access, Delete object, change Permissions, and take Ownership (RXWDPO).

For more details, see smbacls(1).": "사전 정의된 권한 조합:
읽기: 개체에 대한 읽기 및 실행 권한(RX)
변경: 읽기, 실행, 쓰기 및 개체 삭제(RXWD)
전체: 읽기, 실행, 쓰기, 개체 삭제, 권한 변경 및 소유권 취득(RXWDPO)

보다 자세한 사항은 smbacls(1)을(를) 참고하십시오.", - "Prevent the user from logging in or using password-based services until this option is unset. Locking an account is only possible when Disable Password is No and a Password has been created for the account.": "이 선택사항을 설정 해제하기 전에는 사용자가 로그인하거나 비밀번호 기반 서비스를 사용할 수 없습니다. 비밀번호 비활성화아니오이고 계정 비밀번호가 생성된 경우에만 계정을 잠글 수 있습니다.", - "Priority Code Point": "우선순위 코드 포인트(PCP)", - "Proceed with upgrading the pool? WARNING: Upgrading a pool is a one-way operation that might make some features of the pool incompatible with older versions of TrueNAS: ": "풀을 업그레이드하시겠습니까? 위험: 풀 업그레이드는 되돌릴 수 없고, 일부 풀 기능이 이전 버전의 TrueNAS와 호환되지 않을 수 있습니다: ", "Prototyping": "", - "Provide helpful notations related to the share, e.g. ‘Shared to everybody’. Maximum length is 120 characters.": "공유에대한 유용한 설명을 제공합니다(예: '모두에게 공유됨'). 최대 길이는 120자입니다.", - "Provides a plugin interface for Winbind to use varying backends to store SID/uid/gid mapping tables. The correct setting depends on the environment in which the NAS is deployed.": "Winbind가 다양한 백엔드를 사용하여 SID/uid/gid 매핑 테이블을 저장할 수 있도록 플러그인 인터페이스를 제공합니다. 올바른 설정은 NAS가 배포된 환경에 따라 달라집니다.", "Prune By": "", "Pull": "", "Pull Image": "", @@ -398,16 +315,7 @@ "Quiet": "", "Realm": "", "Rear": "", - "Restrict share visibility to users with read or write access to the share. See the smb.conf manual page.": "공유가 읽기 또는 쓰기 권한을 가진 사용자에게만 보이도록 제한합니다. smb.conf 매뉴얼을 참고하십시오.", - "SHORT": "SHORT", "Series": "", - "Session dialect": "세션 변형", - "Set for the LDAP server to disable authentication and allow read and write access to any client.": "LDAP 서버가 인증을 비활성화하고 모든 클라이언트에 대해 읽기와 쓰기 권한을 허용하도록 설정합니다.", - "Set for the default configuration to listen on all interfaces using the known values of user: upsmon and password: fixmepass.": "모든 인터페이스가 기본적으로 알려진 값(사용자: upsmon, 비밀번호: fixmepass)을 사용해 수신하도록 구성합니다.", - "Set only if required by the NFS client. Set to allow serving non-root mount requests.": "NFS 클라이언트에서 요청하는 경우 설정합니다. root가 아닌 탑재 요청을 허용합니다.", - "Set production status as active": "운영 상태를 활성화합니다.", - "Set specific times to snapshot the Source Datasets and replicate the snapshots to the Destination Dataset. Select a preset schedule or choose Custom to use the advanced scheduler.": "원본 데이터셋의 스냅샷을 저장하고 도착지 데이터셋으로 복제할 시각을 지정합니다. 사전설정 일정을 선택하거나 사용자 정의를 선택해 고급 일정관리를 사용합니다.", - "Set to determine if the system participates in a browser election. Leave unset when the network contains an AD or LDAP server, or when Vista or Windows 7 machines are present.": "시스템을 브라우저 선정에 포함할지 여부를 결정합니다. 네트워크에 AD 또는 LDAP 서버가 있거나, Vista 또는 Windows 7 장치가 있는 경우 설정하지 않습니다.", "Short": "", "Tail Lines": "", "Thick": "", @@ -2665,6 +2573,7 @@ "LINK STATE UNKNOWN": "연결 상태 불명", "LINK STATE UP": "연결 상태 양호", "LOCAL": "로컬", + "LONG": "LONG", "LUN ID": "LUN ID", "LUN RPM": "LUN RPM", "Label": "이름표", @@ -2877,6 +2786,7 @@ "Metadata (Special) Small Block Size": "메타데이터(특별) 작은 블록 크기", "Metadata VDEVs": "메타데이터 VDEV", "Method of snapshot transfer:
  • SSH is supported by most systems. It requires a previously created connection in System > SSH Connections.
  • SSH+NETCAT uses SSH to establish a connection to the destination system, then uses py-libzfs to send an unencrypted data stream for higher transfer speeds. This only works when replicating to a TrueNAS, or other system with py-libzfs installed.
  • LOCAL efficiently replicates snapshots to another dataset on the same system without using the network.
  • LEGACY uses the legacy replication engine from FreeNAS 11.2 and earlier.
": "스냅샷 전송 방식:
  • SSH은(는) 대부분의 시스템에서 사용할 수 있습니다. 미리 시스템 > SSH 연결을 통해 연결을 생성해야 합니다.
  • SSH+NETCAT은(는) 원격 연결을 수립하기 위해 SSH를 사용하고, py-libzfs을(를) 사용해 암호화되지 않은 데이터 스트림을 빠른 속도로 전송합니다. TrueNAS 시스템 혹은 py-libzfs이(가) 설치된 시스템으로 복제할 때만 사용할 수 있습니다.
  • 로컬은 네트워크를 통하지 않고 같은 시스템의 다른 데이터셋으로 스냅샷을 복제하는 효율적인 방법입니다.
  • 예전은 FreeNAS 11.2 까지 사용했던 오래된 복제 엔진을 사용합니다.
", + "MiB": "MiB", "MiB. Units smaller than MiB are not allowed.": "MiB보다 작은 단위는 사용할 수 없습니다.", "Middleware": "미들웨어", "Middleware - Credentials": "미들웨어 - 자격증명", @@ -2938,6 +2848,7 @@ "NFSv4 DNS Domain": "NFSv4 DNS 도메인", "NIC": "NIC", "NIC To Attach": "탑재할 NIC", + "NIC was added": "NIC 추가함", "NOTICE": "공지", "NS": "NS", "NTLMv1 Auth": "NTLMv1 인증", @@ -2952,11 +2863,23 @@ "Name and Options": "이름과 옵션", "Name and Provider": "이름과 공급자", "Name and Type": "이름과 유형", + "Name must start and end with a lowercase alphanumeric character. Hyphen is allowed in the middle e.g abc123, abc, abcd-1232": "이름은 반드시 알파벳 소문자로 시작하고 끝나야 합니다. 하이픈(-)은 중간에 사용할 수 있습니다(예: abc123, abc, abcd-1232).", "Name not added": "이름 추가되지 않음", "Name not found": "찾을 수 없는 이름", "Name of the channel to receive notifications. This overrides the default channel in the incoming webhook settings.": "알림을 수신할 채널의 이름입니다. 수신 웹훅 설정의 기본 채널보다 우선합니다.", "Name of the InfluxDB database.": "InfluxDB 데이터베이스의 이름입니다.", + "Name of the WebDAV site, service, or software being used.": "사용중인 WebDAV 사이트, 서비스 또는 소프트웨어의 이름입니다.", "Name of the extent. If the Extent size is not 0, it cannot be an existing file within the pool or dataset.": "익스텐트의 이름입니다. 익스텐트 크기0이 아니면, 풀이나 데이터셋의 기존 파일일 수 없습니다.", + "Name of the new alert service.": "새로운 경고 서비스의 이름입니다.", + "Name of the new cloned boot environment. Alphanumeric characters, dashes (-), underscores (_), and periods (.) are allowed.": "새롭게 복제한 시작 환경의 이름입니다. 알파벳과 숫자, 대시(-), 밑줄(_), 온점(.)을 사용할 수 있습니다.", + "Name of the new dataset created from the cloned snapshot.": "복제한 스냅샷으로부터 생성된 새로운 데이터셋의 이름입니다.", + "Name of the pool is required": "풀 이름 필요", + "Name of the pool must be correct": "올바르지 않은 풀 이름", + "Name of the zvol is required": "ZVOL 이름 필요", + "Name of the zvol must be correct": "올바르지 않은 ZVOL 이름", + "Name of this SSH connection. SSH connection names must be unique.": "이 SSH 연결의 이름입니다. SSH 연결 이름은 고유해야 합니다.", + "Name of this replication configuration.": "이 복제 구성의 이름입니다.", + "Name or Naming Schema must be provided.": "이름 또는 이름짓기 방식을 제공해야 합니다.", "Nameserver": "네임서버", "Nameserver (DHCP)": "네임서버 (DHCP)", "Nameserver 1": "네임서버 1", @@ -2965,10 +2888,16 @@ "Nameserver {n}": "네임서버 {n}", "Nameservers": "네임서버", "Naming Schema": "이름짓기 방식", + "Negotiate – only encrypt transport if explicitly requested by the SMB client": "협상 – SMB 클라이언트가 명시적으로 요청하는 경우에만 전송 암호화", "NetBIOS": "NetBIOS", "NetBIOS Alias": "NetBIOS 별칭", "NetBIOS Name": "NetBIOS 이름", "NetBIOS Name of this NAS. This name must differ from the Workgroup name and be no greater than 15 characters.": "이 NAS의 NetBIOS 이름입니다. 반드시 Workgroup 이름과 달라야 하며 15자를 초과할 수 없습니다.", + "Netcat Active Side": "Netcat 액티브 사이드", + "Netcat Active Side Connect Address": "Netcat 액티브 사이드 연결 주소", + "Netcat Active Side Listen Address": "Netcat 액티브 사이드 수신 주소", + "Netcat Active Side Max Port": "Netcat 액티브 사이드 최대 포트", + "Netcat Active Side Min Port": "Netcat 액티브 사이드 최소 포트", "Network": "네트워크", "Network Configuration": "네트워크 구성", "Network General Read": "네트워크 일반 읽기", @@ -3151,10 +3080,13 @@ "Object Quota": "오브젝트 할당량", "Oct": "10월", "Off": "꺼짐", + "Off by default. When set, smbd(8) attempts to authenticate users with the insecure and vulnerable NTLMv1 encryption. This setting allows backward compatibility with older versions of Windows, but is not recommended and should not be used on untrusted networks.": "기본으로 꺼져있습니다. 설정하면 smbd(8)은(는) 안전하지 않고 취약한 NTLMv1 암호화로 사용자를 인증하려고 시도합니다. 이 설정은 이전 버전의 Windows와의 하위 호환성을 허용하지만 권장하지 않으며, 신뢰할 수 없는 네트워크에서는 사용해서는 안 됩니다.", "Offline": "오프라인", "Offline Disk": "오프라인 디스크", "Offline VDEVs": "오프라인 VDEV", "Offline disk {name}?": "디스크 {name}의 연결을 끊으시겠습니까?", + "Offload Read": "오프로드 읽기", + "Offload Write": "오프로드 쓰기", "Ok": "확인", "Okay": "확인", "On": "켜짐", @@ -3169,11 +3101,24 @@ "Open": "열기", "Open Files": "파일 열기", "Open TrueCommand User Interface": "TrueCommand 사용자 인터페이스 열기", + "Open ticket": "티켓 발행", "Openstack API key or password. This is the OS_PASSWORD from an OpenStack credentials file.": "Openstack API 키 또는 비밀번호입니다. OpenStack 자격증명 파일의 OS_PASSWORD 입니다.", "Openstack user name for login. This is the OS_USERNAME from an OpenStack credentials file.": "Openstack 로그인 사용자 이름입니다. OpenStack 자격증명 파일의 OS_USERNAME 입니다.", "Operating System": "운영체제", "Operation": "작업", "Operation will change permissions on path: {path}": "이 작업은 다음 경로에 대한 권한을 변경합니다: {path}", + "Optional IP": "선택적 IP", + "Optional IP of 2nd Redfish management interface.": "2번째 Redfish 관리 인터페이스의 선택적 IP입니다.", + "Optional description. Portals are automatically assigned a numeric group.": "선택적 설명입니다. 포털에는 자동으로 숫자 그룹이 지정됩니다.", + "Optional user-friendly name.": "선택적 사용자 친화 이름입니다.", + "Optional. Enter a server description.": "선택사항입니다. 서버 설명을 입력합니다.", + "Optional. Only needed for private images.": "선택사항입니다. 개인 이미지에만 필요합니다.", + "Optional: CPU Set (Examples: 0-3,8-11)": "선택사항: CPU 세트 (예시: 0-3,8-11)", + "Optional: Choose installation media image": "선택사항: 설치 매체 이미지 선택", + "Optional: NUMA nodeset (Example: 0-1)": "선택사항: NUMA 노드세트 (예시: 0-1)", + "Options": "선택사항", + "Options cannot be loaded": "선택사항 불러올 수 없음", + "Options for encrypting the LDAP connection:
  • OFF: do not encrypt the LDAP connection.
  • ON: encrypt the LDAP connection with SSL on port 636.
  • START_TLS: encrypt the LDAP connection with STARTTLS on the default LDAP port 389.
": "LDAP 연결 암호화 선택사항:
  • 꺼짐: LDAP 연결을 암호화하지 않습니다.
  • 켜짐: 636번 포트에서 SSL을 사용하여 LDAP 연결을 암호화합니다.
  • START_TLS: 기본 LDAP 포트인 389번 포트에서 STARTTLS를 사용하여 LDAP 연결을 암호화합니다.
", "Order": "명령", "Organization": "조직", "Organizational Unit": "조직 단위", @@ -3187,25 +3132,39 @@ "Other TrueNAS controller has not finished booting.": "다른 TrueNAS 컨트롤러의 부팅이 완료되지 않았습니다.", "Other Write": "그 외 쓰기", "Other node is currently configuring the system dataset.": "다른 지점에서 시스템 데이터셋을 구성하고 있습니다.", + "Other node is currently processing a failover event.": "다른 노드가 장애조치 진행중입니다.", + "Others": "기타", "Out": "송신", "Outbound Activity": "송신 활동", "Outbound Network": "송신 네트워크", "Outbound Network:": "송신 네트워크", + "Outgoing Mail Server": "발송 메일 서버", "Outgoing [{networkInterfaceName}]": "[{networkInterfaceName}] 송신", + "Outlook": "OUTLOOK", "Override Admin Email": "관리자 이메일 대체", + "Overrides default directory creation mask of 0777 which grants directory read, write and execute access for everybody.": "모든 사용자에게 디렉터리 읽기, 쓰기, 실행 권한을 부여하는 기본 디렉터리 생성 마스크인 0777을 대체합니다.", + "Overrides default file creation mask of 0666 which creates files with read and write access for everybody.": "모든 사용자가 읽고 쓸 수 있는 파일을 생성하는 기본 파일 생성 마스크인 0666을 대체합니다.", "Overview": "개요", "Owner": "소유자", "Owner Group": "소유자 그룹", "Owner:": "소유자:", "PASSPHRASE": "비밀구절", + "PCI Passthrough Device": "PCI 통과 장치", + "PCI device does not have a reset mechanism defined and you may experience inconsistent/degraded behavior when starting/stopping the VM.": "PCI 장치에는 재설정 메커니즘이 정의되어 있지 않으므로, 가상머신을 시작/중지할 때 일관되지 않거나 저하된 동작이 발생할 수 있습니다.", + "POSIX": "POSIX", + "POSIX Permissions": "POSIX 권한", + "PagerDuty client name.": "PagerDuty 클라이언트 이름입니다.", + "Pair this certificate's public key with the Certificate Authority private key used to sign this certificate.": "이 인증서의 공개키를 이 인증서에 서명하는 데 사용한 인증기관의 개인 키와 묶습니다.", "Parent": "상위", "Parent Interface": "상위 인터페이스", "Parent Path": "상위 경로", "Parent dataset path (read-only).": "상위 데이터셋 경로입니다 (읽기전용).", "Partition": "파티션", + "Passive Controller": "패시브 컨트롤러", "Passphrase": "비밀구절", "Passphrase and confirmation should match.": "비밀구절과 확인은 일치해야 합니다.", "Passphrase value must match Confirm Passphrase": "비밀구절은 비밀구절 확인과 일치해야 합니다.", + "Passthrough": "통과", "Password": "비밀번호", "Password Disabled": "비밀번호 비활성화", "Password Login": "비밀번호 로그인", @@ -3213,27 +3172,46 @@ "Password Server": "비밀번호 서버", "Password Servers": "비밀번호 서버", "Password and confirmation should match.": "비밀번호와 확인은 일치해야 합니다.", + "Password associated with the LDAP User DN.": "LDAP 사용자 DN과 연결된 비밀번호입니다.", + "Password for the Active Directory administrator account. Required the first time a domain is configured. After initial configuration, the password is not needed to edit, start, or stop the service.": "액티브 디렉터리 관리자 계정의 비밀번호입니다. 도메인을 처음 구성할 때 필요합니다. 초기 구성이 끝나면 서비스를 수정, 시작 또는 멈추는 데 비밀번호가 필요하지 않습니다.", + "Password for the Bind DN.": "Bind DN의 비밀번호입니다.", "Password for the SSH Username account.": "SSH 사용자 이름 계정의 비밀번호입니다.", "Password for the user account.": "사용자 계정의 비밀번호입니다.", "Password is not set": "비밀번호 설정되지 않음", "Password is set": "비밀번호 설정됨", "Password login enabled": "비밀번호 로그인 활성화", + "Password to encrypt and decrypt remote data. Warning: Always securely back up this password! Losing the encryption password will result in data loss.": "원격 데이터를 암호화 및 복호화하는 비밀번호입니다. 위험: 항상 이 비밀번호를 안전한 곳에 보관하십시오! 암호화 비밀번호를 잃으면 데이터도 잃습니다.", "Password updated.": "비밀번호가 갱신되었습니다.", + "Passwords cannot contain a ?. Passwords should be at least eight characters and contain a mix of lower and upper case, numbers, and special characters.": "비밀번호에 물음표(?)를 포함할 수 없습니다. 비밀번호는 최소 8자 이상이어야 하고 대소문자, 숫자, 특수문자를 포함해야 합니다.", "Passwords do not match": "비밀번호 불일치", + "Paste either or both public and private keys. If only a public key is entered, it will be stored alone. If only a private key is pasted, the public key will be automatically calculated and entered in the public key field. Click Generate Keypair to create a new keypair. Encrypted keypairs or keypairs with passphrases are not supported.": "공개 키와 개인 키를 붙여넣습니다. 공개 키만 입력한 경우 단독으로 저장합니다. 개인 키만 입력한 경우 자동으로 계산한 공개 키가 입력됩니다. 키쌍 생성을 누르면 새로운 키쌍을 생성합니다. 암호화된 키쌍 또는 비밀구절이 있는 키쌍은 지원하지 않습니다.", + "Paste the incoming webhook URL associated with this service.": "이 서비스와 연결된 웹훅 수신 URL을 붙여넣습니다.", + "Paste the certificate for the CA.": "인증기관의 인증서를 붙여넣습니다.", + "Paste the contents of your Certificate Signing Request here.": "인증서 서명 요청의 내용을 여기에 붙여넣습니다.", + "Paste the private key associated with the Certificate when available. Please provide a key at least 1024 bits long.": "가능한 경우 인증서와 연결된 개인 키를 붙여넣습니다. 키 길이는 최소 1024비트여야 합니다.", "Path": "경로", "Path Length": "경로 길이", "Path Suffix": "경로 접미사", "Path to the Extent": "익스텐트 경로", "Pattern": "패턴", + "Pattern of naming custom snapshots to include in the replication with the periodic snapshot schedule. Enter the strftime(3) strings that match the snapshots to include in the replication.

When a periodic snapshot is not linked to the replication, enter the naming schema for manually created snapshots. Has the same %Y, %m, %d, %H, and %M string requirements as the Naming Schema in a Periodic Snapshot Task. Separate entries by pressing Enter.": "주기적인 스냅샷 일정에 따라 복제할 사용자 정의 스냅샷의 이름짓기 패턴입니다. 복제에 포함할 스냅샷과 일치하는 strftime(3) 문자열을 입력합니다.

주기적인 스냅샷 작업이 복제 작업과 연결되어있지 않은 경우, 수동으로 생성한 스냅샷의 이름짓기 방식을 입력하십시오. 주기적인 스냅샷 작업이름짓기 방식과 같이 %Y, %m, %d, %H%M 문자열이 필요합니다. Enter키를 눌러 항목을 구분합니다.", + "Pattern of naming custom snapshots to be replicated. Enter the name and strftime(3) %Y, %m, %d, %H, and %M strings that match the snapshots to include in the replication. Separate entries by pressing Enter. The number of snapshots matching the patterns are shown.": "복제할 사용자 정의 스냅샷의 이름짓기 패턴입니다. 복제에 포함할 스냅샷과 일치하는 이름과 strftime(3) %Y, %m, %d, %H%M 문자열을 입력합니다. Enter키를 눌러 항목을 구분합니다. 패턴과 일치하는 스냅샷의 수가 표시됩니다.", "Pause Scrub": "스크럽 일시정지", + "Peer Secret": "다른 지점 시크릿", + "Peer Secret (Confirm)": "다른 지점 시크릿 (수락)", + "Peer User": "다른 지점 사용자", "Pending": "보류중", "Pending Network Changes": "보류중인 네트워크 변경사항", "Pending Sync": "보류중인 동기화", "Pending Sync Keys Cleared": "보류중인 동기화 키 지움", "Percentage": "백분율", "Percentage of total core utilization": "전체 코어 활용 백분율", + "Percentage used of dataset quota at which to generate a critical alert.": "심각 경고를 생성할 데이터셋 할당량의 사용율입니다.", + "Percentage used of dataset quota at which to generate a warning alert.": "위험 경고를 생성할 데이터셋 할당량의 사용율입니다.", + "Perform Reverse DNS Lookups": "역방향 DNS 조회 수행", "Performance": "성능", "Performance Optimization": "성능 최적화", + "Performs authentication from an LDAP server.": "LDAP 서버에서 인증을 수행합니다.", "Periodic S.M.A.R.T. Tests": "주기적인 S.M.A.R.T. 검사", "Periodic Snapshot Tasks": "주기적인 스냅샷 작업", "Permission": "권한", @@ -3248,8 +3226,11 @@ "Permissions saved.": "권한을 저장했습니다.", "Phone": "전화", "Phone Number": "전화번호", + "Photo Library API client secret generated from the Google API Console": "Google API 콘솔에서 생성한 사진 라이브러리 API 클라이언트 시크릿", "Plain (No Encryption)": "평문 (암호화 없음)", "Platform": "플랫폼", + "Please accept the terms of service for the given ACME Server.": "해당 ACME 서버의 서비스 약관에 동의해 주십시오.", + "Please click the button below to create a pool.": "풀을 생성하려면 아래 버튼을 눌러 주십시오.", "Please describe:\n1. Steps to reproduce\n2. Expected Result\n3. Actual Result\n\nPlease use English for your report.": "다음을 설명해주십시오:\n1. 재현을 위한 단계\n2. 원하는 결과\n3. 실제 결과\n\n영어로 작성해주시기 바랍니다.", "Please input password.": "비밀번호를 입력해 주십시오.", "Please input user name.": "사용자 이름을 입력해 주십시오.", @@ -3258,6 +3239,8 @@ "Please specify a valid git repository uri.": "올바른 git 보관소 uri를 지정해 주십시오.", "Please specify branch of git repository to use for the catalog.": "카탈로그로 사용할 git 보관소의 브랜치를 지정해 주십시오.", "Please specify name to be used to lookup catalog.": "카탈로그를 조회하는 데 사용할 이름을 지정해 주십시오.", + "Please specify the name of the image to pull. Format for the name is \"registry/repo/image\"": "가져올 이미지의 이름을 지정해 주십시오. 이름의 형식은 \"registry/repo/image\"입니다.", + "Please specify trains from which UI should retrieve available applications for the catalog.": "UI가 카탈로그에 사용 가능한 애플리케이션을 검색할 버전 구분을 지정해 주십시오.", "Please specify whether to install NVIDIA driver or not.": "NVIDIA 드라이버 설치 여부를 지정해 주십시오.", "Please wait": "기다려 주십시오", "Pods": "파드", @@ -3279,6 +3262,7 @@ "Pool imported successfully.": "풀을 성공적으로 불러왔습니다.", "Pool is not healthy": "풀 건강상태 나쁨", "Pool is not selected": "풀을 선택하지 않음", + "Pool is using more than {maxPct}% of available space": "풀이 사용 가능한 공간의 {maxPct}% 이상을 사용함", "Pool options for {poolName} successfully saved.": "풀 {poolName}에 대한 선택사항이 성공적으로 저장되었습니다.", "Pool status is {status}": "풀이 {status} 상태임", "Pool updated successfully": "풀 갱신 성공", @@ -3309,6 +3293,8 @@ "Pre Init": "초기화 이전", "Pre Script": "이전 스크립트", "Pre-script": "이전 스크립트", + "Predefined certificate extensions. Choose a profile that best matches your certificate usage scenario.": "사전 정의된 인증서 확장입니다. 인증서의 용도에 가장 부합하는 프로파일을 선택하십시오.", + "Predefined permission combinations:
Read: Read access and Execute permission on the object (RX).
Change: Read access, Execute permission, Write access, and Delete object (RXWD).
Full: Read access, Execute permission, Write access, Delete object, change Permissions, and take Ownership (RXWDPO).

For more details, see smbacls(1).": "사전 정의된 권한 조합:
읽기: 개체에 대한 읽기 및 실행 권한(RX)
변경: 읽기, 실행, 쓰기 및 개체 삭제(RXWD)
전체: 읽기, 실행, 쓰기, 개체 삭제, 권한 변경 및 소유권 취득(RXWDPO)

보다 자세한 사항은 smbacls(1)을(를) 참고하십시오.", "Prefer": "선호", "Preferred Trains": "선호 버전 구분", "Preserve Extended Attributes": "확장 속성 유지", @@ -3319,11 +3305,13 @@ "Preset Name": "사전설정 이름", "Presets": "사전설정", "Prevent source system snapshots that have failed replication from being automatically removed by the Snapshot Retention Policy.": "스냅샷 보유 정책에 따라 복제에 실패한 원본 시스템 스냅샷이 자동으로 제거되는 것을 방지합니다.", + "Prevent the user from logging in or using password-based services until this option is unset. Locking an account is only possible when Disable Password is No and a Password has been created for the account.": "이 선택사항을 설정 해제하기 전에는 사용자가 로그인하거나 비밀번호 기반 서비스를 사용할 수 없습니다. 비밀번호 비활성화아니오이고 계정 비밀번호가 생성된 경우에만 계정을 잠글 수 있습니다.", "Preview JSON Service Account Key": "JSON 서비스 계정 키 미리보기", "Previous Page": "이전 페이지", "Primary Contact": "기본 연락처", "Primary DNS server.": "기본 DNS 서버", "Primary Group": "기본 그룹", + "Priority Code Point": "우선순위 코드 포인트(PCP)", "Privacy Passphrase": "개인정보 보호 비밀구절", "Privacy Protocol": "개인정보 보호 프로토콜", "Private Key": "개인 키", @@ -3332,6 +3320,7 @@ "Proactive Support": "사전 지원", "Proactive support settings is not available.": "사전 지원 설정을 사용할 수 없습니다.", "Proceed": "진행", + "Proceed with upgrading the pool? WARNING: Upgrading a pool is a one-way operation that might make some features of the pool incompatible with older versions of TrueNAS: ": "풀을 업그레이드하시겠습니까? 위험: 풀 업그레이드는 되돌릴 수 없고, 일부 풀 기능이 이전 버전의 TrueNAS와 호환되지 않을 수 있습니다: ", "Processor": "프로세서", "Product": "제품", "Product ID": "제품 ID", @@ -3344,8 +3333,10 @@ "Properties Exclude": "제외할 속성", "Properties Override": "덮어쓸 속성", "Protocol Options": "프로토콜 선택사항", + "Provide helpful notations related to the share, e.g. ‘Shared to everybody’. Maximum length is 120 characters.": "공유에대한 유용한 설명을 제공합니다(예: '모두에게 공유됨'). 최대 길이는 120자입니다.", "Provide keys/passphrases manually": "키/비밀구절을 수동으로 제공", "Provider": "공급자", + "Provides a plugin interface for Winbind to use varying backends to store SID/uid/gid mapping tables. The correct setting depends on the environment in which the NAS is deployed.": "Winbind가 다양한 백엔드를 사용하여 SID/uid/gid 매핑 테이블을 저장할 수 있도록 플러그인 인터페이스를 제공합니다. 올바른 설정은 NAS가 배포된 환경에 따라 달라집니다.", "Provisioning Type": "프로비저닝 유형", "Provisioning URI (includes Secret - Read only):": "프로비저닝 URI (시크릿 포함 - 읽기 전용)", "Proxies": "프록시", @@ -3587,6 +3578,7 @@ "Restores files to the selected directory.": "선택한 디렉터리로 파일을 복원합니다.", "Restoring backup": "백업 복원", "Restrict PAM": "PAM 제한", + "Restrict share visibility to users with read or write access to the share. See the smb.conf manual page.": "공유가 읽기 또는 쓰기 권한을 가진 사용자에게만 보이도록 제한합니다. smb.conf 매뉴얼을 참고하십시오.", "Restricted": "제한됨", "Resume Scrub": "스크럽 다시 시작", "Retention": "보유", @@ -3657,6 +3649,7 @@ "SFTP": "SFTP", "SFTP Log Facility": "SFTP 기록 기관", "SFTP Log Level": "SFTP 기록 수준", + "SHORT": "SHORT", "SID": "SID", "SMB": "SMB", "SMB - Client Account": "SMB - 클라이언트 계정", @@ -3965,6 +3958,7 @@ "Session ID": "세션 ID", "Session Timeout": "세션 시간 제한", "Session Token Lifetime": "세션 토큰 수명", + "Session dialect": "세션 변형", "Sessions": "세션", "Set": "설정", "Set ACL": "ACL 설정", @@ -3979,11 +3973,16 @@ "Set email": "이메일 설정", "Set enable sending messages to the address defined in the Email field.": "이메일 필드에 정의된 주소로의 메시지 전송을 활성화합니다.", "Set font size": "글꼴 크기 설정", + "Set for the LDAP server to disable authentication and allow read and write access to any client.": "LDAP 서버가 인증을 비활성화하고 모든 클라이언트에 대해 읽기와 쓰기 권한을 허용하도록 설정합니다.", "Set for the UPS to power off after shutting down the system.": "시스템을 종료한 후 UPS도 종료되도록 설정합니다.", + "Set for the default configuration to listen on all interfaces using the known values of user: upsmon and password: fixmepass.": "모든 인터페이스가 기본적으로 알려진 값(사용자: upsmon, 비밀번호: fixmepass)을 사용해 수신하도록 구성합니다.", "Set if the initiator does not support physical block size values over 4K (MS SQL).": "이니시에이터가 4K 이상의 물리 블록 크기를 지원하지 않는 경우 설정합니다(MS SQL).", "Set new password": "새로운 비밀번호 설정", + "Set only if required by the NFS client. Set to allow serving non-root mount requests.": "NFS 클라이언트에서 요청하는 경우 설정합니다. root가 아닌 탑재 요청을 허용합니다.", "Set or change the password of this SED. This password is used instead of the global SED password.": "이 SED의 비밀번호를 설정하거나 변경합니다. 전역 SED 비밀번호 대신 사용합니다.", "Set password for TrueNAS administrative user:": "TrueNAS 관리권한 사용자에 대한 비밀번호 설정:", + "Set production status as active": "운영 상태를 활성화합니다.", + "Set specific times to snapshot the Source Datasets and replicate the snapshots to the Destination Dataset. Select a preset schedule or choose Custom to use the advanced scheduler.": "원본 데이터셋의 스냅샷을 저장하고 도착지 데이터셋으로 복제할 시각을 지정합니다. 사전설정 일정을 선택하거나 사용자 정의를 선택해 고급 일정관리를 사용합니다.", "Set the maximum number of connections per IP address. 0 means unlimited.": "IP 주소 당 최대 연결수를 설정합니다. 0은 제한을 두지 않습니다.", "Set the number of data copies on this dataset.": "이 데이터셋의 데이터 사본의 수를 설정합니다.", "Set the port the FTP service listens on.": "FTP 서비스가 열어둘 포트를 설정합니다.", @@ -4001,6 +4000,7 @@ "Set to automatically create the defined Remote Path if it does not exist.": "원격 경로가 존재하지 않을 경우 자동으로 정의합니다.", "Set to boot a debug kernel after the next system restart.": "다음 시스템 재시작시 커널 디버그로 부팅합니다.", "Set to create a new primary group with the same name as the user. Unset to select an existing group for the user.": "사용자 이름과 동일한 새로운 기본 그룹을 만듭니다. 설정하지 않으면 기존 그룹을 선택합니다.", + "Set to determine if the system participates in a browser election. Leave unset when the network contains an AD or LDAP server, or when Vista or Windows 7 machines are present.": "시스템을 브라우저 선정에 포함할지 여부를 결정합니다. 네트워크에 AD 또는 LDAP 서버가 있거나, Vista 또는 Windows 7 장치가 있는 경우 설정하지 않습니다.", "Set to display image upload options.": "이미지 업로드 선택사항을 표시합니다.", "Set to either start this replication task immediately after the linked periodic snapshot task completes or continue to create a separate Schedule for this replication.": "연결된 주기적인 스냅샷 작업을 완료한 후 즉시 이 복제 작업을 시작하거나, 이 복제작업에 대한 별도의 일정을 계속 생성합니다.", "Set to enable DHCP. Leave unset to create a static IPv4 or IPv6 configuration. Only one interface can be configured for DHCP.": "DHCP을(를) 활성화합니다. 선택하지 않으면 정적 IPv4 또는 IPv6 구성을 생성합니다. 하나의 인터페이스만 DHCP로 구성할 수 있습니다.", @@ -5315,4 +5315,4 @@ "{used} of {total} ({used_pct})": "{total} 중 {used} ({used_pct})", "{version} is available!": "{version}이 준비되었습니다!", "{view} on {enclosure}": "{enclousure}의 {view}" -} +} \ No newline at end of file diff --git a/src/assets/ui-searchable-elements.json b/src/assets/ui-searchable-elements.json index 2cec9363c6f..a72595a3e6c 100644 --- a/src/assets/ui-searchable-elements.json +++ b/src/assets/ui-searchable-elements.json @@ -234,7 +234,7 @@ "available" ], "routerLink": null, - "anchor": "applications-discover", + "anchor": "available-apps-list", "triggerAnchor": null, "section": "ui" }, @@ -342,7 +342,7 @@ "audit" ], "routerLink": null, - "anchor": "system-audit", + "anchor": "audit-list", "triggerAnchor": null, "section": "ui" }, @@ -359,7 +359,7 @@ "backup-credentials" ], "routerLink": null, - "anchor": "credentials-backup-credentials", + "anchor": "backup-credentials", "triggerAnchor": null, "section": "ui" }, @@ -628,7 +628,7 @@ "certificates" ], "routerLink": null, - "anchor": "credentials-certificates", + "anchor": "certificates-dash", "triggerAnchor": null, "section": "ui" }, @@ -739,7 +739,7 @@ "groups" ], "routerLink": null, - "anchor": "credentials-groups", + "anchor": "groups-list", "triggerAnchor": null, "section": "ui" }, @@ -784,7 +784,7 @@ "privileges" ], "routerLink": null, - "anchor": "credentials-groups-privileges", + "anchor": "privileges-list", "triggerAnchor": null, "section": "ui" }, @@ -801,7 +801,7 @@ "kmip" ], "routerLink": null, - "anchor": "credentials-kmip", + "anchor": "kmip", "triggerAnchor": null, "section": "ui" }, @@ -913,7 +913,7 @@ "users" ], "routerLink": null, - "anchor": "credentials-users", + "anchor": "users-list", "triggerAnchor": null, "section": "ui" }, @@ -952,7 +952,7 @@ "/dashboard" ], "routerLink": null, - "anchor": "dashboard", + "anchor": "main-dashboard", "triggerAnchor": null, "section": "ui" }, @@ -999,7 +999,7 @@ "cloud-backup" ], "routerLink": null, - "anchor": "data-protection-truecloud-backup-tasks", + "anchor": "cloud-backup-tasks", "triggerAnchor": null, "section": "ui" }, @@ -1061,7 +1061,7 @@ "cloudsync" ], "routerLink": null, - "anchor": "data-protection-cloud-sync-tasks", + "anchor": "cloudsync-tasks", "triggerAnchor": null, "section": "ui" }, @@ -1078,7 +1078,7 @@ "/data-protection" ], "routerLink": null, - "anchor": "data-protection", + "anchor": "data-protection-dashboard", "triggerAnchor": null, "section": "ui" }, @@ -1122,7 +1122,7 @@ "replication" ], "routerLink": null, - "anchor": "data-protection-replication-tasks", + "anchor": "replication-tasks", "triggerAnchor": null, "section": "ui" }, @@ -1165,7 +1165,7 @@ "rsync" ], "routerLink": null, - "anchor": "data-protection-rsync-tasks", + "anchor": "rsync-tasks", "triggerAnchor": null, "section": "ui" }, @@ -1184,7 +1184,7 @@ "priority" ], "routerLink": null, - "anchor": "data-protection-scrub-tasks-resilver-priority", + "anchor": "scrub-priority", "triggerAnchor": null, "section": "ui" }, @@ -1227,7 +1227,7 @@ "scrub" ], "routerLink": null, - "anchor": "data-protection-scrub-tasks", + "anchor": "scrub-tasks", "triggerAnchor": null, "section": "ui" }, @@ -1308,7 +1308,7 @@ "smart" ], "routerLink": null, - "anchor": "data-protection-periodic-s.m.a.r.t.-tests", + "anchor": "smart-tasks", "triggerAnchor": null, "section": "ui" }, @@ -1377,7 +1377,7 @@ "snapshot" ], "routerLink": null, - "anchor": "data-protection-periodic-snapshot-tasks", + "anchor": "snapshot-tasks", "triggerAnchor": null, "section": "ui" }, @@ -1394,7 +1394,7 @@ "vmware-snapshots" ], "routerLink": null, - "anchor": "data-protection-vmware-snapshots", + "anchor": "vmware-snapshots", "triggerAnchor": null, "section": "ui" }, @@ -1752,7 +1752,7 @@ "idmap" ], "routerLink": null, - "anchor": "directory-services-idmap", + "anchor": "idmap", "triggerAnchor": null, "section": "ui" }, @@ -1793,7 +1793,7 @@ "kerberoskeytabs" ], "routerLink": null, - "anchor": "directory-services-kerberos-keytabs", + "anchor": "kerberoskeytabs", "triggerAnchor": null, "section": "ui" }, @@ -1834,7 +1834,7 @@ "kerberosrealms" ], "routerLink": null, - "anchor": "directory-services-kerberos-realms", + "anchor": "kerberosrealms", "triggerAnchor": null, "section": "ui" }, @@ -1893,7 +1893,7 @@ "directory-services" ], "routerLink": null, - "anchor": "credentials-directory-services", + "anchor": "directory-services", "triggerAnchor": null, "section": "ui" }, @@ -2114,7 +2114,7 @@ "/network" ], "routerLink": null, - "anchor": "network", + "anchor": "network-dashboard", "triggerAnchor": null, "section": "ui" }, @@ -2158,7 +2158,7 @@ "exporters" ], "routerLink": null, - "anchor": "reporting-reporting-exporters", + "anchor": "exporters", "triggerAnchor": null, "section": "ui" }, @@ -2344,7 +2344,7 @@ "/reportsdashboard" ], "routerLink": null, - "anchor": "reporting", + "anchor": "reports-dashboard", "triggerAnchor": null, "section": "ui" }, @@ -2523,7 +2523,7 @@ "services" ], "routerLink": null, - "anchor": "system-services", + "anchor": "services", "triggerAnchor": null, "section": "ui" }, @@ -2567,7 +2567,7 @@ "/sharing" ], "routerLink": null, - "anchor": "shares", + "anchor": "shares-dashboard", "triggerAnchor": null, "section": "ui" }, @@ -2586,7 +2586,7 @@ "authorized-access" ], "routerLink": null, - "anchor": "shares-iscsi-authorized-access", + "anchor": "authorized-access-list", "triggerAnchor": null, "section": "ui" }, @@ -2605,7 +2605,7 @@ "extents" ], "routerLink": null, - "anchor": "shares-iscsi-extents", + "anchor": "extent-list", "triggerAnchor": null, "section": "ui" }, @@ -2624,7 +2624,7 @@ "fibre-channel-ports" ], "routerLink": null, - "anchor": "shares-iscsi-fibre-channel-ports", + "anchor": "fibre-channel-ports-list", "triggerAnchor": null, "section": "ui" }, @@ -2643,7 +2643,7 @@ "fibre-channel-ports" ], "routerLink": null, - "anchor": "shares-iscsi-fibre-channel-ports", + "anchor": "fibre-channel-ports-list", "triggerAnchor": null, "section": "ui" }, @@ -2663,7 +2663,7 @@ "add" ], "routerLink": null, - "anchor": "shares-initiators-add-initiator", + "anchor": "add-initiator", "triggerAnchor": null, "section": "ui" }, @@ -2684,7 +2684,7 @@ "initiators" ], "routerLink": null, - "anchor": "shares-iscsi-initiators", + "anchor": "initiator-list", "triggerAnchor": null, "section": "ui" }, @@ -2723,7 +2723,7 @@ "portals" ], "routerLink": null, - "anchor": "shares-iscsi-portals", + "anchor": "portal-list", "triggerAnchor": null, "section": "ui" }, @@ -2742,7 +2742,7 @@ "targets" ], "routerLink": null, - "anchor": "shares-iscsi-targets", + "anchor": "target-list", "triggerAnchor": null, "section": "ui" }, @@ -2786,7 +2786,7 @@ "nfs" ], "routerLink": null, - "anchor": "shares-nfs", + "anchor": "nfs-list", "triggerAnchor": null, "section": "ui" }, @@ -2805,7 +2805,7 @@ "sessions" ], "routerLink": null, - "anchor": "shares-nfs-nfs-sessions", + "anchor": "nfs-session-list", "triggerAnchor": null, "section": "ui" }, @@ -2852,7 +2852,7 @@ "smb" ], "routerLink": null, - "anchor": "shares-smb", + "anchor": "smb-list", "triggerAnchor": null, "section": "ui" }, @@ -2971,7 +2971,7 @@ "shell" ], "routerLink": null, - "anchor": "system-shell", + "anchor": "shell", "triggerAnchor": null, "section": "ui" }, @@ -3147,7 +3147,7 @@ "disks" ], "routerLink": null, - "anchor": "storage-disks", + "anchor": "disk-list", "triggerAnchor": null, "section": "ui" }, @@ -3214,7 +3214,7 @@ "/storage" ], "routerLink": null, - "anchor": "storage", + "anchor": "storage-dashboard", "triggerAnchor": null, "section": "ui" }, @@ -3342,7 +3342,7 @@ "advanced" ], "routerLink": null, - "anchor": "system-advanced-settings", + "anchor": "advanced-settings", "triggerAnchor": null, "section": "ui" }, @@ -3700,7 +3700,7 @@ "cron" ], "routerLink": null, - "anchor": "system-cron-jobs", + "anchor": "cron", "triggerAnchor": null, "section": "ui" }, @@ -3849,7 +3849,7 @@ "initshutdown" ], "routerLink": null, - "anchor": "system-init/shutdown-scripts", + "anchor": "init-shutdown", "triggerAnchor": null, "section": "ui" }, @@ -4259,7 +4259,7 @@ "tunable" ], "routerLink": null, - "anchor": "system-tunables", + "anchor": "tunable", "triggerAnchor": null, "section": "ui" }, @@ -4418,7 +4418,7 @@ "services" ], "routerLink": null, - "anchor": "system-alert-settings-alert-services", + "anchor": "alert-service-list", "triggerAnchor": null, "section": "ui" }, @@ -4510,7 +4510,7 @@ "boot" ], "routerLink": null, - "anchor": "system-boot-environments", + "anchor": "boot-list", "triggerAnchor": null, "section": "ui" }, @@ -4529,7 +4529,7 @@ "status" ], "routerLink": null, - "anchor": "system-boot-boot-pool-status", + "anchor": "boot-status", "triggerAnchor": null, "section": "ui" }, @@ -4550,7 +4550,7 @@ "jbof" ], "routerLink": null, - "anchor": "system-view-enclosure-nvme-of-expansion-shelves", + "anchor": "jbof", "triggerAnchor": null, "section": "ui" }, @@ -4569,7 +4569,7 @@ "failover" ], "routerLink": null, - "anchor": "system-failover", + "anchor": "failover", "triggerAnchor": null, "section": "ui" }, @@ -4696,7 +4696,7 @@ "general" ], "routerLink": null, - "anchor": "system-general-settings", + "anchor": "general-settings", "triggerAnchor": null, "section": "ui" }, @@ -5240,7 +5240,7 @@ "eula" ], "routerLink": null, - "anchor": "system-support-eula", + "anchor": "eula", "triggerAnchor": null, "section": "ui" }, @@ -5257,7 +5257,7 @@ "support" ], "routerLink": null, - "anchor": "system-support", + "anchor": "support", "triggerAnchor": null, "section": "ui" }, @@ -5324,7 +5324,7 @@ "manualupdate" ], "routerLink": null, - "anchor": "system-update-manual-update", + "anchor": "manual-update", "triggerAnchor": null, "section": "ui" }, @@ -5390,7 +5390,7 @@ "two-factor" ], "routerLink": null, - "anchor": "credentials-two-factor-authentication", + "anchor": "two-factor", "triggerAnchor": null, "section": "ui" }, @@ -5455,7 +5455,7 @@ "/vm" ], "routerLink": null, - "anchor": "virtual-machines", + "anchor": "vm-list", "triggerAnchor": null, "section": "ui" } From 464a06e861876d4c0bfe7bd13431d7bb70b60793 Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Mon, 6 Jan 2025 03:40:07 +0500 Subject: [PATCH 06/18] NAS-131829 / 25.04 / Adds error log for middleware and efficient calls (#11253) --- src/app/app.component.ts | 6 +- .../core/testing/classes/mock-api.service.ts | 4 +- .../mock-enclosure-api.service.ts | 4 +- .../core/testing/utils/empty-auth.service.ts | 1 - src/app/core/testing/utils/mock-api.utils.ts | 10 +- src/app/core/testing/utils/mock-auth.utils.ts | 9 +- .../modules/auth/auth-guard.service.spec.ts | 4 +- src/app/modules/auth/auth-guard.service.ts | 6 +- src/app/modules/auth/auth.service.spec.ts | 35 +++--- src/app/modules/auth/auth.service.ts | 34 ++---- .../auth/two-factor-guard.service.spec.ts | 5 +- .../modules/auth/two-factor-guard.service.ts | 4 +- .../translations/translations-loaded.guard.ts | 6 +- src/app/modules/websocket/api.service.ts | 4 +- .../modules/websocket/ping.service.spec.ts | 6 +- src/app/modules/websocket/ping.service.ts | 8 +- .../subscription-manager.service.spec.ts | 9 +- .../websocket/subscription-manager.service.ts | 15 ++- .../websocket/websocket-handler.service.ts | 25 +++-- .../credentials/users/store/user.effects.ts | 3 +- src/app/pages/signin/signin.component.spec.ts | 4 +- src/app/pages/signin/signin.component.ts | 6 +- .../pages/signin/store/signin.store.spec.ts | 5 +- src/app/pages/signin/store/signin.store.ts | 4 +- .../config-reset.component.spec.ts | 5 +- .../config-reset/config-reset.component.ts | 4 +- .../failover/failover.component.ts | 4 +- .../failover-settings.component.spec.ts | 4 +- .../gui/gui-form/gui-form.component.spec.ts | 10 +- .../gui/gui-form/gui-form.component.ts | 4 +- .../manual-update-form.component.spec.ts | 4 - .../services/websocket-status.service.spec.ts | 104 ++++++++++++++++++ src/app/services/websocket-status.service.ts | 44 ++++++++ .../store/preferences/preferences.effects.ts | 18 ++- src/main.ts | 8 +- 35 files changed, 302 insertions(+), 124 deletions(-) create mode 100644 src/app/services/websocket-status.service.spec.ts create mode 100644 src/app/services/websocket-status.service.ts diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b8ddb79253a..1c07d95c335 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -6,10 +6,10 @@ import { Router, NavigationEnd, RouterOutlet } from '@angular/router'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { filter, tap } from 'rxjs'; import { WINDOW } from 'app/helpers/window.helper'; -import { AuthService } from 'app/modules/auth/auth.service'; import { LayoutService } from 'app/modules/layout/layout.service'; import { PingService } from 'app/modules/websocket/ping.service'; import { DetectBrowserService } from 'app/services/detect-browser.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; @UntilDestroy() @Component({ @@ -24,13 +24,13 @@ export class AppComponent implements OnInit { constructor( public title: Title, private router: Router, - private authService: AuthService, + private wsStatus: WebSocketStatusService, private detectBrowser: DetectBrowserService, private layoutService: LayoutService, private pingService: PingService, @Inject(WINDOW) private window: Window, ) { - this.authService.isAuthenticated$.pipe(untilDestroyed(this)).subscribe((isAuthenticated) => { + this.wsStatus.isAuthenticated$.pipe(untilDestroyed(this)).subscribe((isAuthenticated) => { this.isAuthenticated = isAuthenticated; }); this.title.setTitle('TrueNAS - ' + this.window.location.hostname); diff --git a/src/app/core/testing/classes/mock-api.service.ts b/src/app/core/testing/classes/mock-api.service.ts index 16c31e8ed88..7fe01659149 100644 --- a/src/app/core/testing/classes/mock-api.service.ts +++ b/src/app/core/testing/classes/mock-api.service.ts @@ -22,6 +22,7 @@ import { Job } from 'app/interfaces/job.interface'; import { ApiService } from 'app/modules/websocket/api.service'; import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; /** * Better than just expect.anything() because it allows null and undefined. @@ -47,10 +48,11 @@ export class MockApiService extends ApiService { constructor( wsHandler: WebSocketHandlerService, + wsStatus: WebSocketStatusService, subscriptionManager: SubscriptionManagerService, translate: TranslateService, ) { - super(wsHandler, subscriptionManager, translate); + super(wsHandler, wsStatus, subscriptionManager, translate); this.call = jest.fn(); this.job = jest.fn(); diff --git a/src/app/core/testing/mock-enclosure/mock-enclosure-api.service.ts b/src/app/core/testing/mock-enclosure/mock-enclosure-api.service.ts index f9ec4dc6de4..b20ba4ae8f2 100644 --- a/src/app/core/testing/mock-enclosure/mock-enclosure-api.service.ts +++ b/src/app/core/testing/mock-enclosure/mock-enclosure-api.service.ts @@ -10,6 +10,7 @@ import { SystemInfo } from 'app/interfaces/system-info.interface'; import { ApiService } from 'app/modules/websocket/api.service'; import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; @Injectable({ providedIn: 'root', @@ -20,10 +21,11 @@ export class MockEnclosureApiService extends ApiService { constructor( wsManager: WebSocketHandlerService, + wsStatus: WebSocketStatusService, subscriptionManager: SubscriptionManagerService, translate: TranslateService, ) { - super(wsManager, subscriptionManager, translate); + super(wsManager, wsStatus, subscriptionManager, translate); console.warn('MockEnclosureApiService is in effect. Some calls will be mocked'); } diff --git a/src/app/core/testing/utils/empty-auth.service.ts b/src/app/core/testing/utils/empty-auth.service.ts index 62d28659523..042356fb1ee 100644 --- a/src/app/core/testing/utils/empty-auth.service.ts +++ b/src/app/core/testing/utils/empty-auth.service.ts @@ -3,7 +3,6 @@ import { AuthService } from 'app/modules/auth/auth.service'; export class EmptyAuthService { readonly authToken$ = getMissingInjectionErrorObservable(AuthService.name); - readonly isAuthenticated$ = getMissingInjectionErrorObservable(AuthService.name); readonly user$ = getMissingInjectionErrorObservable(AuthService.name); readonly isSysAdmin$ = getMissingInjectionErrorObservable(AuthService.name); readonly userTwoFactorConfig$ = getMissingInjectionErrorObservable(AuthService.name); diff --git a/src/app/core/testing/utils/mock-api.utils.ts b/src/app/core/testing/utils/mock-api.utils.ts index ab3fc3b7644..a617bc365b6 100644 --- a/src/app/core/testing/utils/mock-api.utils.ts +++ b/src/app/core/testing/utils/mock-api.utils.ts @@ -14,6 +14,7 @@ import { Job } from 'app/interfaces/job.interface'; import { ApiService } from 'app/modules/websocket/api.service'; import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; /** * This is a sugar syntax for creating simple api mocks. @@ -49,11 +50,12 @@ export function mockApi( { provide: ApiService, useFactory: ( + wsStatus: WebSocketStatusService, wsHandler: WebSocketHandlerService, translate: TranslateService, ) => { const subscriptionManager = {} as SubscriptionManagerService; - const mockApiService = new MockApiService(wsHandler, subscriptionManager, translate); + const mockApiService = new MockApiService(wsHandler, wsStatus, subscriptionManager, translate); (mockResponses || []).forEach((mockResponse) => { if (mockResponse.type === MockApiResponseType.Call) { mockApiService.mockCall(mockResponse.method, mockResponse.response); @@ -66,7 +68,11 @@ export function mockApi( }); return mockApiService; }, - deps: [WebSocketHandlerService, TranslateService], + deps: [WebSocketStatusService, WebSocketHandlerService, TranslateService], + }, + { + provide: WebSocketStatusService, + useValue: ({} as WebSocketStatusService), }, { provide: MockApiService, diff --git a/src/app/core/testing/utils/mock-auth.utils.ts b/src/app/core/testing/utils/mock-auth.utils.ts index 98778a5df6d..b986099cef7 100644 --- a/src/app/core/testing/utils/mock-auth.utils.ts +++ b/src/app/core/testing/utils/mock-auth.utils.ts @@ -11,8 +11,8 @@ import { Role } from 'app/enums/role.enum'; import { LoggedInUser } from 'app/interfaces/ds-cache.interface'; import { AuthService } from 'app/modules/auth/auth.service'; import { ApiService } from 'app/modules/websocket/api.service'; -import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { TokenLastUsedService } from 'app/services/token-last-used.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; export const dummyUser = { privilege: { @@ -40,13 +40,14 @@ export function mockAuth( provide: AuthService, useFactory: () => { const mockService = new MockAuthService( - createSpyObject(WebSocketHandlerService, { - isConnected$: of(true), - }), createSpyObject(Store), createSpyObject(ApiService), createSpyObject(TokenLastUsedService), createSpyObject(Window), + createSpyObject(WebSocketStatusService, { + isConnected$: of(true), + isAuthenticated$: of(false), + }), ); mockService.setUser(user as LoggedInUser); diff --git a/src/app/modules/auth/auth-guard.service.spec.ts b/src/app/modules/auth/auth-guard.service.spec.ts index 72d67f0ec21..2c67e9eb4b1 100644 --- a/src/app/modules/auth/auth-guard.service.spec.ts +++ b/src/app/modules/auth/auth-guard.service.spec.ts @@ -6,7 +6,7 @@ import { } from '@ngneat/spectator/jest'; import { BehaviorSubject } from 'rxjs'; import { AuthGuardService } from 'app/modules/auth/auth-guard.service'; -import { AuthService } from 'app/modules/auth/auth.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; describe('AuthGuardService', () => { const redirectUrl = 'storage/disks'; @@ -17,7 +17,7 @@ describe('AuthGuardService', () => { let spectator: SpectatorService; const createService = createServiceFactory({ service: AuthGuardService, - providers: [mockProvider(AuthService, { isAuthenticated$ })], + providers: [mockProvider(WebSocketStatusService, { isAuthenticated$ })], }); beforeEach(() => { diff --git a/src/app/modules/auth/auth-guard.service.ts b/src/app/modules/auth/auth-guard.service.ts index 48b98da2782..aa29ba14d1d 100644 --- a/src/app/modules/auth/auth-guard.service.ts +++ b/src/app/modules/auth/auth-guard.service.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { WINDOW } from 'app/helpers/window.helper'; -import { AuthService } from 'app/modules/auth/auth.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; @UntilDestroy() @Injectable({ @@ -12,10 +12,10 @@ export class AuthGuardService { isAuthenticated = false; constructor( private router: Router, - private authService: AuthService, + private wsStatus: WebSocketStatusService, @Inject(WINDOW) private window: Window, ) { - this.authService.isAuthenticated$.pipe(untilDestroyed(this)).subscribe((isLoggedIn) => { + this.wsStatus.isAuthenticated$.pipe(untilDestroyed(this)).subscribe((isLoggedIn) => { this.isAuthenticated = isLoggedIn; }); } diff --git a/src/app/modules/auth/auth.service.spec.ts b/src/app/modules/auth/auth.service.spec.ts index 6a0bc54de51..a51e7c1364c 100644 --- a/src/app/modules/auth/auth.service.spec.ts +++ b/src/app/modules/auth/auth.service.spec.ts @@ -7,7 +7,9 @@ import { StorageStrategyStub, withLocalStorage, } from 'ngx-webstorage'; import * as rxjs from 'rxjs'; -import { firstValueFrom, of } from 'rxjs'; +import { + BehaviorSubject, firstValueFrom, +} from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; import { MockApiService } from 'app/core/testing/classes/mock-api.service'; import { mockCall, mockApi } from 'app/core/testing/utils/mock-api.utils'; @@ -21,7 +23,7 @@ import { LoggedInUser } from 'app/interfaces/ds-cache.interface'; import { Preferences } from 'app/interfaces/preferences.interface'; import { AuthService } from 'app/modules/auth/auth.service'; import { ApiService } from 'app/modules/websocket/api.service'; -import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; const authMeUser = { pw_dir: 'dir', @@ -40,9 +42,12 @@ const authMeUser = { }, } as LoggedInUser; +const mockWsStatus = new WebSocketStatusService(); + describe('AuthService', () => { let spectator: SpectatorService; let testScheduler: TestScheduler; + let timer$: BehaviorSubject<0>; const createService = createServiceFactory({ service: AuthService, providers: [ @@ -59,9 +64,10 @@ describe('AuthService', () => { }, } as LoginExResponse), ]), - mockProvider(WebSocketHandlerService, { - isConnected$: of(true), - }), + { + provide: WebSocketStatusService, + useValue: mockWsStatus, + }, { provide: STORAGE_STRATEGIES, useFactory: () => new StorageStrategyStub(LocalStorageStrategy.strategyName), @@ -78,11 +84,14 @@ describe('AuthService', () => { testScheduler = new TestScheduler((actual, expected) => { expect(actual).toEqual(expected); }); + mockWsStatus.setConnectionStatus(true); + timer$ = new BehaviorSubject(0); + jest.spyOn(rxjs, 'timer').mockReturnValue(timer$.asObservable()); }); describe('Login', () => { it('initializes auth session with triggers and token with username/password login', () => { - jest.spyOn(rxjs, 'timer').mockReturnValueOnce(of(0)); + timer$.next(0); const obs$ = spectator.service.login('dummy', 'dummy'); @@ -91,10 +100,6 @@ describe('AuthService', () => { '(a|)', { a: LoginResult.Success }, ); - expectObservable(spectator.service.isAuthenticated$).toBe( - 'c', - { c: true }, - ); expectObservable(spectator.service.authToken$).toBe( 'd', { d: 'DUMMY_TOKEN' }, @@ -109,7 +114,7 @@ describe('AuthService', () => { }); it('initializes auth session with triggers and token with token login', () => { - jest.spyOn(rxjs, 'timer').mockReturnValueOnce(of(0)); + timer$.next(0); const obs$ = spectator.service.loginWithToken(); @@ -118,10 +123,6 @@ describe('AuthService', () => { '(a|)', { a: LoginResult.Success }, ); - expectObservable(spectator.service.isAuthenticated$).toBe( - 'c', - { c: true }, - ); expectObservable(spectator.service.authToken$).toBe( 'd', { d: 'DUMMY_TOKEN' }, @@ -146,10 +147,6 @@ describe('AuthService', () => { a: undefined, }, ); - expectObservable(spectator.service.isAuthenticated$).toBe( - 'c', - { c: false }, - ); expectObservable(spectator.service.authToken$).toBe( '|', {}, diff --git a/src/app/modules/auth/auth.service.ts b/src/app/modules/auth/auth.service.ts index 33b6d6834a3..71c816b10bd 100644 --- a/src/app/modules/auth/auth.service.ts +++ b/src/app/modules/auth/auth.service.ts @@ -3,7 +3,6 @@ import { Store } from '@ngrx/store'; import { LocalStorage } from 'ngx-webstorage'; import { BehaviorSubject, - combineLatest, filter, map, Observable, @@ -23,8 +22,8 @@ import { LoginExMechanism, LoginExResponse, LoginExResponseType } from 'app/inte import { LoggedInUser } from 'app/interfaces/ds-cache.interface'; import { GlobalTwoFactorConfig } from 'app/interfaces/two-factor-config.interface'; import { ApiService } from 'app/modules/websocket/api.service'; -import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { TokenLastUsedService } from 'app/services/token-last-used.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; import { AppState } from 'app/store'; import { adminUiInitialized } from 'app/store/admin-panel/admin.actions'; @@ -51,19 +50,8 @@ export class AuthService { return Boolean(this.token) && this.token !== 'null'; } - private isLoggedIn$ = new BehaviorSubject(false); - private generateTokenSubscription: Subscription | null; - readonly isAuthenticated$ = combineLatest([ - this.wsManager.isConnected$, - this.isLoggedIn$.asObservable(), - ]).pipe( - switchMap(([isConnected, isLoggedIn]) => { - return of(isConnected && isLoggedIn); - }), - ); - readonly user$ = this.loggedInUser$.asObservable(); /** @@ -82,11 +70,11 @@ export class AuthService { private cachedGlobalTwoFactorConfig: GlobalTwoFactorConfig | null; constructor( - private wsManager: WebSocketHandlerService, private store$: Store, private api: ApiService, private tokenLastUsedService: TokenLastUsedService, @Inject(WINDOW) private window: Window, + private wsStatus: WebSocketStatusService, ) { this.setupAuthenticationUpdate(); this.setupWsConnectionUpdate(); @@ -183,7 +171,7 @@ export class AuthService { tap(() => { this.clearAuthToken(); this.api.clearSubscriptions(); - this.isLoggedIn$.next(false); + this.wsStatus.setLoginStatus(false); }), ); } @@ -202,18 +190,18 @@ export class AuthService { this.loggedInUser$.next(result.user_info); if (!result.user_info?.privilege?.webui_access) { - this.isLoggedIn$.next(false); + this.wsStatus.setLoginStatus(false); return of(LoginResult.NoAccess); } - this.isLoggedIn$.next(true); + this.wsStatus.setLoginStatus(true); this.window.sessionStorage.setItem('loginBannerDismissed', 'true'); return this.authToken$.pipe( take(1), map(() => LoginResult.Success), ); } - this.isLoggedIn$.next(false); + this.wsStatus.setLoginStatus(false); if (result.response_type === LoginExResponseType.OtpRequired) { return of(LoginResult.NoOtp); @@ -226,8 +214,8 @@ export class AuthService { private setupPeriodicTokenGeneration(): void { if (!this.generateTokenSubscription || this.generateTokenSubscription.closed) { this.generateTokenSubscription = timer(0, this.tokenRegenerationTimeMillis).pipe( - switchMap(() => this.isAuthenticated$.pipe(take(1))), - filter((isAuthenticated) => isAuthenticated), + switchMap(() => this.wsStatus.isAuthenticated$.pipe(take(1))), + filter(Boolean), switchMap(() => this.api.call('auth.generate_token')), tap((token) => this.latestTokenGenerated$.next(token)), ).subscribe(); @@ -243,7 +231,7 @@ export class AuthService { } private setupAuthenticationUpdate(): void { - this.isAuthenticated$.subscribe({ + this.wsStatus.isAuthenticated$.subscribe({ next: (isAuthenticated) => { if (isAuthenticated) { this.store$.dispatch(adminUiInitialized()); @@ -260,8 +248,8 @@ export class AuthService { } private setupWsConnectionUpdate(): void { - this.wsManager.isConnected$.pipe(filter((isConnected) => !isConnected)).subscribe(() => { - this.isLoggedIn$.next(false); + this.wsStatus.isConnected$.pipe(filter((isConnected) => !isConnected)).subscribe(() => { + this.wsStatus.setLoginStatus(false); }); } diff --git a/src/app/modules/auth/two-factor-guard.service.spec.ts b/src/app/modules/auth/two-factor-guard.service.spec.ts index 721b59de6fb..3ca8fe323f5 100644 --- a/src/app/modules/auth/two-factor-guard.service.spec.ts +++ b/src/app/modules/auth/two-factor-guard.service.spec.ts @@ -5,6 +5,7 @@ import { GlobalTwoFactorConfig, UserTwoFactorConfig } from 'app/interfaces/two-f import { AuthService } from 'app/modules/auth/auth.service'; import { TwoFactorGuardService } from 'app/modules/auth/two-factor-guard.service'; import { DialogService } from 'app/modules/dialog/dialog.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; describe('TwoFactorGuardService', () => { let spectator: SpectatorService; @@ -18,8 +19,10 @@ describe('TwoFactorGuardService', () => { service: TwoFactorGuardService, providers: [ mockProvider(Router), - mockProvider(AuthService, { + mockProvider(WebSocketStatusService, { isAuthenticated$, + }), + mockProvider(AuthService, { userTwoFactorConfig$, getGlobalTwoFactorConfig, hasRole: jest.fn(() => hasRole$), diff --git a/src/app/modules/auth/two-factor-guard.service.ts b/src/app/modules/auth/two-factor-guard.service.ts index 58ae5ce005f..96b961fad3f 100644 --- a/src/app/modules/auth/two-factor-guard.service.ts +++ b/src/app/modules/auth/two-factor-guard.service.ts @@ -10,6 +10,7 @@ import { import { Role } from 'app/enums/role.enum'; import { AuthService } from 'app/modules/auth/auth.service'; import { DialogService } from 'app/modules/dialog/dialog.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; @UntilDestroy() @Injectable({ @@ -19,12 +20,13 @@ export class TwoFactorGuardService implements CanActivateChild { constructor( private router: Router, private authService: AuthService, + private wsStatus: WebSocketStatusService, private dialogService: DialogService, private translate: TranslateService, ) { } canActivateChild(_: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.authService.isAuthenticated$.pipe( + return this.wsStatus.isAuthenticated$.pipe( take(1), switchMap((isAuthenticated) => { if (!isAuthenticated) { diff --git a/src/app/modules/language/translations/translations-loaded.guard.ts b/src/app/modules/language/translations/translations-loaded.guard.ts index c8de8dba5f4..6cb6d3b18af 100644 --- a/src/app/modules/language/translations/translations-loaded.guard.ts +++ b/src/app/modules/language/translations/translations-loaded.guard.ts @@ -5,7 +5,7 @@ import { catchError, map, timeout, } from 'rxjs/operators'; import { LanguageService } from 'app/modules/language/language.service'; -import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; /** * Ensures that translations have been loaded. @@ -18,9 +18,9 @@ export class TranslationsLoadedGuard { isConnected = false; constructor( private languageService: LanguageService, - private wsHandler: WebSocketHandlerService, + private wsStatus: WebSocketStatusService, ) { - this.wsHandler.isConnected$.pipe(untilDestroyed(this)).subscribe((isConnected) => { + this.wsStatus.isConnected$.pipe(untilDestroyed(this)).subscribe((isConnected) => { this.isConnected = isConnected; }); } diff --git a/src/app/modules/websocket/api.service.ts b/src/app/modules/websocket/api.service.ts index 596e13559ad..ffa3952252e 100644 --- a/src/app/modules/websocket/api.service.ts +++ b/src/app/modules/websocket/api.service.ts @@ -34,6 +34,7 @@ import { import { Job } from 'app/interfaces/job.interface'; import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; @Injectable({ providedIn: 'root', @@ -43,10 +44,11 @@ export class ApiService { constructor( protected wsHandler: WebSocketHandlerService, + protected wsStatus: WebSocketStatusService, protected subscriptionManager: SubscriptionManagerService, protected translate: TranslateService, ) { - this.wsHandler.isConnected$?.subscribe((isConnected) => { + this.wsStatus.isConnected$?.subscribe((isConnected) => { if (!isConnected) { this.clearSubscriptions(); } diff --git a/src/app/modules/websocket/ping.service.spec.ts b/src/app/modules/websocket/ping.service.spec.ts index 95d77e61a13..e451ccb8dd4 100644 --- a/src/app/modules/websocket/ping.service.spec.ts +++ b/src/app/modules/websocket/ping.service.spec.ts @@ -1,9 +1,9 @@ import { discardPeriodicTasks, fakeAsync, tick } from '@angular/core/testing'; import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator/jest'; import { BehaviorSubject, of } from 'rxjs'; -import { AuthService } from 'app/modules/auth/auth.service'; import { PingService } from 'app/modules/websocket/ping.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; describe('PingService', () => { let spectator: SpectatorService; @@ -15,9 +15,9 @@ describe('PingService', () => { providers: [ mockProvider(WebSocketHandlerService, { scheduleCall: jest.fn(), - isConnected$: of(true), }), - mockProvider(AuthService, { + mockProvider(WebSocketStatusService, { + isConnected$: of(true), isAuthenticated$, }), ], diff --git a/src/app/modules/websocket/ping.service.ts b/src/app/modules/websocket/ping.service.ts index fe86feca98c..4298e2a21fd 100644 --- a/src/app/modules/websocket/ping.service.ts +++ b/src/app/modules/websocket/ping.service.ts @@ -4,8 +4,8 @@ import { UUID } from 'angular2-uuid'; import { filter, interval, switchMap, tap, } from 'rxjs'; -import { AuthService } from 'app/modules/auth/auth.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; @UntilDestroy() @Injectable({ @@ -16,14 +16,14 @@ export class PingService { constructor( private wsHandler: WebSocketHandlerService, - private authService: AuthService, + private wsStatus: WebSocketStatusService, ) {} setupPing(): void { interval(this.pingTimeoutMillis).pipe( - switchMap(() => this.wsHandler.isConnected$), + switchMap(() => this.wsStatus.isConnected$), filter(Boolean), - switchMap(() => this.authService.isAuthenticated$), + switchMap(() => this.wsStatus.isAuthenticated$), filter(Boolean), tap(() => this.wsHandler.scheduleCall({ id: UUID.UUID(), diff --git a/src/app/modules/websocket/subscription-manager.service.spec.ts b/src/app/modules/websocket/subscription-manager.service.spec.ts index 9c66b06343a..9a943276166 100644 --- a/src/app/modules/websocket/subscription-manager.service.spec.ts +++ b/src/app/modules/websocket/subscription-manager.service.spec.ts @@ -1,8 +1,9 @@ import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator/jest'; import { UUID } from 'angular2-uuid'; -import { Subject } from 'rxjs'; +import { of, Subject } from 'rxjs'; import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; describe('SubscriptionManagerService', () => { let spectator: SpectatorService; @@ -16,6 +17,12 @@ describe('SubscriptionManagerService', () => { responses$, scheduleCall: jest.fn(), }), + mockProvider(WebSocketStatusService, { + isConnected$: of(true), + isAuthenticated$: of(true), + isConnected: true, + isAuthenticated: true, + }), ], }); diff --git a/src/app/modules/websocket/subscription-manager.service.ts b/src/app/modules/websocket/subscription-manager.service.ts index b8be6932725..59143d46818 100644 --- a/src/app/modules/websocket/subscription-manager.service.ts +++ b/src/app/modules/websocket/subscription-manager.service.ts @@ -6,6 +6,7 @@ import { import { isCollectionUpdateMessage, isSuccessfulResponse } from 'app/helpers/api.helper'; import { ApiEventMethod, ApiEventTyped, CollectionUpdateMessage } from 'app/interfaces/api-message.interface'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; type Method = ApiEventMethod | `${ApiEventMethod}:${string}`; @@ -35,6 +36,7 @@ export class SubscriptionManagerService { private subscriptionsToClose = new Set(); constructor( + private wsStatus: WebSocketStatusService, private wsHandler: WebSocketHandlerService, ) { this.listenForSubscriptionsToBeEstablished(); @@ -122,11 +124,14 @@ export class SubscriptionManagerService { private cancelSubscription(method: Method): void { const backendSubscriptionId = this.establishedSubscriptions.get(method); - this.wsHandler.scheduleCall({ - id: UUID.UUID(), - method: 'core.unsubscribe', - params: [backendSubscriptionId], - }); + const isAuthenticated = this.wsStatus.isAuthenticated; + if (isAuthenticated) { + this.wsHandler.scheduleCall({ + id: UUID.UUID(), + method: 'core.unsubscribe', + params: [backendSubscriptionId], + }); + } this.establishedSubscriptions.delete(method); this.openSubscriptions.delete(method); diff --git a/src/app/modules/websocket/websocket-handler.service.ts b/src/app/modules/websocket/websocket-handler.service.ts index 912b61d875d..cf12840ec5b 100644 --- a/src/app/modules/websocket/websocket-handler.service.ts +++ b/src/app/modules/websocket/websocket-handler.service.ts @@ -17,12 +17,15 @@ import { timer, } from 'rxjs'; import { webSocket as rxjsWebSocket } from 'rxjs/webSocket'; -import { makeRequestMessage } from 'app/helpers/api.helper'; +import { isErrorResponse, makeRequestMessage } from 'app/helpers/api.helper'; import { WEBSOCKET } from 'app/helpers/websocket.helper'; import { WINDOW } from 'app/helpers/window.helper'; -import { RequestMessage, IncomingMessage } from 'app/interfaces/api-message.interface'; +import { + RequestMessage, IncomingMessage, +} from 'app/interfaces/api-message.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { WebSocketConnection } from 'app/modules/websocket/websocket-connection.class'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; type ApiCall = Required>; @@ -34,9 +37,6 @@ export class WebSocketHandlerService { private readonly wsConnection: WebSocketConnection = new WebSocketConnection(this.webSocket); private connectionUrl = (this.window.location.protocol === 'https:' ? 'wss://' : 'ws://') + environment.remote + '/api/current'; - private readonly connectionEstablished$ = new BehaviorSubject(false); - readonly isConnected$ = this.connectionEstablished$.asObservable(); - private readonly reconnectTimeoutMillis = 5 * 1000; private reconnectTimerSubscription: Subscription | undefined; private readonly maxConcurrentCalls = 20; @@ -71,9 +71,8 @@ export class WebSocketHandlerService { private showingConcurrentCallsError = false; private callsInConcurrentCallsError = new Set(); - private subscriptionIds: Record = {}; - constructor( + private wsStatus: WebSocketStatusService, private dialogService: DialogService, private translate: TranslateService, @Inject(WINDOW) protected window: Window, @@ -90,7 +89,7 @@ export class WebSocketHandlerService { private setupScheduledCalls(): void { combineLatest([ this.triggerNextCall$, - this.isConnected$, + this.wsStatus.isConnected$, ]).pipe( filter(([, isConnected]) => isConnected), tap(() => { @@ -114,7 +113,11 @@ export class WebSocketHandlerService { return this.responses$.pipe( filter((message) => 'id' in message && message.id === call.id), take(1), - tap(() => { + tap((message) => { + // Following `if` block needs to be removed once NAS-131829 is resolved + if (isErrorResponse(message)) { + console.error('Error: ', message.error); + } this.activeCalls--; this.pendingCalls.delete(call.id); this.triggerNextCall$.next(); @@ -178,7 +181,7 @@ export class WebSocketHandlerService { } private onClose(event: CloseEvent): void { - this.connectionEstablished$.next(false); + this.wsStatus.setConnectionStatus(false); this.isConnectionLive$.next(false); if (this.reconnectTimerSubscription) { return; @@ -203,7 +206,7 @@ export class WebSocketHandlerService { return; } this.shutDownInProgress = false; - this.connectionEstablished$.next(true); + this.wsStatus.setConnectionStatus(true); performance.mark('WS Connected'); performance.measure('Establishing WS connection', 'WS Init', 'WS Connected'); diff --git a/src/app/pages/credentials/users/store/user.effects.ts b/src/app/pages/credentials/users/store/user.effects.ts index b33edebf2d1..58179cd50bc 100644 --- a/src/app/pages/credentials/users/store/user.effects.ts +++ b/src/app/pages/credentials/users/store/user.effects.ts @@ -5,6 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { of } from 'rxjs'; import { catchError, filter, map, switchMap, + take, } from 'rxjs/operators'; import { CollectionChangeType } from 'app/enums/api.enum'; import { QueryParams } from 'app/interfaces/query-api.interface'; @@ -24,7 +25,7 @@ import { waitForPreferences } from 'app/store/preferences/preferences.selectors' export class UserEffects { loadUsers$ = createEffect(() => this.actions$.pipe( ofType(userPageEntered, builtinUsersToggled), - switchMap(() => this.store$.pipe(waitForPreferences)), + switchMap(() => this.store$.pipe(waitForPreferences, take(1))), switchMap((preferences) => { let params: QueryParams = []; if (preferences.hideBuiltinUsers) { diff --git a/src/app/pages/signin/signin.component.spec.ts b/src/app/pages/signin/signin.component.spec.ts index 2e7a08cc350..2dd8fef9e1e 100644 --- a/src/app/pages/signin/signin.component.spec.ts +++ b/src/app/pages/signin/signin.component.spec.ts @@ -4,7 +4,6 @@ import { BehaviorSubject, of } from 'rxjs'; import { AuthService } from 'app/modules/auth/auth.service'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { CopyrightLineComponent } from 'app/modules/layout/copyright-line/copyright-line.component'; -import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { DisconnectedMessageComponent, } from 'app/pages/signin/disconnected-message/disconnected-message.component'; @@ -18,6 +17,7 @@ import { TrueCommandStatusComponent, } from 'app/pages/signin/true-command-status/true-command-status.component'; import { TokenLastUsedService } from 'app/services/token-last-used.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; describe('SigninComponent', () => { let spectator: Spectator; @@ -58,7 +58,7 @@ describe('SigninComponent', () => { mockProvider(TokenLastUsedService, { isTokenWithinTimeline$, }), - mockProvider(WebSocketHandlerService, { + mockProvider(WebSocketStatusService, { isConnected$, }), ], diff --git a/src/app/pages/signin/signin.component.ts b/src/app/pages/signin/signin.component.ts index c15315f0238..3b65b39a2db 100644 --- a/src/app/pages/signin/signin.component.ts +++ b/src/app/pages/signin/signin.component.ts @@ -17,13 +17,13 @@ import { AuthService } from 'app/modules/auth/auth.service'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { IxIconComponent } from 'app/modules/ix-icon/ix-icon.component'; import { CopyrightLineComponent } from 'app/modules/layout/copyright-line/copyright-line.component'; -import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { DisconnectedMessageComponent } from 'app/pages/signin/disconnected-message/disconnected-message.component'; import { SetAdminPasswordFormComponent } from 'app/pages/signin/set-admin-password-form/set-admin-password-form.component'; import { SigninFormComponent } from 'app/pages/signin/signin-form/signin-form.component'; import { SigninStore } from 'app/pages/signin/store/signin.store'; import { TrueCommandStatusComponent } from 'app/pages/signin/true-command-status/true-command-status.component'; import { TokenLastUsedService } from 'app/services/token-last-used.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; @UntilDestroy() @Component({ @@ -53,7 +53,7 @@ export class SigninComponent implements OnInit { readonly wasAdminSet$ = this.signinStore.wasAdminSet$; readonly canLogin$ = this.signinStore.canLogin$; - readonly isConnected$ = this.wsManager.isConnected$; + readonly isConnected$ = this.wsStatus.isConnected$; isConnectedDelayed$: Observable = of(null).pipe( delay(1000), switchMap(() => this.isConnected$), @@ -70,7 +70,7 @@ export class SigninComponent implements OnInit { ); constructor( - private wsManager: WebSocketHandlerService, + private wsStatus: WebSocketStatusService, private signinStore: SigninStore, private dialog: DialogService, private authService: AuthService, diff --git a/src/app/pages/signin/store/signin.store.spec.ts b/src/app/pages/signin/store/signin.store.spec.ts index d470a65ffdf..e0b91f5e330 100644 --- a/src/app/pages/signin/store/signin.store.spec.ts +++ b/src/app/pages/signin/store/signin.store.spec.ts @@ -15,6 +15,7 @@ import { SigninStore } from 'app/pages/signin/store/signin.store'; import { SystemGeneralService } from 'app/services/system-general.service'; import { TokenLastUsedService } from 'app/services/token-last-used.service'; import { UpdateService } from 'app/services/update.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; describe('SigninStore', () => { let spectator: SpectatorService; @@ -31,8 +32,10 @@ describe('SigninStore', () => { mockCall('auth.twofactor.config', { enabled: false } as GlobalTwoFactorConfig), mockCall('system.advanced.login_banner', ''), ]), - mockProvider(WebSocketHandlerService, { + mockProvider(WebSocketStatusService, { isConnected$: of(true), + }), + mockProvider(WebSocketHandlerService, { responses$: of(), }), mockProvider(TokenLastUsedService, { diff --git a/src/app/pages/signin/store/signin.store.ts b/src/app/pages/signin/store/signin.store.ts index 211c0cf4e11..3a59c54190c 100644 --- a/src/app/pages/signin/store/signin.store.ts +++ b/src/app/pages/signin/store/signin.store.ts @@ -22,6 +22,7 @@ import { ErrorHandlerService } from 'app/services/error-handler.service'; import { SystemGeneralService } from 'app/services/system-general.service'; import { TokenLastUsedService } from 'app/services/token-last-used.service'; import { UpdateService } from 'app/services/update.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; import { loginBannerUpdated } from 'app/store/system-config/system-config.actions'; interface SigninState { @@ -43,7 +44,7 @@ export class SigninStore extends ComponentStore { wasAdminSet$ = this.select((state) => state.wasAdminSet); isLoading$ = this.select((state) => state.isLoading); - canLogin$ = this.wsManager.isConnected$; + canLogin$ = this.wsStatus.isConnected$; private handleLoginResult = (loginResult: LoginResult): void => { if (loginResult !== LoginResult.Success) { @@ -66,6 +67,7 @@ export class SigninStore extends ComponentStore { private authService: AuthService, private updateService: UpdateService, private actions$: Actions, + private wsStatus: WebSocketStatusService, private activatedRoute: ActivatedRoute, @Inject(WINDOW) private window: Window, ) { diff --git a/src/app/pages/system-tasks/config-reset/config-reset.component.spec.ts b/src/app/pages/system-tasks/config-reset/config-reset.component.spec.ts index d0ffcef1c88..883f35eb513 100644 --- a/src/app/pages/system-tasks/config-reset/config-reset.component.spec.ts +++ b/src/app/pages/system-tasks/config-reset/config-reset.component.spec.ts @@ -13,6 +13,7 @@ import { CopyrightLineComponent } from 'app/modules/layout/copyright-line/copyri import { ApiService } from 'app/modules/websocket/api.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { ConfigResetComponent } from 'app/pages/system-tasks/config-reset/config-reset.component'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; describe('ConfigResetComponent', () => { let spectator: Spectator; @@ -31,9 +32,11 @@ describe('ConfigResetComponent', () => { mockProvider(MatDialog), mockProvider(Location), mockProvider(WebSocketHandlerService, { - isConnected$, prepareShutdown: jest.fn(), }), + mockProvider(WebSocketStatusService, { + isConnected$, + }), mockProvider(DialogService, { jobDialog: jest.fn(() => ({ afterClosed: () => of({}), diff --git a/src/app/pages/system-tasks/config-reset/config-reset.component.ts b/src/app/pages/system-tasks/config-reset/config-reset.component.ts index f8094f25504..5b77f69caaa 100644 --- a/src/app/pages/system-tasks/config-reset/config-reset.component.ts +++ b/src/app/pages/system-tasks/config-reset/config-reset.component.ts @@ -15,6 +15,7 @@ import { AppLoaderService } from 'app/modules/loader/app-loader.service'; import { ApiService } from 'app/modules/websocket/api.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { ErrorHandlerService } from 'app/services/error-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; @UntilDestroy() @Component({ @@ -35,6 +36,7 @@ export class ConfigResetComponent implements OnInit, OnDestroy { constructor( private wsManager: WebSocketHandlerService, + private wsStatus: WebSocketStatusService, protected router: Router, protected loader: AppLoaderService, private errorHandler: ErrorHandlerService, @@ -47,7 +49,7 @@ export class ConfigResetComponent implements OnInit, OnDestroy { isWsConnected(): void { // TODO: isConnected$ doesn't work correctly. - this.wsManager.isConnected$.pipe(untilDestroyed(this)).subscribe({ + this.wsStatus.isConnected$.pipe(untilDestroyed(this)).subscribe({ next: (isConnected) => { if (isConnected) { this.loader.close(); diff --git a/src/app/pages/system-tasks/failover/failover.component.ts b/src/app/pages/system-tasks/failover/failover.component.ts index 73f376d7ab9..1d87148bde3 100644 --- a/src/app/pages/system-tasks/failover/failover.component.ts +++ b/src/app/pages/system-tasks/failover/failover.component.ts @@ -14,6 +14,7 @@ import { AppLoaderService } from 'app/modules/loader/app-loader.service'; import { ApiService } from 'app/modules/websocket/api.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { ErrorHandlerService } from 'app/services/error-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; import { passiveNodeReplaced } from 'app/store/system-info/system-info.actions'; @UntilDestroy() @@ -36,6 +37,7 @@ export class FailoverComponent implements OnInit { protected api: ApiService, private errorHandler: ErrorHandlerService, private wsManager: WebSocketHandlerService, + private wsStatus: WebSocketStatusService, protected router: Router, protected loader: AppLoaderService, protected dialogService: DialogService, @@ -45,7 +47,7 @@ export class FailoverComponent implements OnInit { ) {} isWsConnected(): void { - this.wsManager.isConnected$.pipe(untilDestroyed(this)).subscribe({ + this.wsStatus.isConnected$.pipe(untilDestroyed(this)).subscribe({ next: (isConnected) => { if (isConnected) { this.loader.close(); diff --git a/src/app/pages/system/failover-settings/failover-settings.component.spec.ts b/src/app/pages/system/failover-settings/failover-settings.component.spec.ts index 649364de111..4d1367ba8cb 100644 --- a/src/app/pages/system/failover-settings/failover-settings.component.spec.ts +++ b/src/app/pages/system/failover-settings/failover-settings.component.spec.ts @@ -13,8 +13,8 @@ import { IxFormHarness } from 'app/modules/forms/ix-forms/testing/ix-form.harnes import { SearchInput1Component } from 'app/modules/forms/search-input1/search-input1.component'; import { SnackbarService } from 'app/modules/snackbar/services/snackbar.service'; import { ApiService } from 'app/modules/websocket/api.service'; -import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { FailoverSettingsComponent } from 'app/pages/system/failover-settings/failover-settings.component'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; describe('FailoverComponent', () => { let spectator: Spectator; @@ -41,7 +41,7 @@ describe('FailoverComponent', () => { }), ]), mockProvider(SnackbarService), - mockProvider(WebSocketHandlerService, { + mockProvider(WebSocketStatusService, { isConnected$: of(true), }), ], diff --git a/src/app/pages/system/general-settings/gui/gui-form/gui-form.component.spec.ts b/src/app/pages/system/general-settings/gui/gui-form/gui-form.component.spec.ts index c191d66b0a8..67428af99d2 100644 --- a/src/app/pages/system/general-settings/gui/gui-form/gui-form.component.spec.ts +++ b/src/app/pages/system/general-settings/gui/gui-form/gui-form.component.spec.ts @@ -23,6 +23,7 @@ import { ApiService } from 'app/modules/websocket/api.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { GuiFormComponent } from 'app/pages/system/general-settings/gui/gui-form/gui-form.component'; import { SystemGeneralService } from 'app/services/system-general.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; import { themeChangedInGuiForm } from 'app/store/preferences/preferences.actions'; import { selectPreferences, selectTheme } from 'app/store/preferences/preferences.selectors'; import { selectGeneralConfig } from 'app/store/system-config/system-config.selectors'; @@ -69,6 +70,7 @@ describe('GuiFormComponent', () => { slideInClosed$: of(), }), mockProvider(WebSocketHandlerService), + mockProvider(WebSocketStatusService), mockProvider(DialogService, { confirm: jest.fn(() => of(true)), }), @@ -166,8 +168,8 @@ describe('GuiFormComponent', () => { }); it('shows confirm dialog if HTTPS redirect is enabled', async () => { - const websocketManager = spectator.inject(WebSocketHandlerService); - Object.defineProperty(websocketManager, 'isConnected$', { + const wsStatus = spectator.inject(WebSocketStatusService); + Object.defineProperty(wsStatus, 'isConnected$', { get: jest.fn(() => new BehaviorSubject(true)), }); @@ -186,8 +188,8 @@ describe('GuiFormComponent', () => { }); it('shows confirm dialog if service restart is needed and restarts it', async () => { - const websocketManager = spectator.inject(WebSocketHandlerService); - Object.defineProperty(websocketManager, 'isConnected$', { + const wsStatus = spectator.inject(WebSocketStatusService); + Object.defineProperty(wsStatus, 'isConnected$', { get: jest.fn(() => new BehaviorSubject(true)), }); diff --git a/src/app/pages/system/general-settings/gui/gui-form/gui-form.component.ts b/src/app/pages/system/general-settings/gui/gui-form/gui-form.component.ts index 2f94573952f..033afd16ece 100644 --- a/src/app/pages/system/general-settings/gui/gui-form/gui-form.component.ts +++ b/src/app/pages/system/general-settings/gui/gui-form/gui-form.component.ts @@ -34,6 +34,7 @@ import { ApiService } from 'app/modules/websocket/api.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { ErrorHandlerService } from 'app/services/error-handler.service'; import { SystemGeneralService } from 'app/services/system-general.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; import { AppState } from 'app/store'; import { guiFormSubmitted, themeChangedInGuiForm } from 'app/store/preferences/preferences.actions'; import { waitForPreferences } from 'app/store/preferences/preferences.selectors'; @@ -97,6 +98,7 @@ export class GuiFormComponent { private cdr: ChangeDetectorRef, private api: ApiService, private wsManager: WebSocketHandlerService, + private wsStatus: WebSocketStatusService, private dialog: DialogService, private loader: AppLoaderService, private translate: TranslateService, @@ -110,7 +112,7 @@ export class GuiFormComponent { } replaceHrefWhenWsConnected(href: string): void { - this.wsManager.isConnected$.pipe(untilDestroyed(this)).subscribe((isConnected) => { + this.wsStatus.isConnected$.pipe(untilDestroyed(this)).subscribe((isConnected) => { if (isConnected) { this.loader.close(); this.window.location.replace(href); diff --git a/src/app/pages/system/update/components/manual-update-form/manual-update-form.component.spec.ts b/src/app/pages/system/update/components/manual-update-form/manual-update-form.component.spec.ts index caafdc84c52..f2a5e75c640 100644 --- a/src/app/pages/system/update/components/manual-update-form/manual-update-form.component.spec.ts +++ b/src/app/pages/system/update/components/manual-update-form/manual-update-form.component.spec.ts @@ -16,7 +16,6 @@ import { Preferences } from 'app/interfaces/preferences.interface'; import { SystemInfo } from 'app/interfaces/system-info.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { IxSelectHarness } from 'app/modules/forms/ix-forms/components/ix-select/ix-select.harness'; -import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { ManualUpdateFormComponent } from 'app/pages/system/update/components/manual-update-form/manual-update-form.component'; import { SystemGeneralService } from 'app/services/system-general.service'; import { selectIsHaLicensed } from 'app/store/ha-info/ha-info.selectors'; @@ -58,9 +57,6 @@ describe('ManualUpdateFormComponent', () => { getItem: () => ProductType.ScaleEnterprise, }, }), - mockProvider(WebSocketHandlerService, { - isConnected$: of(true), - }), provideMockStore({ selectors: [ { diff --git a/src/app/services/websocket-status.service.spec.ts b/src/app/services/websocket-status.service.spec.ts new file mode 100644 index 00000000000..63df9903a47 --- /dev/null +++ b/src/app/services/websocket-status.service.spec.ts @@ -0,0 +1,104 @@ +import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; +import { TestScheduler } from 'rxjs/testing'; +import { getTestScheduler } from 'app/core/testing/utils/get-test-scheduler.utils'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; + +describe('WebSocketStatusSerice', () => { + let spectator: SpectatorService; + let testScheduler: TestScheduler; + + const createService = createServiceFactory({ + service: WebSocketStatusService, + }); + + beforeEach(() => { + spectator = createService(); + testScheduler = getTestScheduler(); + }); + + it('starts with false statuses', () => { + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isAuthenticated$).toBe('a', { a: false }); + }); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isConnected$).toBe('a', { a: false }); + }); + expect(spectator.service.isAuthenticated).toBe(false); + expect(spectator.service.isConnected).toBe(false); + }); + + it('updates connection status', () => { + spectator.service.setConnectionStatus(true); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isAuthenticated$).toBe('a', { a: false }); + }); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isConnected$).toBe('a', { a: true }); + }); + expect(spectator.service.isAuthenticated).toBe(false); + expect(spectator.service.isConnected).toBe(true); + }); + + it('updates login status and auth status also updates', () => { + spectator.service.setConnectionStatus(true); + spectator.service.setLoginStatus(true); + + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isAuthenticated$).toBe('a', { a: true }); + }); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isConnected$).toBe('a', { a: true }); + }); + expect(spectator.service.isAuthenticated).toBe(true); + expect(spectator.service.isConnected).toBe(true); + }); + + it('reverses auth status when logout', () => { + spectator.service.setConnectionStatus(true); + spectator.service.setLoginStatus(true); + + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isAuthenticated$).toBe('a', { a: true }); + }); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isConnected$).toBe('a', { a: true }); + }); + expect(spectator.service.isAuthenticated).toBe(true); + expect(spectator.service.isConnected).toBe(true); + + spectator.service.setLoginStatus(false); + + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isAuthenticated$).toBe('a', { a: false }); + }); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isConnected$).toBe('a', { a: true }); + }); + expect(spectator.service.isAuthenticated).toBe(false); + expect(spectator.service.isConnected).toBe(true); + }); + + it('reverses auth status when connection closes', () => { + spectator.service.setConnectionStatus(true); + spectator.service.setLoginStatus(true); + + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isAuthenticated$).toBe('a', { a: true }); + }); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isConnected$).toBe('a', { a: true }); + }); + expect(spectator.service.isAuthenticated).toBe(true); + expect(spectator.service.isConnected).toBe(true); + + spectator.service.setConnectionStatus(false); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isAuthenticated$).toBe('a', { a: false }); + }); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.isConnected$).toBe('a', { a: false }); + }); + expect(spectator.service.isAuthenticated).toBe(false); + expect(spectator.service.isConnected).toBe(false); + }); +}); diff --git a/src/app/services/websocket-status.service.ts b/src/app/services/websocket-status.service.ts new file mode 100644 index 00000000000..2866c4dc26d --- /dev/null +++ b/src/app/services/websocket-status.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { + BehaviorSubject, combineLatest, + tap, +} from 'rxjs'; + +@UntilDestroy() +@Injectable({ + providedIn: 'root', +}) +export class WebSocketStatusService { + private readonly connectionEstablished$ = new BehaviorSubject(false); + readonly isConnected$ = this.connectionEstablished$.asObservable(); + + get isConnected(): boolean { + return this.connectionEstablished$.getValue(); + } + + private readonly isLoggedIn$ = new BehaviorSubject(false); + private readonly authStatus$ = new BehaviorSubject(false); + readonly isAuthenticated$ = this.authStatus$.asObservable(); + get isAuthenticated(): boolean { + return this.authStatus$.getValue(); + } + + constructor() { + combineLatest([ + this.isConnected$, + this.isLoggedIn$.asObservable(), + ]).pipe( + tap(([isConnected, isLoggedIn]) => this.authStatus$.next(isConnected && isLoggedIn)), + untilDestroyed(this), + ).subscribe(); + } + + setLoginStatus(isLoggedIn: boolean): void { + this.isLoggedIn$.next(isLoggedIn); + } + + setConnectionStatus(connected: boolean): void { + this.connectionEstablished$.next(connected); + } +} diff --git a/src/app/store/preferences/preferences.effects.ts b/src/app/store/preferences/preferences.effects.ts index 6cf8eb75b0f..059fbf27f6e 100644 --- a/src/app/store/preferences/preferences.effects.ts +++ b/src/app/store/preferences/preferences.effects.ts @@ -1,9 +1,10 @@ import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; -import { EMPTY, throwError } from 'rxjs'; +import { isEqual } from 'lodash'; +import { EMPTY } from 'rxjs'; import { - catchError, filter, map, mergeMap, switchMap, withLatestFrom, + catchError, filter, map, mergeMap, pairwise, switchMap, take, withLatestFrom, } from 'rxjs/operators'; import { AuthService } from 'app/modules/auth/auth.service'; import { ApiService } from 'app/modules/websocket/api.service'; @@ -18,7 +19,7 @@ import { themeNotFound, updateRebootAfterManualUpdate, } from 'app/store/preferences/preferences.actions'; -import { selectPreferencesState } from 'app/store/preferences/preferences.selectors'; +import { waitForPreferences } from 'app/store/preferences/preferences.selectors'; import { sidenavUpdated } from 'app/store/topbar/topbar.actions'; import { snapshotExtraColumnsToggled, dashboardStateLoaded, noPreferencesFound, noDashboardStateFound, @@ -71,13 +72,10 @@ export class PreferencesEffects { updateRebootAfterManualUpdate, autoRefreshReportsToggled, ), - withLatestFrom(this.store$.select(selectPreferencesState)), - switchMap(([, state]) => { - if (!state.areLoaded) { - return throwError(() => new Error('Attempting to save user preferences before they were loaded.')); - } - - return this.api.call('auth.set_attribute', ['preferences', state.preferences]); + withLatestFrom(this.store$.pipe(waitForPreferences, take(1), pairwise())), + filter(([, [prevPrefs, newPrefs]]) => !isEqual(prevPrefs, newPrefs)), + switchMap(([, preferences]) => { + return this.api.call('auth.set_attribute', ['preferences', preferences]); }), ), { dispatch: false }); diff --git a/src/main.ts b/src/main.ts index 598ec9c99a6..ba118c9e2ce 100644 --- a/src/main.ts +++ b/src/main.ts @@ -42,6 +42,7 @@ import { ApiService } from 'app/modules/websocket/api.service'; import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service'; import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service'; import { ErrorHandlerService } from 'app/services/error-handler.service'; +import { WebSocketStatusService } from 'app/services/websocket-status.service'; import { rootReducers, rootEffects } from 'app/store'; import { CustomRouterStateSerializer } from 'app/store/router/custom-router-serializer'; @@ -117,16 +118,17 @@ bootstrapApplication(AppComponent, { }, { provide: ApiService, - deps: [WebSocketHandlerService, SubscriptionManagerService, TranslateService], + deps: [WebSocketStatusService, WebSocketHandlerService, SubscriptionManagerService, TranslateService], useFactory: ( + wsStatus: WebSocketStatusService, connection: WebSocketHandlerService, subscriptionManager: SubscriptionManagerService, translate: TranslateService, ) => { if (environment.mockConfig.enabled) { - return new MockEnclosureApiService(connection, subscriptionManager, translate); + return new MockEnclosureApiService(connection, wsStatus, subscriptionManager, translate); } - return new ApiService(connection, subscriptionManager, translate); + return new ApiService(connection, wsStatus, subscriptionManager, translate); }, }, provideCharts(withDefaultRegisterables()), From d07b24fed591c1e5278d0e1516d06efc0cda3151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sall=C3=A9?= Date: Mon, 6 Jan 2025 09:49:18 +0100 Subject: [PATCH 07/18] Add translate pipe for hints (#11287) Changes: Add translate pipe for hints Testing: Check if hints translated properly in another language. --- .../components/instance-wizard/instance-wizard.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.html b/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.html index e1ed8a4287a..f224bfec1a2 100644 --- a/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.html +++ b/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.html @@ -41,7 +41,7 @@ From 4e63ed362b373d40769e0437294de0bc3599fdba Mon Sep 17 00:00:00 2001 From: Alex Karpov Date: Mon, 6 Jan 2025 12:20:08 +0200 Subject: [PATCH 08/18] NAS-133325 / 25.04 / Migrate more instances of OldSlideInService #4 (#11278) * NAS-133300: Migrate more instances of OldSlideInService * NAS-133300: NAS-133317: Migrate more instances of OldSlideInService #2 * NAS-133319: Migrate more instances of OldSlideInService #3 * NAS-133319: PR update * NAS-133319: Skip * NAS-133325: Migrate more instances of OldSlideInService #4 * NAS-133325: PR Update * NAS-133325: PR update --- src/app/modules/slide-ins/slide-in.ts | 10 + .../app-wizard/app-wizard.component.spec.ts | 16 +- .../utils/dataset-form.service.spec.ts | 11 +- .../utils/dataset-form.service.ts | 8 +- .../zvol-form/zvol-form.component.spec.ts | 1 - .../authorized-access-form.component.html | 6 +- .../authorized-access-form.component.spec.ts | 25 ++- .../authorized-access-form.component.ts | 19 +- .../authorized-access-list.component.spec.ts | 20 +- .../authorized-access-list.component.ts | 19 +- .../extent-form/extent-form.component.html | 6 +- .../extent-form/extent-form.component.spec.ts | 25 ++- .../extent-form/extent-form.component.ts | 19 +- .../extent-list/extent-list.component.spec.ts | 20 +- .../extent-list/extent-list.component.ts | 20 +- .../portal-form/portal-form.component.html | 8 +- .../portal-form/portal-form.component.spec.ts | 37 ++-- .../portal-form/portal-form.component.ts | 20 +- .../portal-list/portal-list.component.spec.ts | 20 +- .../portal-list/portal-list.component.ts | 18 +- .../import-pool/import-pool.component.html | 4 +- .../import-pool/import-pool.component.spec.ts | 10 +- .../import-pool/import-pool.component.ts | 10 +- .../disk-info-card.component.spec.ts | 40 ++-- .../disk-info-card.component.ts | 10 +- .../disk-bulk-edit.component.html | 4 +- .../disk-bulk-edit.component.spec.ts | 15 +- .../disk-bulk-edit.component.ts | 18 +- .../disk-form/disk-form.component.html | 4 +- .../disk-form/disk-form.component.spec.ts | 16 +- .../disk-form/disk-form.component.ts | 11 +- .../disk-list/disk-list.component.spec.ts | 16 +- .../disk-list/disk-list.component.ts | 20 +- .../storage/pools-dashboard.component.ts | 10 +- .../alert-service-list.component.spec.ts | 12 +- .../alert-service-list.component.ts | 16 +- .../alert-service.component.html | 4 +- .../alert-service.component.spec.ts | 27 +-- .../alert-service/alert-service.component.ts | 16 +- .../bootenv-form/bootenv-form.component.html | 4 +- .../bootenv-form.component.spec.ts | 25 ++- .../bootenv-form/bootenv-form.component.ts | 24 +-- .../bootenv-list.component.spec.ts | 9 +- .../bootenv-list/bootenv-list.component.ts | 18 +- .../jbof-form/jbof-form.component.html | 7 +- .../jbof-form/jbof-form.component.spec.ts | 43 +++-- .../jbof-form/jbof-form.component.ts | 19 +- .../jbof-list/jbof-list.component.spec.ts | 11 +- .../jbof-list/jbof-list.component.ts | 10 +- .../email-card/email-card.component.spec.ts | 14 +- .../email/email-card/email-card.component.ts | 11 +- .../email-form/email-form.component.html | 4 +- .../email-form/email-form.component.spec.ts | 16 +- .../email/email-form/email-form.component.ts | 19 +- .../gui/gui-card/gui-card.component.spec.ts | 8 +- .../gui/gui-card/gui-card.component.ts | 9 +- .../gui/gui-form/gui-form.component.html | 5 +- .../gui/gui-form/gui-form.component.spec.ts | 12 +- .../gui/gui-form/gui-form.component.ts | 15 +- .../localization-card.component.spec.ts | 8 +- .../localization-card.component.ts | 6 +- .../localization-form.component.html | 4 +- .../localization-form.component.spec.ts | 44 ++--- .../localization-form.component.ts | 17 +- .../ntp-server-card.component.spec.ts | 13 +- .../ntp-server-card.component.ts | 16 +- .../ntp-server-form.component.html | 6 +- .../ntp-server-form.component.spec.ts | 42 +++-- .../ntp-server-form.component.ts | 19 +- .../support/license/license.component.html | 7 +- .../support/license/license.component.spec.ts | 20 +- .../support/license/license.component.ts | 10 +- .../proactive/proactive.component.html | 4 +- .../proactive/proactive.component.scss | 4 + .../proactive/proactive.component.spec.ts | 21 ++- .../support/proactive/proactive.component.ts | 10 +- .../support-card/support-card.component.ts | 8 +- .../device-form/device-form.component.html | 7 +- .../device-form/device-form.component.spec.ts | 175 +++++++----------- .../device-form/device-form.component.ts | 23 +-- .../device-list/device-list.component.spec.ts | 10 +- .../device-list/device-list.component.ts | 31 ++-- src/app/pages/vm/vm-list.component.spec.ts | 11 +- src/app/pages/vm/vm-list.component.ts | 13 +- .../vm/vm-wizard/vm-wizard.component.html | 4 +- .../vm/vm-wizard/vm-wizard.component.spec.ts | 20 +- .../pages/vm/vm-wizard/vm-wizard.component.ts | 10 +- 87 files changed, 777 insertions(+), 660 deletions(-) diff --git a/src/app/modules/slide-ins/slide-in.ts b/src/app/modules/slide-ins/slide-in.ts index 7c2e03217eb..fcbf7e7bb22 100644 --- a/src/app/modules/slide-ins/slide-in.ts +++ b/src/app/modules/slide-ins/slide-in.ts @@ -82,6 +82,16 @@ export class SlideIn extends ComponentStore { }; }); + closeLast = this.updater((state) => { + const newMap = new Map(state.components); + const { id } = this.getAliveComponents(newMap).pop(); + newMap.set(id, { ...newMap.get(id), isComponentAlive: false }); + this.focusOnTheCloseButton(); + return { + components: newMap, + }; + }); + closeAll = this.updater(() => { this.focusOnTheCloseButton(); return { diff --git a/src/app/pages/apps/components/app-wizard/app-wizard.component.spec.ts b/src/app/pages/apps/components/app-wizard/app-wizard.component.spec.ts index c1eb03c0baf..be88bceebb8 100644 --- a/src/app/pages/apps/components/app-wizard/app-wizard.component.spec.ts +++ b/src/app/pages/apps/components/app-wizard/app-wizard.component.spec.ts @@ -16,14 +16,13 @@ import { DialogService } from 'app/modules/dialog/dialog.service'; import { IxInputHarness } from 'app/modules/forms/ix-forms/components/ix-input/ix-input.harness'; import { IxFormHarness } from 'app/modules/forms/ix-forms/testing/ix-form.harness'; import { PageHeaderComponent } from 'app/modules/page-header/page-title-header/page-header.component'; -import { OldSlideInRef } from 'app/modules/slide-ins/old-slide-in-ref'; -import { SLIDE_IN_DATA } from 'app/modules/slide-ins/slide-in.token'; +import { SlideIn } from 'app/modules/slide-ins/slide-in'; +import { SlideInRef } from 'app/modules/slide-ins/slide-in-ref'; import { ApiService } from 'app/modules/websocket/api.service'; import { AppWizardComponent } from 'app/pages/apps/components/app-wizard/app-wizard.component'; import { DockerHubRateInfoDialogComponent } from 'app/pages/apps/components/dockerhub-rate-limit-info-dialog/dockerhub-rate-limit-info-dialog.component'; import { ApplicationsService } from 'app/pages/apps/services/applications.service'; import { DockerStore } from 'app/pages/apps/store/docker.store'; -import { OldSlideInService } from 'app/services/old-slide-in.service'; const appVersion121 = { healthy: true, @@ -270,6 +269,12 @@ describe('AppWizardComponent', () => { }, } as App; + const slideInRef: SlideInRef = { + close: jest.fn(), + requireConfirmationWhen: jest.fn(), + getData: jest.fn(() => undefined), + }; + const createComponent = createComponentFactory({ component: AppWizardComponent, imports: [ @@ -281,7 +286,7 @@ describe('AppWizardComponent', () => { mockProvider(MatDialog), ], providers: [ - mockProvider(OldSlideInService), + mockProvider(SlideIn), mockProvider(DialogService, { jobDialog: jest.fn(() => ({ afterClosed: () => of({}), @@ -308,10 +313,9 @@ describe('AppWizardComponent', () => { mockProvider(DockerStore, { selectedPool$: of('pool set'), }), - mockProvider(OldSlideInRef), + mockProvider(SlideInRef, slideInRef), mockProvider(Router), mockAuth(), - { provide: SLIDE_IN_DATA, useValue: undefined }, ], }); diff --git a/src/app/pages/datasets/components/dataset-form/utils/dataset-form.service.spec.ts b/src/app/pages/datasets/components/dataset-form/utils/dataset-form.service.spec.ts index fbeac6c8926..acec4e4b7b8 100644 --- a/src/app/pages/datasets/components/dataset-form/utils/dataset-form.service.spec.ts +++ b/src/app/pages/datasets/components/dataset-form/utils/dataset-form.service.spec.ts @@ -6,9 +6,9 @@ import { inherit } from 'app/enums/with-inherit.enum'; import { helptextDatasetForm } from 'app/helptext/storage/volumes/datasets/dataset-form'; import { Dataset } from 'app/interfaces/dataset.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; +import { SlideIn } from 'app/modules/slide-ins/slide-in'; import { ApiService } from 'app/modules/websocket/api.service'; import { DatasetFormService } from 'app/pages/datasets/components/dataset-form/utils/dataset-form.service'; -import { OldSlideInService } from 'app/services/old-slide-in.service'; describe('DatasetFormService', () => { let spectator: SpectatorService; @@ -22,7 +22,10 @@ describe('DatasetFormService', () => { mockProvider(DialogService, { warn: jest.fn(() => of(true)), }), - mockProvider(OldSlideInService), + mockProvider(SlideIn, { + components$: of([]), + closeLast: jest.fn(), + }), ], }); @@ -37,7 +40,7 @@ describe('DatasetFormService', () => { helptextDatasetForm.pathWarningTitle, helptextDatasetForm.pathIsTooLongWarning, ); - expect(spectator.inject(OldSlideInService).closeLast).toHaveBeenCalled(); + expect(spectator.inject(SlideIn).closeLast).toHaveBeenCalled(); }); it('checks parent path, shows error if it nesting level is too deep and closes slide in', async () => { @@ -48,7 +51,7 @@ describe('DatasetFormService', () => { helptextDatasetForm.pathWarningTitle, helptextDatasetForm.pathIsTooDeepWarning, ); - expect(spectator.inject(OldSlideInService).closeLast).toHaveBeenCalled(); + expect(spectator.inject(SlideIn).closeLast).toHaveBeenCalled(); }); }); diff --git a/src/app/pages/datasets/components/dataset-form/utils/dataset-form.service.ts b/src/app/pages/datasets/components/dataset-form/utils/dataset-form.service.ts index 49e5fcc7488..0cf624fd5d5 100644 --- a/src/app/pages/datasets/components/dataset-form/utils/dataset-form.service.ts +++ b/src/app/pages/datasets/components/dataset-form/utils/dataset-form.service.ts @@ -10,8 +10,8 @@ import { helptextDatasetForm } from 'app/helptext/storage/volumes/datasets/datas import { Dataset } from 'app/interfaces/dataset.interface'; import { Option } from 'app/interfaces/option.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; +import { SlideIn } from 'app/modules/slide-ins/slide-in'; import { ApiService } from 'app/modules/websocket/api.service'; -import { OldSlideInService } from 'app/services/old-slide-in.service'; @Injectable({ providedIn: 'root', @@ -21,7 +21,7 @@ export class DatasetFormService { private dialog: DialogService, private api: ApiService, private translate: TranslateService, - private slideInService: OldSlideInService, + private slideIn: SlideIn, ) {} checkAndWarnForLengthAndDepth(path: string): Observable { @@ -35,7 +35,7 @@ export class DatasetFormService { this.translate.instant(helptextDatasetForm.pathWarningTitle), this.translate.instant(helptextDatasetForm.pathIsTooDeepWarning), ).pipe( - tap(() => this.slideInService.closeLast()), + tap(() => this.slideIn.closeLast()), map(() => false), ); } @@ -44,7 +44,7 @@ export class DatasetFormService { this.translate.instant(helptextDatasetForm.pathWarningTitle), this.translate.instant(helptextDatasetForm.pathIsTooLongWarning), ).pipe( - tap(() => this.slideInService.closeLast()), + tap(() => this.slideIn.closeLast()), map(() => false), ); } diff --git a/src/app/pages/datasets/components/zvol-form/zvol-form.component.spec.ts b/src/app/pages/datasets/components/zvol-form/zvol-form.component.spec.ts index bfcb4a1aebb..f5052c2f57c 100644 --- a/src/app/pages/datasets/components/zvol-form/zvol-form.component.spec.ts +++ b/src/app/pages/datasets/components/zvol-form/zvol-form.component.spec.ts @@ -191,7 +191,6 @@ describe('ZvolFormComponent', () => { spectator = createComponent({ providers: [ mockProvider(SlideInRef, { ...slideInRef, getData: jest.fn(() => ({ isNew: false, parentId: 'test pool' })) }), - ], }); loader = TestbedHarnessEnvironment.loader(spectator.fixture); diff --git a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.html b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.html index cbe8e5d923c..2ad24fd2d76 100644 --- a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.html +++ b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.html @@ -1,4 +1,8 @@ - + diff --git a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.spec.ts b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.spec.ts index 82f10184aef..f54b2657d43 100644 --- a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.spec.ts +++ b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.spec.ts @@ -3,18 +3,18 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { ReactiveFormsModule } from '@angular/forms'; import { MatButtonHarness } from '@angular/material/button/testing'; import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator/jest'; +import { of } from 'rxjs'; import { mockCall, mockApi } from 'app/core/testing/utils/mock-api.utils'; import { mockAuth } from 'app/core/testing/utils/mock-auth.utils'; import { IscsiAuthAccess } from 'app/interfaces/iscsi.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { IxFormHarness } from 'app/modules/forms/ix-forms/testing/ix-form.harness'; -import { OldSlideInRef } from 'app/modules/slide-ins/old-slide-in-ref'; -import { SLIDE_IN_DATA } from 'app/modules/slide-ins/slide-in.token'; +import { SlideIn } from 'app/modules/slide-ins/slide-in'; +import { SlideInRef } from 'app/modules/slide-ins/slide-in-ref'; import { ApiService } from 'app/modules/websocket/api.service'; import { AuthorizedAccessFormComponent, } from 'app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component'; -import { OldSlideInService } from 'app/services/old-slide-in.service'; describe('AuthorizedAccessFormComponent', () => { let spectator: Spectator; @@ -30,6 +30,12 @@ describe('AuthorizedAccessFormComponent', () => { peersecret: 'peer123456789012', } as IscsiAuthAccess; + const slideInRef: SlideInRef = { + close: jest.fn(), + requireConfirmationWhen: jest.fn(), + getData: jest.fn(() => undefined), + }; + const createComponent = createComponentFactory({ component: AuthorizedAccessFormComponent, imports: [ @@ -37,14 +43,15 @@ describe('AuthorizedAccessFormComponent', () => { ], providers: [ mockAuth(), - mockProvider(OldSlideInService), + mockProvider(SlideIn, { + components$: of([]), + }), mockProvider(DialogService), mockApi([ mockCall('iscsi.auth.create'), mockCall('iscsi.auth.update'), ]), - mockProvider(OldSlideInRef), - { provide: SLIDE_IN_DATA, useValue: undefined }, + mockProvider(SlideInRef, slideInRef), ], }); @@ -77,7 +84,7 @@ describe('AuthorizedAccessFormComponent', () => { peersecret: 'peer123456789012', discovery_auth: 'NONE', }]); - expect(spectator.inject(OldSlideInRef).close).toHaveBeenCalled(); + expect(spectator.inject(SlideInRef).close).toHaveBeenCalled(); }); }); @@ -85,7 +92,7 @@ describe('AuthorizedAccessFormComponent', () => { beforeEach(async () => { spectator = createComponent({ providers: [ - { provide: SLIDE_IN_DATA, useValue: existingAuthorizedAccess }, + mockProvider(SlideInRef, { ...slideInRef, getData: () => existingAuthorizedAccess }), ], }); loader = TestbedHarnessEnvironment.loader(spectator.fixture); @@ -135,7 +142,7 @@ describe('AuthorizedAccessFormComponent', () => { }, ], ); - expect(spectator.inject(OldSlideInRef).close).toHaveBeenCalled(); + expect(spectator.inject(SlideInRef).close).toHaveBeenCalled(); }); }); }); diff --git a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.ts b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.ts index 56a84a0d495..d1160d4953f 100644 --- a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.ts +++ b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.ts @@ -1,5 +1,5 @@ import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnInit, + ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, } from '@angular/core'; import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; @@ -25,9 +25,8 @@ import { doesNotEqualFgValidator, matchOthersFgValidator, } from 'app/modules/forms/ix-forms/validators/password-validation/password-validation'; -import { OldModalHeaderComponent } from 'app/modules/slide-ins/components/old-modal-header/old-modal-header.component'; -import { OldSlideInRef } from 'app/modules/slide-ins/old-slide-in-ref'; -import { SLIDE_IN_DATA } from 'app/modules/slide-ins/slide-in.token'; +import { ModalHeaderComponent } from 'app/modules/slide-ins/components/modal-header/modal-header.component'; +import { SlideInRef } from 'app/modules/slide-ins/slide-in-ref'; import { TestDirective } from 'app/modules/test-id/test.directive'; import { ApiService } from 'app/modules/websocket/api.service'; @@ -38,7 +37,7 @@ import { ApiService } from 'app/modules/websocket/api.service'; changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ - OldModalHeaderComponent, + ModalHeaderComponent, MatCard, MatCardContent, ReactiveFormsModule, @@ -105,6 +104,7 @@ export class AuthorizedAccessFormComponent implements OnInit { isLoading = false; discoveryAuthOptions$: Observable[]>; + protected editingAccess: IscsiAuthAccess | undefined; readonly defaultDiscoveryAuthOptions = [ { @@ -139,9 +139,10 @@ export class AuthorizedAccessFormComponent implements OnInit { private cdr: ChangeDetectorRef, private api: ApiService, private validatorService: IxValidatorsService, - private slideInRef: OldSlideInRef, - @Inject(SLIDE_IN_DATA) private editingAccess: IscsiAuthAccess, - ) {} + public slideInRef: SlideInRef, + ) { + this.editingAccess = this.slideInRef.getData(); + } ngOnInit(): void { this.discoveryAuthOptions$ = of(this.defaultDiscoveryAuthOptions); @@ -205,7 +206,7 @@ export class AuthorizedAccessFormComponent implements OnInit { request$.pipe(untilDestroyed(this)).subscribe({ next: () => { this.isLoading = false; - this.slideInRef.close(true); + this.slideInRef.close({ response: true, error: null }); }, error: (error: unknown) => { this.isLoading = false; diff --git a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.spec.ts b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.spec.ts index 891622c23ce..a3e9d37c4ae 100644 --- a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.spec.ts +++ b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.spec.ts @@ -16,11 +16,11 @@ import { IxTableColumnsSelectorComponent, } from 'app/modules/ix-table/components/ix-table-columns-selector/ix-table-columns-selector.component'; import { FakeProgressBarComponent } from 'app/modules/loader/components/fake-progress-bar/fake-progress-bar.component'; -import { OldSlideInRef } from 'app/modules/slide-ins/old-slide-in-ref'; +import { SlideIn } from 'app/modules/slide-ins/slide-in'; +import { SlideInRef } from 'app/modules/slide-ins/slide-in-ref'; import { ApiService } from 'app/modules/websocket/api.service'; import { AuthorizedAccessFormComponent } from 'app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component'; import { AuthorizedAccessListComponent } from 'app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component'; -import { OldSlideInService } from 'app/services/old-slide-in.service'; const authAccess: IscsiAuthAccess[] = [ { @@ -36,6 +36,12 @@ describe('AuthorizedAccessListComponent', () => { let loader: HarnessLoader; let table: IxTableHarness; + const slideInRef: SlideInRef = { + close: jest.fn(), + requireConfirmationWhen: jest.fn(), + getData: jest.fn(() => undefined), + }; + const createComponent = createComponentFactory({ component: AuthorizedAccessListComponent, imports: [ @@ -50,12 +56,12 @@ describe('AuthorizedAccessListComponent', () => { mockCall('iscsi.auth.query', authAccess), mockCall('iscsi.auth.delete'), ]), - mockProvider(OldSlideInRef), + mockProvider(SlideInRef, slideInRef), mockProvider(DialogService, { confirm: jest.fn(() => of(true)), }), - mockProvider(OldSlideInService, { - open: jest.fn(() => ({ slideInClosed$: of(true) })), + mockProvider(SlideIn, { + open: jest.fn(() => of()), }), mockProvider(MatDialog, { open: jest.fn(), @@ -78,14 +84,14 @@ describe('AuthorizedAccessListComponent', () => { const addButton = await loader.getHarness(MatButtonHarness.with({ text: 'Add' })); await addButton.click(); - expect(spectator.inject(OldSlideInService).open).toHaveBeenCalledWith(AuthorizedAccessFormComponent); + expect(spectator.inject(SlideIn).open).toHaveBeenCalledWith(AuthorizedAccessFormComponent); }); it('opens authorized access form when "Edit" button is pressed', async () => { const editButton = await table.getHarnessInCell(IxIconHarness.with({ name: 'edit' }), 1, 3); await editButton.click(); - expect(spectator.inject(OldSlideInService).open).toHaveBeenCalledWith(AuthorizedAccessFormComponent, { + expect(spectator.inject(SlideIn).open).toHaveBeenCalledWith(AuthorizedAccessFormComponent, { data: authAccess[0], }); }); diff --git a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.ts b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.ts index dc42ac38413..38a676d1dd2 100644 --- a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.ts +++ b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.component.ts @@ -30,6 +30,7 @@ import { IxTableEmptyDirective } from 'app/modules/ix-table/directives/ix-table- import { createTable } from 'app/modules/ix-table/utils'; import { AppLoaderService } from 'app/modules/loader/app-loader.service'; import { FakeProgressBarComponent } from 'app/modules/loader/components/fake-progress-bar/fake-progress-bar.component'; +import { SlideIn } from 'app/modules/slide-ins/slide-in'; import { TestDirective } from 'app/modules/test-id/test.directive'; import { ApiService } from 'app/modules/websocket/api.service'; import { AuthorizedAccessFormComponent } from 'app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component'; @@ -38,7 +39,6 @@ import { } from 'app/pages/sharing/iscsi/authorized-access/authorized-access-list/authorized-access-list.elements'; import { ErrorHandlerService } from 'app/services/error-handler.service'; import { IscsiService } from 'app/services/iscsi.service'; -import { OldSlideInService } from 'app/services/old-slide-in.service'; @UntilDestroy() @Component({ @@ -100,10 +100,11 @@ export class AuthorizedAccessListComponent implements OnInit { iconName: iconMarker('edit'), tooltip: this.translate.instant('Edit'), onClick: (row) => { - const slideInRef = this.slideInService.open(AuthorizedAccessFormComponent, { data: row }); - slideInRef.slideInClosed$ - .pipe(filter(Boolean), untilDestroyed(this)) - .subscribe(() => this.refresh()); + this.slideIn.open(AuthorizedAccessFormComponent, { data: row }) + .pipe( + filter((response) => !!response.response), + untilDestroyed(this), + ).subscribe(() => this.refresh()); }, }, { @@ -140,7 +141,7 @@ export class AuthorizedAccessListComponent implements OnInit { private dialogService: DialogService, private api: ApiService, private translate: TranslateService, - private slideInService: OldSlideInService, + private slideIn: SlideIn, private errorHandler: ErrorHandlerService, private cdr: ChangeDetectorRef, private iscsiService: IscsiService, @@ -161,8 +162,10 @@ export class AuthorizedAccessListComponent implements OnInit { } doAdd(): void { - const slideInRef = this.slideInService.open(AuthorizedAccessFormComponent); - slideInRef.slideInClosed$.pipe(filter(Boolean), untilDestroyed(this)).subscribe(() => this.refresh()); + this.slideIn.open(AuthorizedAccessFormComponent).pipe( + filter((response) => !!response.response), + untilDestroyed(this), + ).subscribe(() => this.refresh()); } onListFiltered(query: string): void { diff --git a/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.html b/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.html index 2aef200cc8a..82d59a8149b 100644 --- a/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.html +++ b/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.html @@ -1,4 +1,8 @@ - + diff --git a/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.spec.ts b/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.spec.ts index 51ee7b876d6..df5c6c6ad1d 100644 --- a/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.spec.ts +++ b/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.spec.ts @@ -3,6 +3,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { ReactiveFormsModule } from '@angular/forms'; import { MatButtonHarness } from '@angular/material/button/testing'; import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator/jest'; +import { of } from 'rxjs'; import { KiB } from 'app/constants/bytes.constant'; import { mockCall, mockApi } from 'app/core/testing/utils/mock-api.utils'; import { mockAuth } from 'app/core/testing/utils/mock-auth.utils'; @@ -11,11 +12,10 @@ import { Choices } from 'app/interfaces/choices.interface'; import { IscsiExtent } from 'app/interfaces/iscsi.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { IxFormHarness } from 'app/modules/forms/ix-forms/testing/ix-form.harness'; -import { OldSlideInRef } from 'app/modules/slide-ins/old-slide-in-ref'; -import { SLIDE_IN_DATA } from 'app/modules/slide-ins/slide-in.token'; +import { SlideIn } from 'app/modules/slide-ins/slide-in'; +import { SlideInRef } from 'app/modules/slide-ins/slide-in-ref'; import { ApiService } from 'app/modules/websocket/api.service'; import { ExtentFormComponent } from 'app/pages/sharing/iscsi/extent/extent-form/extent-form.component'; -import { OldSlideInService } from 'app/services/old-slide-in.service'; import { StorageService } from 'app/services/storage.service'; describe('ExtentFormComponent', () => { @@ -23,6 +23,12 @@ describe('ExtentFormComponent', () => { let loader: HarnessLoader; let form: IxFormHarness; + const slideInRef: SlideInRef = { + close: jest.fn(), + requireConfirmationWhen: jest.fn(), + getData: jest.fn(() => undefined), + }; + const existingExtent = { id: 123, name: 'test_name', @@ -49,7 +55,9 @@ describe('ExtentFormComponent', () => { ], providers: [ mockAuth(), - mockProvider(OldSlideInService), + mockProvider(SlideIn, { + components$: of([]), + }), mockProvider(StorageService), mockProvider(DialogService), mockApi([ @@ -61,8 +69,7 @@ describe('ExtentFormComponent', () => { key_device_3: 'value_device_3', } as Choices), ]), - mockProvider(OldSlideInRef), - { provide: SLIDE_IN_DATA, useValue: undefined }, + mockProvider(SlideInRef, slideInRef), ], }); @@ -124,7 +131,7 @@ describe('ExtentFormComponent', () => { type: IscsiExtentType.Disk, xen: true, }]); - expect(spectator.inject(OldSlideInRef).close).toHaveBeenCalled(); + expect(spectator.inject(SlideInRef).close).toHaveBeenCalled(); }); }); @@ -132,7 +139,7 @@ describe('ExtentFormComponent', () => { beforeEach(async () => { spectator = createComponent({ providers: [ - { provide: SLIDE_IN_DATA, useValue: existingExtent }, + mockProvider(SlideInRef, { ...slideInRef, getData: () => existingExtent }), ], }); loader = TestbedHarnessEnvironment.loader(spectator.fixture); @@ -188,7 +195,7 @@ describe('ExtentFormComponent', () => { xen: true, }, ]); - expect(spectator.inject(OldSlideInRef).close).toHaveBeenCalled(); + expect(spectator.inject(SlideInRef).close).toHaveBeenCalled(); }); }); }); diff --git a/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.ts b/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.ts index 81d2d735d2e..cebe94acbe0 100644 --- a/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.ts +++ b/src/app/pages/sharing/iscsi/extent/extent-form/extent-form.component.ts @@ -1,5 +1,5 @@ import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnInit, + ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, } from '@angular/core'; import { Validators, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; @@ -28,9 +28,8 @@ import { IxInputComponent } from 'app/modules/forms/ix-forms/components/ix-input import { IxSelectComponent } from 'app/modules/forms/ix-forms/components/ix-select/ix-select.component'; import { FormErrorHandlerService } from 'app/modules/forms/ix-forms/services/form-error-handler.service'; import { IxFormatterService } from 'app/modules/forms/ix-forms/services/ix-formatter.service'; -import { OldModalHeaderComponent } from 'app/modules/slide-ins/components/old-modal-header/old-modal-header.component'; -import { OldSlideInRef } from 'app/modules/slide-ins/old-slide-in-ref'; -import { SLIDE_IN_DATA } from 'app/modules/slide-ins/slide-in.token'; +import { ModalHeaderComponent } from 'app/modules/slide-ins/components/modal-header/modal-header.component'; +import { SlideInRef } from 'app/modules/slide-ins/slide-in-ref'; import { TestDirective } from 'app/modules/test-id/test.directive'; import { ApiService } from 'app/modules/websocket/api.service'; import { FilesystemService } from 'app/services/filesystem.service'; @@ -44,7 +43,7 @@ import { IscsiService } from 'app/services/iscsi.service'; changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ - OldModalHeaderComponent, + ModalHeaderComponent, MatCard, MatCardContent, ReactiveFormsModule, @@ -98,6 +97,7 @@ export class ExtentFormComponent implements OnInit { }); isLoading = false; + protected editingExtent: IscsiExtent | undefined; private extentDiskBeingEdited$ = new BehaviorSubject

} - @if (isReady && !report()?.errorConf) { + @if (isReady && !report().errorConf) {
diff --git a/src/app/pages/reports-dashboard/components/report/report.component.ts b/src/app/pages/reports-dashboard/components/report/report.component.ts index 333e50a8cd9..92bb212e412 100644 --- a/src/app/pages/reports-dashboard/components/report/report.component.ts +++ b/src/app/pages/reports-dashboard/components/report/report.component.ts @@ -90,7 +90,7 @@ export class ReportComponent implements OnInit, OnChanges { private readonly lineChart = viewChild(LineChartComponent); - updateReport$ = new BehaviorSubject>(null); + updateReport$ = new BehaviorSubject | null>(null); fetchReport$ = new BehaviorSubject(null); autoRefreshTimer: Subscription; autoRefreshEnabled: boolean; @@ -130,7 +130,8 @@ export class ReportComponent implements OnInit, OnChanges { get reportTitle(): string { const trimmed = this.report().title.replace(/[()]/g, ''); - return this.identifier() ? trimmed.replace(/{identifier}/, this.identifier()) : this.report().title; + const identifier = this.identifier(); + return identifier ? trimmed.replace(/{identifier}/, identifier) : this.report().title; } get currentZoomLevel(): ReportZoomLevel { diff --git a/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.ts b/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.ts index d6c1a9e7221..3d025f7d5c3 100644 --- a/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.ts +++ b/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.ts @@ -4,7 +4,7 @@ import { ChangeDetectorRef, Component, OnInit, output, } from '@angular/core'; -import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { NonNullableFormBuilder, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatMenuTrigger, MatMenu, MatMenuItem } from '@angular/material/menu'; import { ActivatedRoute, RouterLink } from '@angular/router'; @@ -56,7 +56,7 @@ export class ReportsGlobalControlsComponent implements OnInit { metrics: [[] as string[]], }); - protected activeTab: ReportTab; + protected activeTab: ReportTab | undefined; protected allTabs: ReportTab[]; protected diskDevices$ = this.reportsService.getDiskDevices(); protected diskMetrics$ = this.reportsService.getDiskMetrics(); @@ -65,7 +65,7 @@ export class ReportsGlobalControlsComponent implements OnInit { protected readonly searchableElements = reportingGlobalControlsElements; constructor( - private fb: FormBuilder, + private fb: NonNullableFormBuilder, private route: ActivatedRoute, private store$: Store, private reportsService: ReportsService, diff --git a/src/app/pages/services/components/service-ssh/service-ssh.component.ts b/src/app/pages/services/components/service-ssh/service-ssh.component.ts index 56d9ff21211..f62990f8c4d 100644 --- a/src/app/pages/services/components/service-ssh/service-ssh.component.ts +++ b/src/app/pages/services/components/service-ssh/service-ssh.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, } from '@angular/core'; -import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { NonNullableFormBuilder, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; @@ -69,15 +69,15 @@ export class ServiceSshComponent implements OnInit { }; form = this.fb.group({ - tcpport: [null as number], - password_login_groups: [null as string[]], + tcpport: [null as number | null], + password_login_groups: [null as string[] | null], passwordauth: [false], kerberosauth: [false], tcpfwd: [false], bindiface: [[] as string[]], compression: [false], - sftp_log_level: [null as SshSftpLogLevel], - sftp_log_facility: [null as SshSftpLogFacility], + sftp_log_level: [null as SshSftpLogLevel | null], + sftp_log_facility: [null as SshSftpLogFacility | null], weak_ciphers: [[] as SshWeakCipher[]], options: [''], }); @@ -106,7 +106,7 @@ export class ServiceSshComponent implements OnInit { private errorHandler: ErrorHandlerService, private cdr: ChangeDetectorRef, private formErrorHandler: FormErrorHandlerService, - private fb: FormBuilder, + private fb: NonNullableFormBuilder, private dialogService: DialogService, private userService: UserService, private translate: TranslateService, diff --git a/src/app/pages/services/components/service-ups/service-ups.component.ts b/src/app/pages/services/components/service-ups/service-ups.component.ts index c0f664028e8..41d78c56c69 100644 --- a/src/app/pages/services/components/service-ups/service-ups.component.ts +++ b/src/app/pages/services/components/service-ups/service-ups.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, } from '@angular/core'; -import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; +import { Validators, ReactiveFormsModule, NonNullableFormBuilder } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; @@ -63,25 +63,25 @@ export class ServiceUpsComponent implements OnInit { isMasterMode = true; form = this.fb.group({ - identifier: [null as string, [Validators.required, Validators.pattern(/^[\w|,|.|\-|_]+$/)]], - mode: [null as UpsMode], - remotehost: [null as string, Validators.required], - remoteport: [null as number, Validators.required], - driver: [null as string, Validators.required], - port: [null as string, Validators.required], - monuser: [null as string, Validators.required], - monpwd: [null as string, Validators.pattern(/^((?![#|\s]).)*$/)], - extrausers: [null as string], + identifier: [null as string | null, [Validators.required, Validators.pattern(/^[\w|,|.|\-|_]+$/)]], + mode: [null as UpsMode | null], + remotehost: [null as string | null, Validators.required], + remoteport: [null as number | null, Validators.required], + driver: [null as string | null, Validators.required], + port: [null as string | null, Validators.required], + monuser: [null as string | null, Validators.required], + monpwd: [null as string | null, Validators.pattern(/^((?![#|\s]).)*$/)], + extrausers: [null as string | null], rmonitor: [false], - shutdown: [null as string], - shutdowntimer: [null as number], - shutdowncmd: [null as string], + shutdown: [null as string | null], + shutdowntimer: [null as number | null], + shutdowncmd: [null as string | null], powerdown: [false], nocommwarntime: [300], hostsync: [15], - description: [null as string], - options: [null as string], - optionsupsd: [null as string], + description: [null as string | null], + options: [null as string | null], + optionsupsd: [null as string | null], }); readonly helptext = helptextServiceUps; @@ -150,7 +150,7 @@ export class ServiceUpsComponent implements OnInit { private formErrorHandler: FormErrorHandlerService, private cdr: ChangeDetectorRef, private errorHandler: ErrorHandlerService, - private fb: FormBuilder, + private fb: NonNullableFormBuilder, private dialogService: DialogService, private translate: TranslateService, private snackbar: SnackbarService, diff --git a/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.html b/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.html index 405e69bed05..a0e89ef17c9 100644 --- a/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.html +++ b/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.html @@ -10,7 +10,7 @@

@@ -20,7 +20,7 @@

mat-button [ixTest]="['iscsi-share', 'wizard']" [ixUiSearch]="searchableElements.elements.wizard" - (click)="openForm(null, true)" + (click)="openForm(undefined, true)" > {{ 'Wizard' | translate }} diff --git a/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.ts b/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.ts index aa503517719..b9d020a655f 100644 --- a/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.ts +++ b/src/app/pages/sharing/components/shares-dashboard/iscsi-card/iscsi-card.component.ts @@ -79,7 +79,7 @@ export class IscsiCardComponent implements OnInit { Role.SharingWrite, ]; - targets = signal(null); + targets = signal(null); protected readonly searchableElements = iscsiCardElements; @@ -98,9 +98,7 @@ export class IscsiCardComponent implements OnInit { title: this.translate.instant('Mode'), propertyName: 'mode', hidden: true, - getValue: (row) => (iscsiTargetModeNames.has(row.mode) - ? this.translate.instant(iscsiTargetModeNames.get(row.mode)) - : row.mode || '-'), + getValue: (row) => this.translate.instant(iscsiTargetModeNames.get(row.mode) || row.mode) || '-', }), actionsColumn({ actions: [ diff --git a/src/app/pages/sharing/components/shares-dashboard/nfs-card/nfs-card.component.html b/src/app/pages/sharing/components/shares-dashboard/nfs-card/nfs-card.component.html index db4c53e705d..a810dc527d1 100644 --- a/src/app/pages/sharing/components/shares-dashboard/nfs-card/nfs-card.component.html +++ b/src/app/pages/sharing/components/shares-dashboard/nfs-card/nfs-card.component.html @@ -10,7 +10,7 @@

diff --git a/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.html b/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.html index 146d40576d9..4b71c159cf6 100644 --- a/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.html +++ b/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.html @@ -10,7 +10,7 @@

diff --git a/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.ts b/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.ts index b7f71077361..fe3437277fe 100644 --- a/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.ts +++ b/src/app/pages/sharing/components/shares-dashboard/smb-card/smb-card.component.ts @@ -123,7 +123,7 @@ export class SmbCardComponent implements OnInit { { iconName: iconMarker('edit'), tooltip: this.translate.instant('Edit'), - disabled: (row) => this.loadingMap$.pipe(map((ids) => ids.get(row.id))), + disabled: (row) => this.loadingMap$.pipe(map((ids) => Boolean(ids.get(row.id)))), onClick: (row) => this.openForm(row), }, { diff --git a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.ts b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.ts index d1160d4953f..f447bacd462 100644 --- a/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.ts +++ b/src/app/pages/sharing/iscsi/authorized-access/authorized-access-form/authorized-access-form.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, } from '@angular/core'; -import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; +import { Validators, ReactiveFormsModule, NonNullableFormBuilder } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; @@ -63,7 +63,7 @@ export class AuthorizedAccessFormComponent implements OnInit { } form = this.formBuilder.group({ - tag: [null as number, [Validators.required, Validators.min(0)]], + tag: [null as number | null, [Validators.required, Validators.min(0)]], user: ['', Validators.required], secret: ['', [ Validators.minLength(12), @@ -134,7 +134,7 @@ export class AuthorizedAccessFormComponent implements OnInit { constructor( private translate: TranslateService, - private formBuilder: FormBuilder, + private formBuilder: NonNullableFormBuilder, private errorHandler: FormErrorHandlerService, private cdr: ChangeDetectorRef, private api: ApiService, @@ -188,7 +188,7 @@ export class AuthorizedAccessFormComponent implements OnInit { } onSubmit(): void { - const values = this.form.value; + const values = this.form.getRawValue(); const payload = { tag: values.tag, user: values.user, diff --git a/src/app/pages/sharing/iscsi/iscsi-wizard/steps/extent-wizard-step/extent-wizard-step.component.ts b/src/app/pages/sharing/iscsi/iscsi-wizard/steps/extent-wizard-step/extent-wizard-step.component.ts index 866082a8d85..60c4d17ae90 100644 --- a/src/app/pages/sharing/iscsi/iscsi-wizard/steps/extent-wizard-step/extent-wizard-step.component.ts +++ b/src/app/pages/sharing/iscsi/iscsi-wizard/steps/extent-wizard-step/extent-wizard-step.component.ts @@ -33,7 +33,7 @@ import { IscsiService } from 'app/services/iscsi.service'; ], }) export class ExtentWizardStepComponent implements OnInit { - readonly form = input(); + readonly form = input.required(); readonly helptextSharingIscsi = helptextSharingIscsi; readonly fileNodeProvider = this.filesystemService.getFilesystemNodeProvider(); diff --git a/src/app/pages/sharing/iscsi/iscsi-wizard/steps/protocol-options-wizard-step/protocol-options-wizard-step.component.ts b/src/app/pages/sharing/iscsi/iscsi-wizard/steps/protocol-options-wizard-step/protocol-options-wizard-step.component.ts index 70fea6402b7..c52058a9ff6 100644 --- a/src/app/pages/sharing/iscsi/iscsi-wizard/steps/protocol-options-wizard-step/protocol-options-wizard-step.component.ts +++ b/src/app/pages/sharing/iscsi/iscsi-wizard/steps/protocol-options-wizard-step/protocol-options-wizard-step.component.ts @@ -35,7 +35,7 @@ import { IscsiService } from 'app/services/iscsi.service'; ], }) export class ProtocolOptionsWizardStepComponent implements OnInit { - form = input(); + form = input.required(); isFibreChannelMode = input(false); readonly helptextSharingIscsi = helptextSharingIscsi; diff --git a/src/app/pages/sharing/iscsi/iscsi-wizard/steps/target-wizard-step/target-wizard-step.component.ts b/src/app/pages/sharing/iscsi/iscsi-wizard/steps/target-wizard-step/target-wizard-step.component.ts index 300d3ee1bd2..db20ad88fe1 100644 --- a/src/app/pages/sharing/iscsi/iscsi-wizard/steps/target-wizard-step/target-wizard-step.component.ts +++ b/src/app/pages/sharing/iscsi/iscsi-wizard/steps/target-wizard-step/target-wizard-step.component.ts @@ -28,7 +28,7 @@ import { IscsiService } from 'app/services/iscsi.service'; ], }) export class TargetWizardStepComponent { - form = input(); + form = input.required(); readonly helptextSharingIscsi = helptextSharingIscsi; diff --git a/src/app/pages/sharing/iscsi/target/all-targets/target-details/associated-extents-card/associated-extents-card.component.html b/src/app/pages/sharing/iscsi/target/all-targets/target-details/associated-extents-card/associated-extents-card.component.html index 027ba6db32c..bf094042ff0 100644 --- a/src/app/pages/sharing/iscsi/target/all-targets/target-details/associated-extents-card/associated-extents-card.component.html +++ b/src/app/pages/sharing/iscsi/target/all-targets/target-details/associated-extents-card/associated-extents-card.component.html @@ -9,13 +9,13 @@

} @else {
diff --git a/src/app/pages/system/general-settings/manage-configuration-menu/manage-configuration-menu.component.ts b/src/app/pages/system/general-settings/manage-configuration-menu/manage-configuration-menu.component.ts index bf92c7cf639..7f2817061b9 100644 --- a/src/app/pages/system/general-settings/manage-configuration-menu/manage-configuration-menu.component.ts +++ b/src/app/pages/system/general-settings/manage-configuration-menu/manage-configuration-menu.component.ts @@ -62,11 +62,12 @@ export class ManageConfigurationMenuComponent { this.matDialog.open(UploadConfigDialogComponent); } - onResetDefaults(): void { + onResetToDefaults(): void { this.dialogService.confirm({ title: helptext.reset_config_form.title, message: helptext.reset_config_form.message, buttonText: helptext.reset_config_form.button_text, + buttonColor: 'warn', }) .pipe( filter(Boolean), diff --git a/src/app/pages/system/general-settings/ntp-server/ntp-server-card/ntp-server-card.component.ts b/src/app/pages/system/general-settings/ntp-server/ntp-server-card/ntp-server-card.component.ts index 95c47693199..a198dbf601a 100644 --- a/src/app/pages/system/general-settings/ntp-server/ntp-server-card/ntp-server-card.component.ts +++ b/src/app/pages/system/general-settings/ntp-server/ntp-server-card/ntp-server-card.component.ts @@ -131,6 +131,7 @@ export class NtpServerCardComponent implements OnInit { { address: server.address }, ), buttonText: this.translate.instant('Delete'), + buttonColor: 'warn', }).pipe( filter(Boolean), switchMap(() => this.api.call('system.ntpserver.delete', [server.id])), diff --git a/src/app/pages/system/general-settings/upload-config-dialog/upload-config-dialog.component.html b/src/app/pages/system/general-settings/upload-config-dialog/upload-config-dialog.component.html index 650626a823e..3c8496c1a62 100644 --- a/src/app/pages/system/general-settings/upload-config-dialog/upload-config-dialog.component.html +++ b/src/app/pages/system/general-settings/upload-config-dialog/upload-config-dialog.component.html @@ -15,7 +15,7 @@

{{ 'Upload Config' | translate }}

*ixRequiresRoles="requiredRoles" mat-button type="submit" - color="primary" + color="warn" ixTest="upload" [disabled]="form.invalid" > diff --git a/src/app/pages/vm/devices/device-list/device-delete-modal/device-delete-modal.component.html b/src/app/pages/vm/devices/device-list/device-delete-modal/device-delete-modal.component.html index b3254db7379..1f93ed37d89 100644 --- a/src/app/pages/vm/devices/device-list/device-delete-modal/device-delete-modal.component.html +++ b/src/app/pages/vm/devices/device-list/device-delete-modal/device-delete-modal.component.html @@ -51,7 +51,7 @@

{{ 'Delete' | translate }}

*ixRequiresRoles="requiredRoles" type="submit" mat-button - color="primary" + color="warn" ixTest="delete-device" [disabled]="form.invalid" > diff --git a/src/app/pages/vm/vm-list/delete-vm-dialog/delete-vm-dialog.component.html b/src/app/pages/vm/vm-list/delete-vm-dialog/delete-vm-dialog.component.html index 15efed6bb8d..c5c4a21d86b 100644 --- a/src/app/pages/vm/vm-list/delete-vm-dialog/delete-vm-dialog.component.html +++ b/src/app/pages/vm/vm-list/delete-vm-dialog/delete-vm-dialog.component.html @@ -30,7 +30,7 @@

{{ 'Delete Virtual Machine' | translate }}

+
+ + + @if (showLabels()) { +
{{ option.label | translate }}
+ @if (option.description) { + {{ option.description | translate }} + } + } +
} @empty { {{ 'No options are passed' | translate }} } diff --git a/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.scss b/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.scss index 6b5d0bdd38c..05fb0a91e58 100644 --- a/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.scss +++ b/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.scss @@ -20,3 +20,51 @@ .selected { color: var(--primary); } + +.title, +.description { + display: block; + margin: 0; + text-align: center; +} + +.title { + font-size: 14px; + margin-bottom: 2px; + margin-top: 8px; +} + +.description { + color: var(--fg2); +} + +.with-labels { + gap: 16px; + + ::ng-deep .mdc-icon-button { + border: 2px solid var(--lines); + border-radius: 0; + height: 100px !important; + line-height: 100px; + width: 100px !important; + + .mdc-icon-button__ripple, + .mat-ripple { + border-radius: 0 !important; + height: 100px !important; + width: 100px !important; + } + + .ix-icon, + .ix-icon svg { + font-size: 40px; + height: 40px; + line-height: 1; + width: 40px; + } + + &.selected { + border-color: var(--primary); + } + } +} diff --git a/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.spec.ts b/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.spec.ts index bd02b7b5265..6320dd8658a 100644 --- a/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.spec.ts +++ b/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.spec.ts @@ -28,6 +28,7 @@ describe('IxIconGroupComponent', () => { [tooltip]="tooltip" [required]="required" [formControl]="formControl" + [showLabels]="true" >`, { hostProps: { @@ -85,6 +86,14 @@ describe('IxIconGroupComponent', () => { formControl.setValue('edit'); expect(await iconGroupHarness.getValue()).toBe('edit'); }); + it('shows labels when `showLabels` is set to true', async () => { + const icons = await iconGroupHarness.getIcons(); + expect(icons).toHaveLength(2); + + const labels = spectator.queryAll('h5.title').map((el) => el.textContent); + expect(labels[0]).toBe('Edit'); + expect(labels[1]).toBe('Delete'); + }); }); it('updates form control value when user presses the button', async () => { diff --git a/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.ts b/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.ts index 0a342a0dece..b2cee9d93ca 100644 --- a/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.ts +++ b/src/app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component.ts @@ -20,13 +20,13 @@ import { TestDirective } from 'app/modules/test-id/test.directive'; changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ - IxLabelComponent, - MatIconButton, - IxIconComponent, IxErrorsComponent, + IxIconComponent, + IxLabelComponent, ReactiveFormsModule, - TranslateModule, TestDirective, + TranslateModule, + MatIconButton, ], hostDirectives: [ { ...registeredDirectiveConfig }, @@ -37,6 +37,7 @@ export class IxIconGroupComponent implements ControlValueAccessor { readonly label = input(); readonly tooltip = input(); readonly required = input(false); + readonly showLabels = input(false); protected isDisabled = false; protected value: IconGroupOption['value']; diff --git a/src/app/pages/datasets/components/dataset-node/dataset-roles-cell/dataset-roles-cell.component.html b/src/app/pages/datasets/components/dataset-node/dataset-roles-cell/dataset-roles-cell.component.html index ee7dcc1cfe7..5efa3ff9ad8 100644 --- a/src/app/pages/datasets/components/dataset-node/dataset-roles-cell/dataset-roles-cell.component.html +++ b/src/app/pages/datasets/components/dataset-node/dataset-roles-cell/dataset-roles-cell.component.html @@ -22,7 +22,7 @@ @if (dataset().vms?.length) { } diff --git a/src/app/pages/datasets/components/dataset-node/dataset-roles-cell/dataset-roles-cell.component.spec.ts b/src/app/pages/datasets/components/dataset-node/dataset-roles-cell/dataset-roles-cell.component.spec.ts index 39b37646603..775b6e73fc7 100644 --- a/src/app/pages/datasets/components/dataset-node/dataset-roles-cell/dataset-roles-cell.component.spec.ts +++ b/src/app/pages/datasets/components/dataset-node/dataset-roles-cell/dataset-roles-cell.component.spec.ts @@ -51,7 +51,7 @@ describe('DatasetRolesCellComponent', () => { it('shows "VM" icon and tooltip when dataset has vms', async () => { await setupTest({ name: 'root', vms: [{ name: 'vm1', path: '' }, { name: 'vm1', path: '' }, { name: 'vm2', path: '' }] } as DatasetDetails, false); - expect(await ixIcon.getName()).toBe('computer'); + expect(await ixIcon.getName()).toBe('mdi-laptop'); expect(spectator.query(MatTooltip)!.message).toBe('This dataset is used by: vm1, vm2'); }); @@ -71,7 +71,7 @@ describe('DatasetRolesCellComponent', () => { it('shows "VM" icon when dataset has VMs', async () => { await setupTest({ name: 'root', vms: [{}] } as DatasetDetails, false); - expect(await ixIcon.getName()).toBe('computer'); + expect(await ixIcon.getName()).toBe('mdi-laptop'); }); it('shows "SMB Share" icon for dataset', async () => { diff --git a/src/app/pages/datasets/components/roles-card/roles-card.component.html b/src/app/pages/datasets/components/roles-card/roles-card.component.html index 106b6f8eeaa..42adb41c8ba 100644 --- a/src/app/pages/datasets/components/roles-card/roles-card.component.html +++ b/src/app/pages/datasets/components/roles-card/roles-card.component.html @@ -44,7 +44,7 @@

{{ 'Roles' | translate }}

} @if (dataset().vms?.length) {
- +
{{ 'VM' | translate }}:
{{ 'This dataset is used by: {vms}' | translate: { vms: vmNames() } }} diff --git a/src/app/pages/virtualization/components/all-instances/instance-details/instance-devices/add-device-menu/add-device-menu.component.ts b/src/app/pages/virtualization/components/all-instances/instance-details/instance-devices/add-device-menu/add-device-menu.component.ts index cf41980cb50..ac0dc197054 100644 --- a/src/app/pages/virtualization/components/all-instances/instance-details/instance-devices/add-device-menu/add-device-menu.component.ts +++ b/src/app/pages/virtualization/components/all-instances/instance-details/instance-devices/add-device-menu/add-device-menu.component.ts @@ -7,7 +7,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { pickBy } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { VirtualizationDeviceType, VirtualizationGpuType, VirtualizationType } from 'app/enums/virtualization.enum'; +import { VirtualizationDeviceType, VirtualizationGpuType } from 'app/enums/virtualization.enum'; import { AvailableUsb, VirtualizationDevice, @@ -42,7 +42,7 @@ import { ErrorHandlerService } from 'app/services/error-handler.service'; export class AddDeviceMenuComponent { private readonly usbChoices = toSignal(this.api.call('virt.device.usb_choices'), { initialValue: {} }); // TODO: Stop hardcoding params - private readonly gpuChoices = toSignal(this.api.call('virt.device.gpu_choices', [VirtualizationType.Container, VirtualizationGpuType.Physical]), { initialValue: {} }); + private readonly gpuChoices = toSignal(this.api.call('virt.device.gpu_choices', [VirtualizationGpuType.Physical]), { initialValue: {} }); protected readonly isLoadingDevices = this.deviceStore.isLoading; diff --git a/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.html b/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.html index f224bfec1a2..02751cc20de 100644 --- a/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.html +++ b/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.html @@ -16,6 +16,14 @@ [required]="true" > + +
- @if ((usbDevices$ | async); as usbDevices) { - @if (usbDevices.length > 0) { - 0) { + + - - - } + [options]="usbDevices$" + > + } - @if ((gpuDevices$ | async); as gpuDevices) { - @if (gpuDevices.length > 0) { - 0) { + + - - - } + [options]="gpuDevices$" + > + }
diff --git a/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.spec.ts b/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.spec.ts index 3418b6fad98..6c1c75763f7 100644 --- a/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.spec.ts +++ b/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.spec.ts @@ -1,6 +1,7 @@ import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { MatButtonHarness } from '@angular/material/button/testing'; +import { MatCheckboxHarness } from '@angular/material/checkbox/testing'; import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { @@ -25,6 +26,7 @@ import { VirtualizationInstance } from 'app/interfaces/virtualization.interface' import { AuthService } from 'app/modules/auth/auth.service'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { IxCheckboxHarness } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.harness'; +import { IxIconGroupHarness } from 'app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.harness'; import { IxListHarness } from 'app/modules/forms/ix-forms/components/ix-list/ix-list.harness'; import { IxFormHarness } from 'app/modules/forms/ix-forms/testing/ix-form.harness'; import { PageHeaderComponent } from 'app/modules/page-header/page-title-header/page-header.component'; @@ -34,8 +36,7 @@ import { InstanceWizardComponent } from 'app/pages/virtualization/components/ins import { VirtualizationImageWithId } from 'app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component'; import { FilesystemService } from 'app/services/filesystem.service'; -// TODO: https://ixsystems.atlassian.net/browse/NAS-133118 -describe.skip('InstanceWizardComponent', () => { +describe('InstanceWizardComponent', () => { let spectator: SpectatorRouting; let loader: HarnessLoader; let form: IxFormHarness; @@ -57,7 +58,15 @@ describe.skip('InstanceWizardComponent', () => { autostart: false, cpu: 'Intel Xeon', memory: 2 * GiB, - } as VirtualizationInstance]), + }, + { + id: 'testVM', + name: 'testVM', + type: VirtualizationType.Vm, + autostart: false, + cpu: 'Intel Xeon', + memory: 4 * GiB, + }] as VirtualizationInstance[]), mockCall('interface.has_pending_changes', false), mockCall('virt.device.nic_choices', { nic1: 'nic1', @@ -113,18 +122,98 @@ describe.skip('InstanceWizardComponent', () => { expect(spectator.inject(MatDialog).open).toHaveBeenCalled(); expect(await form.getValues()).toMatchObject({ - Image: 'Almalinux 8 Cloud', + Image: 'almalinux/8/cloud', + }); + }); + + it('creates new container instance when form is submitted', async () => { + await form.fillForm({ + Name: 'new', + 'CPU Configuration': '1-2', + 'Memory Size': '1 GiB', }); + + const browseButton = await loader.getHarness(MatButtonHarness.with({ text: 'Browse Catalog' })); + await browseButton.click(); + + const diskList = await loader.getHarness(IxListHarness.with({ label: 'Disks' })); + await diskList.pressAddButton(); + const diskForm = await diskList.getLastListItem(); + await diskForm.fillForm({ + Source: '/mnt/source', + Destination: 'destination', + }); + + const proxiesList = await loader.getHarness(IxListHarness.with({ label: 'Proxies' })); + await proxiesList.pressAddButton(); + const proxyForm = await proxiesList.getLastListItem(); + await proxyForm.fillForm({ + 'Host Port': 3000, + 'Host Protocol': 'TCP', + 'Instance Port': 2000, + 'Instance Protocol': 'UDP', + }); + + // TODO: Fix this to use IxCheckboxHarness + const usbDeviceCheckbox = await loader.getHarness(MatCheckboxHarness.with({ + label: 'xHCI Host Controller (0003)', + })); + await usbDeviceCheckbox.check(); + + const useDefaultNetworkCheckbox = await loader.getHarness(IxCheckboxHarness.with({ label: 'Use default network settings' })); + await useDefaultNetworkCheckbox.setValue(false); + + // TODO: Fix this to use IxCheckboxHarness + const nicDeviceCheckbox = await loader.getHarness(MatCheckboxHarness.with({ label: 'nic1' })); + await nicDeviceCheckbox.check(); + + // TODO: Fix this to use IxCheckboxHarness + const gpuDeviceCheckbox = await loader.getHarness(MatCheckboxHarness.with({ label: 'NVIDIA GeForce GTX 1080' })); + await gpuDeviceCheckbox.check(); + + const createButton = await loader.getHarness(MatButtonHarness.with({ text: 'Create' })); + await createButton.click(); + + expect(spectator.inject(ApiService).job).toHaveBeenCalledWith('virt.instance.create', [{ + name: 'new', + autostart: true, + cpu: '1-2', + instance_type: VirtualizationType.Container, + devices: [ + { + dev_type: VirtualizationDeviceType.Disk, + source: '/mnt/source', + destination: 'destination', + }, + { + dev_type: VirtualizationDeviceType.Proxy, + source_port: 3000, + source_proto: VirtualizationProxyProtocol.Tcp, + dest_port: 2000, + dest_proto: VirtualizationProxyProtocol.Udp, + }, + { dev_type: VirtualizationDeviceType.Nic, nic_type: VirtualizationNicType.Bridged, parent: 'nic1' }, + { dev_type: VirtualizationDeviceType.Usb, product_id: '0003' }, + { dev_type: VirtualizationDeviceType.Gpu, pci: 'pci_0000_01_00_0' }, + ], + image: 'almalinux/8/cloud', + memory: GiB, + environment: {}, + }]); + expect(spectator.inject(DialogService).jobDialog).toHaveBeenCalled(); + expect(spectator.inject(SnackbarService).success).toHaveBeenCalled(); }); - it('creates new instance when form is submitted', async () => { + it('creates new vm instance when form is submitted', async () => { await form.fillForm({ Name: 'new', - Autostart: true, 'CPU Configuration': '1-2', 'Memory Size': '1 GiB', }); + const instanceType = await loader.getHarness(IxIconGroupHarness.with({ label: 'Virtualization Method' })); + await instanceType.setValue('VM'); + const browseButton = await loader.getHarness(MatButtonHarness.with({ text: 'Browse Catalog' })); await browseButton.click(); @@ -146,17 +235,22 @@ describe.skip('InstanceWizardComponent', () => { 'Instance Protocol': 'UDP', }); - const usbDeviceCheckbox = await loader.getHarness(IxCheckboxHarness.with({ label: 'xHCI Host Controller (0003)' })); - await usbDeviceCheckbox.setValue(true); + // TODO: Fix this to use IxCheckboxHarness + const usbDeviceCheckbox = await loader.getHarness(MatCheckboxHarness.with({ + label: 'xHCI Host Controller (0003)', + })); + await usbDeviceCheckbox.check(); const useDefaultNetworkCheckbox = await loader.getHarness(IxCheckboxHarness.with({ label: 'Use default network settings' })); await useDefaultNetworkCheckbox.setValue(false); - const nicDeviceCheckbox = await loader.getHarness(IxCheckboxHarness.with({ label: 'nic1' })); - await nicDeviceCheckbox.setValue(true); + // TODO: Fix this to use IxCheckboxHarness + const nicDeviceCheckbox = await loader.getHarness(MatCheckboxHarness.with({ label: 'nic1' })); + await nicDeviceCheckbox.check(); - const gpuDeviceCheckbox = await loader.getHarness(IxCheckboxHarness.with({ label: 'NVIDIA GeForce GTX 1080' })); - await gpuDeviceCheckbox.setValue(true); + // TODO: Fix this to use IxCheckboxHarness + const gpuDeviceCheckbox = await loader.getHarness(MatCheckboxHarness.with({ label: 'NVIDIA GeForce GTX 1080' })); + await gpuDeviceCheckbox.check(); const createButton = await loader.getHarness(MatButtonHarness.with({ text: 'Create' })); await createButton.click(); @@ -165,6 +259,7 @@ describe.skip('InstanceWizardComponent', () => { name: 'new', autostart: true, cpu: '1-2', + instance_type: VirtualizationType.Vm, devices: [ { dev_type: VirtualizationDeviceType.Disk, @@ -193,7 +288,6 @@ describe.skip('InstanceWizardComponent', () => { it('sends no NIC devices when default network settings checkbox is set', async () => { await form.fillForm({ Name: 'new', - Autostart: true, 'CPU Configuration': '1-2', 'Memory Size': '1 GiB', }); @@ -204,10 +298,12 @@ describe.skip('InstanceWizardComponent', () => { const useDefaultNetworkCheckbox = await loader.getHarness(IxCheckboxHarness.with({ label: 'Use default network settings' })); await useDefaultNetworkCheckbox.setValue(false); - const nicDeviceCheckbox = await loader.getHarness(IxCheckboxHarness.with({ label: 'nic1' })); - await nicDeviceCheckbox.setValue(true); + // TODO: Fix this to use IxCheckboxHarness + const nicDeviceCheckbox = await loader.getHarness(MatCheckboxHarness.with({ label: 'nic1' })); + await nicDeviceCheckbox.check(); await useDefaultNetworkCheckbox.setValue(true); // no nic1 should be send now + spectator.detectChanges(); const createButton = await loader.getHarness(MatButtonHarness.with({ text: 'Create' })); await createButton.click(); @@ -220,6 +316,7 @@ describe.skip('InstanceWizardComponent', () => { image: 'almalinux/8/cloud', memory: GiB, environment: {}, + instance_type: 'CONTAINER', }]); expect(spectator.inject(DialogService).jobDialog).toHaveBeenCalled(); expect(spectator.inject(SnackbarService).success).toHaveBeenCalled(); diff --git a/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.ts b/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.ts index 807e77dbb99..a2ed66c7ff9 100644 --- a/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.ts +++ b/src/app/pages/virtualization/components/instance-wizard/instance-wizard.component.ts @@ -14,6 +14,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { + filter, map, Observable, of, } from 'rxjs'; import { Role } from 'app/enums/role.enum'; @@ -26,6 +27,8 @@ import { virtualizationProxyProtocolLabels, VirtualizationRemote, VirtualizationType, + virtualizationTypeIcons, + virtualizationTypeLabels, } from 'app/enums/virtualization.enum'; import { mapToOptions } from 'app/helpers/options.helper'; import { containersHelptext } from 'app/helptext/virtualization/containers'; @@ -42,6 +45,7 @@ import { IxCheckboxListComponent } from 'app/modules/forms/ix-forms/components/i import { IxExplorerComponent } from 'app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component'; import { IxFormGlossaryComponent } from 'app/modules/forms/ix-forms/components/ix-form-glossary/ix-form-glossary.component'; import { IxFormSectionComponent } from 'app/modules/forms/ix-forms/components/ix-form-section/ix-form-section.component'; +import { IxIconGroupComponent } from 'app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component'; import { IxInputComponent } from 'app/modules/forms/ix-forms/components/ix-input/ix-input.component'; import { IxListItemComponent } from 'app/modules/forms/ix-forms/components/ix-list/ix-list-item/ix-list-item.component'; import { IxListComponent } from 'app/modules/forms/ix-forms/components/ix-list/ix-list.component'; @@ -64,23 +68,24 @@ import { FilesystemService } from 'app/services/filesystem.service'; selector: 'ix-instance-wizard', standalone: true, imports: [ - PageHeaderComponent, - IxInputComponent, - ReactiveFormsModule, - TranslateModule, - IxCheckboxComponent, - MatButton, - TestDirective, - ReadOnlyComponent, AsyncPipe, - IxListComponent, + IxCheckboxComponent, + IxCheckboxListComponent, + IxExplorerComponent, IxFormGlossaryComponent, IxFormSectionComponent, - IxCheckboxListComponent, + IxInputComponent, + IxListComponent, IxListItemComponent, IxSelectComponent, - IxExplorerComponent, + MatButton, NgxSkeletonLoaderModule, + PageHeaderComponent, + ReactiveFormsModule, + ReadOnlyComponent, + TestDirective, + TranslateModule, + IxIconGroupComponent, ], templateUrl: './instance-wizard.component.html', styleUrls: ['./instance-wizard.component.scss'], @@ -90,6 +95,8 @@ export class InstanceWizardComponent { protected readonly isLoading = signal(false); protected readonly requiredRoles = [Role.VirtGlobalWrite]; protected readonly VirtualizationNicType = VirtualizationNicType; + protected readonly virtualizationTypeOptions$ = of(mapToOptions(virtualizationTypeLabels, this.translate)); + protected readonly virtualizationTypeIcons = virtualizationTypeIcons; protected readonly hasPendingInterfaceChanges = toSignal(this.api.call('interface.has_pending_changes')); @@ -109,10 +116,9 @@ export class InstanceWizardComponent { }))), ); - // TODO: MV supports only [Container, Physical] for now (based on the response) gpuDevices$ = this.api.call( 'virt.device.gpu_choices', - [VirtualizationType.Container, VirtualizationGpuType.Physical], + [VirtualizationGpuType.Physical], ).pipe( map((choices) => Object.entries(choices).map(([pci, gpu]) => ({ label: gpu.description, @@ -121,10 +127,11 @@ export class InstanceWizardComponent { ); protected readonly form = this.formBuilder.nonNullable.group({ - name: ['', Validators.required], - image: ['', Validators.required], + name: ['', [Validators.required, Validators.minLength(1), Validators.maxLength(200)]], + instance_type: [VirtualizationType.Container, Validators.required], + image: ['', [Validators.required, Validators.minLength(1), Validators.maxLength(200)]], cpu: ['', [cpuValidator()]], - memory: [null as number | null], + memory: [null as number], use_default_network: [true], usb_devices: [[] as string[]], gpu_devices: [[] as string[]], @@ -132,9 +139,9 @@ export class InstanceWizardComponent { mac_vlan_nics: [[] as string[]], proxies: this.formBuilder.array; - source_port: FormControl; + source_port: FormControl; dest_proto: FormControl; - dest_port: FormControl; + dest_port: FormControl; }>>([]), disks: this.formBuilder.array; @@ -167,25 +174,22 @@ export class InstanceWizardComponent { minWidth: '90vw', data: { remote: VirtualizationRemote.LinuxContainers, + type: this.form.controls.instance_type.value, }, }) .afterClosed() - .pipe(untilDestroyed(this)) + .pipe(filter(Boolean), untilDestroyed(this)) .subscribe((image: VirtualizationImageWithId) => { - if (!image) { - return; - } - this.form.controls.image.setValue(image.id); }); } protected addProxy(): void { - const control = this.formBuilder.nonNullable.group({ + const control = this.formBuilder.group({ source_proto: [VirtualizationProxyProtocol.Tcp], - source_port: [null as number | null, Validators.required], + source_port: [null as number, Validators.required], dest_proto: [VirtualizationProxyProtocol.Tcp], - dest_port: [null as number | null, Validators.required], + dest_port: [null as number, Validators.required], }); this.form.controls.proxies.push(control); @@ -196,7 +200,7 @@ export class InstanceWizardComponent { } protected addDisk(): void { - const control = this.formBuilder.nonNullable.group({ + const control = this.formBuilder.group({ source: ['', Validators.required], destination: ['', Validators.required], }); @@ -228,7 +232,7 @@ export class InstanceWizardComponent { } addEnvironmentVariable(): void { - const control = this.formBuilder.nonNullable.group({ + const control = this.formBuilder.group({ name: ['', Validators.required], value: ['', Validators.required], }); @@ -246,6 +250,7 @@ export class InstanceWizardComponent { return { devices, autostart: true, + instance_type: this.form.controls.instance_type.value, name: this.form.controls.name.value, cpu: this.form.controls.cpu.value, memory: this.form.controls.memory.value, @@ -297,23 +302,26 @@ export class InstanceWizardComponent { dev_type: VirtualizationDeviceType.Gpu, }); } - const macVlanNics: { parent: string; dev_type: VirtualizationDeviceType; nic_type: VirtualizationNicType }[] = []; - for (const parent of this.form.controls.mac_vlan_nics.value) { - macVlanNics.push({ - parent, - dev_type: VirtualizationDeviceType.Nic, - nic_type: VirtualizationNicType.Macvlan, - }); + if (!this.form.controls.use_default_network.value) { + for (const parent of this.form.controls.mac_vlan_nics.value) { + macVlanNics.push({ + parent, + dev_type: VirtualizationDeviceType.Nic, + nic_type: VirtualizationNicType.Macvlan, + }); + } } const bridgedNics: { parent: string; dev_type: VirtualizationDeviceType; nic_type: VirtualizationNicType }[] = []; - for (const parent of this.form.controls.bridged_nics.value) { - bridgedNics.push({ - parent, - dev_type: VirtualizationDeviceType.Nic, - nic_type: VirtualizationNicType.Bridged, - }); + if (!this.form.controls.use_default_network.value) { + for (const parent of this.form.controls.bridged_nics.value) { + bridgedNics.push({ + parent, + dev_type: VirtualizationDeviceType.Nic, + nic_type: VirtualizationNicType.Bridged, + }); + } } const proxies = this.form.controls.proxies.value.map((proxy) => ({ diff --git a/src/app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component.spec.ts b/src/app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component.spec.ts index 2ae49a09c2a..c3b0e7f0f2d 100644 --- a/src/app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component.spec.ts +++ b/src/app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component.spec.ts @@ -12,7 +12,7 @@ import { mockCall, mockApi, } from 'app/core/testing/utils/mock-api.utils'; -import { VirtualizationRemote } from 'app/enums/virtualization.enum'; +import { VirtualizationRemote, VirtualizationType } from 'app/enums/virtualization.enum'; import { VirtualizationImage } from 'app/interfaces/virtualization.interface'; import { IxFormHarness } from 'app/modules/forms/ix-forms/testing/ix-form.harness'; import { ApiService } from 'app/modules/websocket/api.service'; @@ -25,6 +25,7 @@ const imageChoices: Record = { release: '8', archs: ['arm64'], variant: 'cloud', + instance_types: [VirtualizationType.Container], } as VirtualizationImage, 'alpine/3.18/default': { label: 'Alpine 3.18 (armhf, default)', @@ -32,6 +33,7 @@ const imageChoices: Record = { release: '3.18', archs: ['armhf'], variant: 'default', + instance_types: [VirtualizationType.Container], } as VirtualizationImage, } as Record; @@ -47,7 +49,10 @@ describe('SelectImageDialogComponent', () => { mockProvider(MatDialogRef), { provide: MAT_DIALOG_DATA, - useValue: { remote: VirtualizationRemote.LinuxContainers }, + useValue: { + remote: VirtualizationRemote.LinuxContainers, + type: VirtualizationType.Container, + }, }, ], }); diff --git a/src/app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component.ts b/src/app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component.ts index 52ee27272d3..7822dffdc9d 100644 --- a/src/app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component.ts +++ b/src/app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, Inject, signal, OnInit, + computed, } from '@angular/core'; import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -11,7 +12,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { catchError, Observable, of } from 'rxjs'; import { EmptyType } from 'app/enums/empty-type.enum'; -import { VirtualizationRemote } from 'app/enums/virtualization.enum'; +import { VirtualizationRemote, VirtualizationType } from 'app/enums/virtualization.enum'; import { EmptyConfig } from 'app/interfaces/empty-config.interface'; import { Option } from 'app/interfaces/option.interface'; import { VirtualizationImage } from 'app/interfaces/virtualization.interface'; @@ -71,13 +72,17 @@ export class SelectImageDialogComponent implements OnInit { large: true, } as EmptyConfig); + protected isContainer = computed(() => { + return this.data.type === VirtualizationType.Container; + }); + constructor( private api: ApiService, private dialogRef: MatDialogRef, private fb: FormBuilder, private translate: TranslateService, private errorHandler: ErrorHandlerService, - @Inject(MAT_DIALOG_DATA) protected data: { remote: VirtualizationRemote }, + @Inject(MAT_DIALOG_DATA) protected data: { remote: VirtualizationRemote; type: VirtualizationType }, ) { this.filterForm.valueChanges.pipe(untilDestroyed(this)).subscribe(() => this.filterImages()); } @@ -95,7 +100,7 @@ export class SelectImageDialogComponent implements OnInit { } private getImages(): void { - this.api.call('virt.instance.image_choices', [this.data]) + this.api.call('virt.instance.image_choices', [{ remote: this.data.remote }]) .pipe( catchError((error: unknown) => { this.errorHandler.showErrorModal(error); @@ -114,7 +119,10 @@ export class SelectImageDialogComponent implements OnInit { const variantSet = new Set(); const releaseSet = new Set(); - const imageArray = Object.entries(images).map(([id, image]) => ({ ...image, id })); + const imageArray = Object.entries(images) + .filter(([_, image]) => image?.instance_types?.includes(this.data.type)) + .map(([id, image]) => ({ ...image, id })); + this.images.set(imageArray); imageArray.forEach((image) => { diff --git a/src/app/services/navigation/navigation.service.ts b/src/app/services/navigation/navigation.service.ts index 49a2038a839..fb7076b2644 100644 --- a/src/app/services/navigation/navigation.service.ts +++ b/src/app/services/navigation/navigation.service.ts @@ -90,17 +90,15 @@ export class NavigationService { name: T('Virtualization'), type: MenuItemType.Link, tooltip: T('Virtualization'), - icon: iconMarker('computer'), - state: 'vm', - isVisible$: this.hasVms$, + icon: iconMarker('mdi-laptop'), + state: 'virtualization', }, { - name: T('Containers (WIP)'), + name: T('Virtualization (Old)'), type: MenuItemType.Link, - tooltip: T('Containers'), - icon: iconMarker('view_in_ar'), - state: 'virtualization', - isVisible$: this.hasApps$, + tooltip: T('Virtualization (Old)'), + icon: iconMarker('mdi-laptop'), + state: 'vm', }, { name: T('Apps'), diff --git a/src/assets/i18n/af.json b/src/assets/i18n/af.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/af.json +++ b/src/assets/i18n/af.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ar.json b/src/assets/i18n/ar.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ar.json +++ b/src/assets/i18n/ar.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ast.json b/src/assets/i18n/ast.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ast.json +++ b/src/assets/i18n/ast.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/az.json b/src/assets/i18n/az.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/az.json +++ b/src/assets/i18n/az.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/be.json b/src/assets/i18n/be.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/be.json +++ b/src/assets/i18n/be.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/bg.json b/src/assets/i18n/bg.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/bg.json +++ b/src/assets/i18n/bg.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/bn.json b/src/assets/i18n/bn.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/bn.json +++ b/src/assets/i18n/bn.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/br.json b/src/assets/i18n/br.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/br.json +++ b/src/assets/i18n/br.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/bs.json b/src/assets/i18n/bs.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/bs.json +++ b/src/assets/i18n/bs.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ca.json b/src/assets/i18n/ca.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ca.json +++ b/src/assets/i18n/ca.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/cs.json b/src/assets/i18n/cs.json index 6b46f75e981..a8e45134625 100644 --- a/src/assets/i18n/cs.json +++ b/src/assets/i18n/cs.json @@ -149,6 +149,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -660,7 +661,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2005,6 +2005,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4178,6 +4179,7 @@ "Variant": "", "View logs": "", "Virtual Ports": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4185,6 +4187,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization settings updated": "", "WARNING: A failover will temporarily interrupt system services.": "", "WARNING: Adding data VDEVs with different numbers of disks is not recommended.": "", diff --git a/src/assets/i18n/cy.json b/src/assets/i18n/cy.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/cy.json +++ b/src/assets/i18n/cy.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/da.json b/src/assets/i18n/da.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/da.json +++ b/src/assets/i18n/da.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 02f06add9ed..894a887b4ee 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -289,6 +289,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any system service can communicate externally.": "", "Api Keys": "", "App": "", @@ -723,7 +724,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -1807,6 +1807,7 @@ "Link Aggregation Protocol": "", "Link aggregation interface": "", "Linked Service": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -3674,6 +3675,7 @@ "Virtual Machines": "", "Virtual Ports": "", "Virtual machine created": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -3681,6 +3683,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/dsb.json b/src/assets/i18n/dsb.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/dsb.json +++ b/src/assets/i18n/dsb.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/el.json b/src/assets/i18n/el.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/el.json +++ b/src/assets/i18n/el.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/en-au.json b/src/assets/i18n/en-au.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/en-au.json +++ b/src/assets/i18n/en-au.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/en-gb.json b/src/assets/i18n/en-gb.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/en-gb.json +++ b/src/assets/i18n/en-gb.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/eo.json b/src/assets/i18n/eo.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/eo.json +++ b/src/assets/i18n/eo.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/es-ar.json b/src/assets/i18n/es-ar.json index 586002a4d7c..417802c0c07 100644 --- a/src/assets/i18n/es-ar.json +++ b/src/assets/i18n/es-ar.json @@ -1,5 +1,6 @@ { "": "", + "Any OS": "", "Archs": "", "Flash Identify Light": "", "Key Cert Sign": "", @@ -12,6 +13,7 @@ "LUN ID": "", "LUN RPM": "", "Lan": "", + "Linux Only": "", "Locks": "", "MOTD": "", "Machine Time: {machineTime} \n Browser Time: {browserTime}": "", @@ -85,6 +87,8 @@ "The following { n, plural, one {boot environment} other {# boot environments} } will be deleted. Are you sure you want to proceed?": "", "USB Passthrough Device": "", "Unlock Child Encrypted Roots": "", + "Virtualization (Old)": "", + "Virtualization Method": "", "everyone@": "", "group@": "", "\n It looks like your session has been inactive for more than {lifetime} seconds.
\n For security reasons we will log you out at {time}.\n ": "\nParece que tu sesión estuvo inactiva durante más de {lifetime} segundos.
\nPor razones de seguridad, vamos a cerrar tu sesión a las {time}.\n", @@ -1080,7 +1084,6 @@ "Container Shell": "Shell del contenedor", "Container Write": "Escritura del contenedor", "Containers": "Contenedores", - "Containers (WIP)": "Contenedores (en proceso)", "Content Commitment": "Compromiso de contenido", "Contents of the uploaded Service Account JSON file.": "Contenido del archivo JSON de cuenta de servicio cargado.", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "Las operaciones de copiar y pegar del menú contextual están deshabilitadas en el Shell. Los accesos directos para copiar y pegar para Mac son Command+c y Command+v. Para la mayoría de los sistemas operativos, use Ctrl+Insertar para copiar y Mayús+Insertar para pegar.", diff --git a/src/assets/i18n/es-co.json b/src/assets/i18n/es-co.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/es-co.json +++ b/src/assets/i18n/es-co.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/es-mx.json b/src/assets/i18n/es-mx.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/es-mx.json +++ b/src/assets/i18n/es-mx.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/es-ni.json b/src/assets/i18n/es-ni.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/es-ni.json +++ b/src/assets/i18n/es-ni.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/es-ve.json b/src/assets/i18n/es-ve.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/es-ve.json +++ b/src/assets/i18n/es-ve.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 1e2ff977be7..16523543d4a 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -386,6 +386,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -880,7 +881,6 @@ "Container Read": "", "Container Shell": "", "Container Write": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2269,6 +2269,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4530,6 +4531,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4537,6 +4539,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/et.json b/src/assets/i18n/et.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/et.json +++ b/src/assets/i18n/et.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/eu.json b/src/assets/i18n/eu.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/eu.json +++ b/src/assets/i18n/eu.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/fa.json b/src/assets/i18n/fa.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/fa.json +++ b/src/assets/i18n/fa.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/fi.json b/src/assets/i18n/fi.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/fi.json +++ b/src/assets/i18n/fi.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 504758cd911..0eca8840e58 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -29,6 +29,7 @@ "Admins": "", "Age": "", "Alias": "", + "Any OS": "", "Api Keys": "", "App": "", "App Info": "", @@ -122,7 +123,6 @@ "Container Read": "", "Container Shell": "", "Container Write": "", - "Containers (WIP)": "", "Continue in background": "", "Contract Type": "", "Controller": "", @@ -384,6 +384,7 @@ "Link Aggregation Protocol": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "Locks": "", "MOTD": "", "Machine": "", @@ -825,6 +826,7 @@ "Vendor ID": "", "Verbose Logging": "", "Virtual Ports": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -832,6 +834,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Voltage": "", "WWPN": "", "WWPN (B)": "", diff --git a/src/assets/i18n/fy.json b/src/assets/i18n/fy.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/fy.json +++ b/src/assets/i18n/fy.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ga.json b/src/assets/i18n/ga.json index 7c604f78351..b0894ca0154 100644 --- a/src/assets/i18n/ga.json +++ b/src/assets/i18n/ga.json @@ -29,6 +29,7 @@ "Allow clients to access the TrueNAS server if they are members of domains that have a trust relationship with the domain to which TrueNAS is joined. This requires valid idmap backend configuration for all trusted domains.": "", "Also unlock any separate encryption roots that are children of this dataset. Child datasets that inherit encryption from this encryption root will be unlocked in either case.": "", "An update is already applied. Please restart the system.": "", + "Any OS": "", "Api Keys": "", "App Info": "", "App Network": "", @@ -72,7 +73,6 @@ "Container ID": "", "Container Logs": "", "Container Shell": "", - "Containers (WIP)": "", "Continue in background": "", "Controller": "", "Controller A WWPN": "", @@ -189,6 +189,7 @@ "Leave empty to allow all host CPUs to be used.": "", "Leave empty to not limit instance memory.": "", "Legacy OS: Extent block size 512b, TPC enabled, no Xen compat mode, SSD speed": "", + "Linux Only": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", "Log In To Outlook": "", "Log in to {oauthType} to set up Oauth credentials.": "", @@ -359,6 +360,7 @@ "View Logs": "", "View logs": "", "Virtual Ports": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -366,6 +368,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization settings updated": "", "Volume Mounts": "", "WWPN": "", diff --git a/src/assets/i18n/gd.json b/src/assets/i18n/gd.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/gd.json +++ b/src/assets/i18n/gd.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/gl.json b/src/assets/i18n/gl.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/gl.json +++ b/src/assets/i18n/gl.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/he.json b/src/assets/i18n/he.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/he.json +++ b/src/assets/i18n/he.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/hi.json b/src/assets/i18n/hi.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/hi.json +++ b/src/assets/i18n/hi.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/hr.json b/src/assets/i18n/hr.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/hr.json +++ b/src/assets/i18n/hr.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/hsb.json b/src/assets/i18n/hsb.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/hsb.json +++ b/src/assets/i18n/hsb.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/hu.json b/src/assets/i18n/hu.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/hu.json +++ b/src/assets/i18n/hu.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ia.json b/src/assets/i18n/ia.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ia.json +++ b/src/assets/i18n/ia.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/id.json b/src/assets/i18n/id.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/id.json +++ b/src/assets/i18n/id.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/io.json b/src/assets/i18n/io.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/io.json +++ b/src/assets/i18n/io.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/is.json b/src/assets/i18n/is.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/is.json +++ b/src/assets/i18n/is.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index bec9d1afbe6..61e6f110290 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -382,6 +382,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -868,7 +869,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2323,6 +2323,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -3821,6 +3822,7 @@ "Vdevs spans enclosure": "", "View logs": "", "Virtual Ports": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -3828,6 +3830,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization settings updated": "", "WARNING: Adding data VDEVs with different numbers of disks is not recommended.": "", "WWPN": "", diff --git a/src/assets/i18n/ja.json b/src/assets/i18n/ja.json index 792cd157375..a0f74565233 100644 --- a/src/assets/i18n/ja.json +++ b/src/assets/i18n/ja.json @@ -358,6 +358,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any system service can communicate externally.": "", "Api Keys": "", "App": "", @@ -826,7 +827,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Continue in background": "", @@ -2215,6 +2215,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4492,6 +4493,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4499,6 +4501,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ka.json b/src/assets/i18n/ka.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ka.json +++ b/src/assets/i18n/ka.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/kk.json b/src/assets/i18n/kk.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/kk.json +++ b/src/assets/i18n/kk.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/km.json b/src/assets/i18n/km.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/km.json +++ b/src/assets/i18n/km.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/kn.json b/src/assets/i18n/kn.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/kn.json +++ b/src/assets/i18n/kn.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ko.json b/src/assets/i18n/ko.json index d4d1b53f218..e2da57e7e65 100644 --- a/src/assets/i18n/ko.json +++ b/src/assets/i18n/ko.json @@ -1,5 +1,6 @@ { "": "", + "Any OS": "", "Change Session Timeout in": "", "Change from public to increase system security. Can only contain alphanumeric characters, underscores, dashes, periods, and spaces. This can be left empty for SNMPv3 networks.": "", "Changes to Hosts Allow or Hosts Deny take effect when the SMB service restarts.": "", @@ -68,6 +69,7 @@ "Front": "", "Hot Spare": "", "Hottest": "", + "Linux Only": "", "Long": "", "Name ~ \"admin\"": "", "Networking": "", @@ -93,6 +95,8 @@ "Thick": "", "Unkeep": "", "Until": "", + "Virtualization (Old)": "", + "Virtualization Method": "", "\n It looks like your session has been inactive for more than {lifetime} seconds.
\n For security reasons we will log you out at {time}.\n ": "세션의 비활성화 시간이 {lifetime}초를 넘었습니다.
보안을 위해 {time}에 로그아웃 되었습니다.", " Est. Usable Raw Capacity": " 사용 가능한 원시 용량 추정", " When the UPS Mode is set to slave. Enter the open network port number of the UPS Master system. The default port is 3493.": " UPS 모드슬레이브일 때, 마스터 UPS 시스템의 네트워크 포트 번호를 입력합니다. 기본 포트는 3493입니다.", @@ -1041,7 +1045,6 @@ "Container Shell": "컨테이너 셸", "Container Write": "컨테이너 쓰기", "Containers": "콘테이너", - "Containers (WIP)": "콘테이너 (WIP)", "Contents of the uploaded Service Account JSON file.": "업로드한 서비스 계정 JSON 파일의 내용입니다.", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "셸에선 컨텍스트 메뉴의 복사와 붙여넣기를 사용할 수 없습니다. 복사와 붙여넣기의 Mac 단축키는 Command+cCommand+v입니다. 대부분의 운영체제는 Ctrl+Insert로 복사하고 Shift+Insert로 붙여넣을 수 있습니다.", "Continue": "계속", diff --git a/src/assets/i18n/lb.json b/src/assets/i18n/lb.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/lb.json +++ b/src/assets/i18n/lb.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/lt.json b/src/assets/i18n/lt.json index 673da2e4057..3c45116c5ba 100644 --- a/src/assets/i18n/lt.json +++ b/src/assets/i18n/lt.json @@ -408,6 +408,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -979,7 +980,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2504,6 +2504,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4967,6 +4968,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4974,6 +4976,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/lv.json b/src/assets/i18n/lv.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/lv.json +++ b/src/assets/i18n/lv.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/mk.json b/src/assets/i18n/mk.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/mk.json +++ b/src/assets/i18n/mk.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ml.json b/src/assets/i18n/ml.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ml.json +++ b/src/assets/i18n/ml.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/mn.json b/src/assets/i18n/mn.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/mn.json +++ b/src/assets/i18n/mn.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/mr.json b/src/assets/i18n/mr.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/mr.json +++ b/src/assets/i18n/mr.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/my.json b/src/assets/i18n/my.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/my.json +++ b/src/assets/i18n/my.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/nb.json b/src/assets/i18n/nb.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/nb.json +++ b/src/assets/i18n/nb.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ne.json b/src/assets/i18n/ne.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ne.json +++ b/src/assets/i18n/ne.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/nl.json b/src/assets/i18n/nl.json index a7fe4a2bdb8..9616a1b6428 100644 --- a/src/assets/i18n/nl.json +++ b/src/assets/i18n/nl.json @@ -1,15 +1,19 @@ { "": "", + "Any OS": "", "Choose a new virtual port": "", "Create new virtual port": "", "Delete Target \"{name}\"": "", "Delete {n} associated {n, plural, one {extent} other {extents}}": "", "Do not connect to a fibre channel port": "", "Existing Ports": "", + "Linux Only": "", "New Cloud Credential": "", "Protocol Options": "", "Use an existing port": "", "Use current port": "", + "Virtualization (Old)": "", + "Virtualization Method": "", "Warning: iSCSI Target is currently in use.
": "", "You are about to delete the target \"{name}\".": "", "You are about to delete the target \"{name}\". You may also choose to delete all extents associated with this target. Note the volumes will not be deleted with the extents.": "", @@ -1008,7 +1012,6 @@ "Container Shell": "Container shell", "Container Write": "Container Schrijven", "Containers": "Containers", - "Containers (WIP)": "Containers (WIP)", "Content Commitment": "Inhoudsbevestiging", "Contents of the uploaded Service Account JSON file.": "Inhoud van het geüploade serviceaccount JSON-bestand.", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "Kopieer- en plakbewerkingen in het contextmenu zijn uitgeschakeld in de Shell. Snelkoppelingen voor kopiëren en plakken voor Mac zijn Command+c en Command+v. Gebruik voor de meeste besturingssystemen Ctrl+Insert om te kopiëren en Shift+Insert om te plakken.", diff --git a/src/assets/i18n/nn.json b/src/assets/i18n/nn.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/nn.json +++ b/src/assets/i18n/nn.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/os.json b/src/assets/i18n/os.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/os.json +++ b/src/assets/i18n/os.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/pa.json b/src/assets/i18n/pa.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/pa.json +++ b/src/assets/i18n/pa.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/pl.json b/src/assets/i18n/pl.json index 40bdb558b96..27c3821c720 100644 --- a/src/assets/i18n/pl.json +++ b/src/assets/i18n/pl.json @@ -368,6 +368,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -938,7 +939,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2456,6 +2456,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4899,6 +4900,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4906,6 +4908,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/pt-br.json b/src/assets/i18n/pt-br.json index a965c276f6f..e31fae75039 100644 --- a/src/assets/i18n/pt-br.json +++ b/src/assets/i18n/pt-br.json @@ -356,6 +356,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -927,7 +928,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2451,6 +2451,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4917,6 +4918,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4924,6 +4926,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/pt.json b/src/assets/i18n/pt.json index 9eaa08a8cd5..fe11df70117 100644 --- a/src/assets/i18n/pt.json +++ b/src/assets/i18n/pt.json @@ -193,6 +193,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Api Keys": "", "App": "", "App Info": "", @@ -436,7 +437,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", "Continue in background": "", @@ -1434,6 +1434,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -3199,6 +3200,7 @@ "Virtual IP Address (Failover Address)": "", "Virtual Machine": "", "Virtual Ports": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -3206,6 +3208,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Voltage": "", diff --git a/src/assets/i18n/ro.json b/src/assets/i18n/ro.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ro.json +++ b/src/assets/i18n/ro.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ru.json b/src/assets/i18n/ru.json index f1506e01662..19a81dbf56e 100644 --- a/src/assets/i18n/ru.json +++ b/src/assets/i18n/ru.json @@ -257,6 +257,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any system service can communicate externally.": "", "Api Keys": "", "App": "", @@ -598,7 +599,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Continue in background": "", "Continue with the upgrade": "", @@ -1548,6 +1548,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -3305,6 +3306,7 @@ "Virtual Machine": "", "Virtual Ports": "", "Virtual machine created": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -3312,6 +3314,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Voltage": "", diff --git a/src/assets/i18n/sk.json b/src/assets/i18n/sk.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/sk.json +++ b/src/assets/i18n/sk.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/sl.json b/src/assets/i18n/sl.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/sl.json +++ b/src/assets/i18n/sl.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/sq.json b/src/assets/i18n/sq.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/sq.json +++ b/src/assets/i18n/sq.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/sr-latn.json b/src/assets/i18n/sr-latn.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/sr-latn.json +++ b/src/assets/i18n/sr-latn.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/sr.json b/src/assets/i18n/sr.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/sr.json +++ b/src/assets/i18n/sr.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/strings.json b/src/assets/i18n/strings.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/strings.json +++ b/src/assets/i18n/strings.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/sv.json b/src/assets/i18n/sv.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/sv.json +++ b/src/assets/i18n/sv.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/sw.json b/src/assets/i18n/sw.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/sw.json +++ b/src/assets/i18n/sw.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/ta.json b/src/assets/i18n/ta.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/ta.json +++ b/src/assets/i18n/ta.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/te.json b/src/assets/i18n/te.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/te.json +++ b/src/assets/i18n/te.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/th.json b/src/assets/i18n/th.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/th.json +++ b/src/assets/i18n/th.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/tr.json b/src/assets/i18n/tr.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/tr.json +++ b/src/assets/i18n/tr.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/tt.json b/src/assets/i18n/tt.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/tt.json +++ b/src/assets/i18n/tt.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/udm.json b/src/assets/i18n/udm.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/udm.json +++ b/src/assets/i18n/udm.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/uk.json b/src/assets/i18n/uk.json index 39d2f8333e5..3862fee8975 100644 --- a/src/assets/i18n/uk.json +++ b/src/assets/i18n/uk.json @@ -158,6 +158,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Api Keys": "", "App": "", "App Info": "", @@ -390,7 +391,6 @@ "Container Read": "", "Container Shell": "", "Container Write": "", - "Containers (WIP)": "", "Continue in background": "", "Continue with the upgrade": "", "Contract Type": "", @@ -1008,6 +1008,7 @@ "Link Aggregation Interfaces": "", "Link Aggregation Protocol": "", "Linked Service": "", + "Linux Only": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", "List of groups for which to generate audit messages. Keep this list empty to Watch All.": "", "List of groups to ignore when auditing. If conflict arises between Watch List and Ignore List (based on user group membership), then Watch List will take precedence and ops will be audited.": "", @@ -2025,6 +2026,7 @@ "Virtual IP Address (Failover Address)": "", "Virtual Machine": "", "Virtual Ports": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -2032,6 +2034,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Voltage": "", diff --git a/src/assets/i18n/vi.json b/src/assets/i18n/vi.json index a69c7620a87..b1f680347a8 100644 --- a/src/assets/i18n/vi.json +++ b/src/assets/i18n/vi.json @@ -413,6 +413,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -985,7 +986,6 @@ "Container Shell": "", "Container Write": "", "Containers": "", - "Containers (WIP)": "", "Content Commitment": "", "Contents of the uploaded Service Account JSON file.": "", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "", @@ -2510,6 +2510,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -4976,6 +4977,7 @@ "Virtual Ports": "", "Virtual machine created": "", "Virtualization": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -4983,6 +4985,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", diff --git a/src/assets/i18n/zh-hans.json b/src/assets/i18n/zh-hans.json index e7cec50b2dc..b7bc31bbfce 100644 --- a/src/assets/i18n/zh-hans.json +++ b/src/assets/i18n/zh-hans.json @@ -1,7 +1,11 @@ { "": "", "...": "", + "Any OS": "", "Both": "", + "Linux Only": "", + "Virtualization (Old)": "", + "Virtualization Method": "", "{n, plural, one {Pool in Enclosure} other {Pools in Enclosure} }": "", "\n It looks like your session has been inactive for more than {lifetime} seconds.
\n For security reasons we will log you out at {time}.\n ": "\n您的会话似乎已超过 {lifetime} 秒处于非活动状态。
\n 出于安全原因,我们将在 {time} 后注销您的会话。\n", " Est. Usable Raw Capacity": "估计可用原始容量", @@ -995,7 +999,6 @@ "Container Shell": "容器 Shell", "Container Write": "容器写入", "Containers": "容器", - "Containers (WIP)": "容器(WIP)", "Content Commitment": "内容承诺", "Contents of the uploaded Service Account JSON file.": "已上传的服务帐户 JSON 文件的内容。", "Context menu copy and paste operations are disabled in the Shell. Copy and paste shortcuts for Mac are Command+c and Command+v. For most operating systems, use Ctrl+Insert to copy and Shift+Insert to paste.": "命令行管理程序中禁用了上下文菜单复制和粘贴操作。Mac 的复制和粘贴快捷键是Command+cCommand+v。对于大多数操作系统,请使用Ctrl+Insert复制和Shift+Insert粘贴。", diff --git a/src/assets/i18n/zh-hant.json b/src/assets/i18n/zh-hant.json index d120af5dd76..bbc8d2667b0 100644 --- a/src/assets/i18n/zh-hant.json +++ b/src/assets/i18n/zh-hant.json @@ -239,6 +239,7 @@ "An update is already applied. Please restart the system.": "", "Anonymous User Download Bandwidth": "", "Anonymous User Upload Bandwidth": "", + "Any OS": "", "Any notes about initiators.": "", "Any system service can communicate externally.": "", "Api Keys": "", @@ -1613,6 +1614,7 @@ "Link aggregation interface": "", "Linked Service": "", "Linux": "", + "Linux Only": "", "List any existing dataset properties to remove from the replicated files.": "", "List of chat IDs": "", "List of files and directories to exclude from backup.
Separate entries by pressing Enter. See restic exclude patterns for more details about the --exclude option.": "", @@ -3407,6 +3409,7 @@ "Virtual Machine": "", "Virtual Ports": "", "Virtual machine created": "", + "Virtualization (Old)": "", "Virtualization Global Read": "", "Virtualization Global Write": "", "Virtualization Image Read": "", @@ -3414,6 +3417,7 @@ "Virtualization Instance Delete": "", "Virtualization Instance Read": "", "Virtualization Instance Write": "", + "Virtualization Method": "", "Virtualization is not supported": "", "Virtualization settings updated": "", "Visible": "", @@ -4117,7 +4121,6 @@ "Container Shell": "容器命令列", "Container Write": "容器寫入", "Containers": "容器", - "Containers (WIP)": "容器 (進行中)", "Continue": "繼續", "Continue in background": "在背景繼續", "Continue with download?": "繼續並下載嗎?", diff --git a/src/assets/icons/sprite-config.json b/src/assets/icons/sprite-config.json index ea5ff843078..f46094f44bd 100644 --- a/src/assets/icons/sprite-config.json +++ b/src/assets/icons/sprite-config.json @@ -1,3 +1,3 @@ { - "iconUrl": "assets/icons/sprite.svg?v=0a208a877e" + "iconUrl": "assets/icons/sprite.svg?v=aa5e18694d" } \ No newline at end of file diff --git a/src/assets/icons/sprite.svg b/src/assets/icons/sprite.svg index b7f0748fbb1..6b2355ddcdb 100644 --- a/src/assets/icons/sprite.svg +++ b/src/assets/icons/sprite.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 2df646d74a6913b3f8c70ea92a8180c7be89a11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sall=C3=A9?= Date: Mon, 6 Jan 2025 20:14:03 +0100 Subject: [PATCH 16/18] Update fr.json (#11293) Add some French translations --- src/assets/i18n/fr.json | 98 ++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 0eca8840e58..bf72ea89fb9 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -97,7 +97,7 @@ "Certificate Subject": "", "Certificate Write": "", "Check": "", - "Choose a new virtual port": "", + "Choose a new virtual port": "Choisissez un nouveau port virtuel", "Clear": "", "Client ID": "", "Close Instance Form": "", @@ -273,10 +273,10 @@ "HTTP Proxy:": "", "HTTPS Port": "", "HTTPS Redirect": "", - "Hardware Change": "", + "Hardware Change": "Changement de matériel", "Has Allow List": "", "Healthy": "", - "Hide Password": "", + "Hide Password": "Masquer le mot de passe", "Home Widgets": "", "Host Model": "", "Host Mounts": "", @@ -390,7 +390,7 @@ "Machine": "", "Macvlan NICs": "", "Main menu": "", - "Maintenance Window": "", + "Maintenance Window": "Maintenance programmée", "Manual Test": "", "Manual Upgrade": "", "Mapall Group": "", @@ -444,12 +444,12 @@ "Network Interface Read": "", "Network Interface Write": "", "Network Reconnection Issue": "", - "Network Reset": "", - "Network Settings": "", - "Network Stats": "", - "Network Traffic": "", + "Network Reset": "Réinitialisation du réseau", + "Network Settings": "Paramètres réseau", + "Network Stats": "Stats réseau", + "Network Traffic": "Trafic réseau", "Network Usage": "", - "Network Utilization": "", + "Network Utilization": "Utilisation du réseau", "New Backup Credential": "", "New Bucket Name": "", "New CSR": "", @@ -503,7 +503,7 @@ "Parent Interface": "", "Passive Controller": "", "Passthrough": "", - "Password Login": "", + "Password Login": "Mot de passe de connexion", "Pattern": "", "Pause Scrub": "", "Pending": "", @@ -519,9 +519,9 @@ "Privilege": "", "Privileges": "", "Processor": "", - "Product": "", - "Product ID": "", - "Promote": "", + "Product": "Produit", + "Product ID": "ID produit", + "Promote": "Promouvoir", "Prompt": "", "Properties Exclude": "", "Properties Override": "", @@ -540,15 +540,15 @@ "RAM": "", "Range Size": "", "Raw Filesize": "", - "Re-Open": "", - "Re-Open All Alerts": "", + "Re-Open": "Réouvrir", + "Re-Open All Alerts": "Réouvrir toutes les alertes", "Read Only": "", "Readonly Admin": "", "Rear": "", - "Reason": "", - "Reboot Local": "", - "Reboot Remote": "", - "Reboot Required": "", + "Reason": "Raison", + "Reboot Local": "Redémarrage local", + "Reboot Remote": "Redémarrage à distance", + "Reboot Required": "Redémarrage requis", "Release": "", "Remove extent association": "", "Replication Admin": "", @@ -577,7 +577,7 @@ "Routing": "", "Rsync": "", "Run As Context": "", - "Running Jobs": "", + "Running Jobs": "Tâches en cours", "S.M.A.R.T.": "", "SAS Connector": "", "SAS Expander": "", @@ -705,7 +705,7 @@ "Snapshot Time": "", "Snapshot Time {time}": "", "Snapshot Write": "", - "Software Installation": "", + "Software Installation": "Installation logiciel", "Sort": "", "Source Path": "", "Spares": "", @@ -731,10 +731,10 @@ "Support License": "", "Support Read": "", "Support Write": "", - "Switch To Advanced": "", - "Switch To Wizard": "", + "Switch To Advanced": "Basculer vers les optiond avancées", + "Switch To Wizard": "Basculer vers l'assistant", "Synced": "", - "Syslog Settings": "", + "Syslog Settings": "Paramètres Syslog", "Syslog TLS Certificate": "", "Syslog TLS Certificate Authority": "", "System Advanced Read": "", @@ -743,25 +743,25 @@ "System Audit Write": "", "System Data Pool": "", "System Dataset": "", - "System Freeze": "", + "System Freeze": "Freeze du système", "System General Read": "", "System General Write": "", - "System Image": "", + "System Image": "Image système", "System Information – Active": "", "System Information – Standby": "", - "System Overload": "", - "System Reports": "", - "System Security Settings": "", + "System Overload": "Surcharge du système", + "System Reports": "Rapports système", + "System Security Settings": "Paramètres de sécurité du système", "System Serial": "", - "System Stats": "", - "System Update": "", - "System Uptime": "", - "System Utilization": "", - "System Version": "", - "TLS No Empty Fragments": "", + "System Stats": "Stats du système", + "System Update": "Mise à jour du système", + "System Uptime": "Durée de fonctionnement du système", + "System Utilization": "Utilisation du système", + "System Version": "Version du Système", + "TLS No Empty Fragments": "TLS Aucun fragment vide", "TLS Policy": "", "Table Actions of Expandable Table": "", - "Task": "", + "Task": "Tâche", "Tenant Domain": "", "Terminal": "", "Test": "", @@ -771,8 +771,8 @@ "Timestamp": "", "Toggle Sidenav": "", "Tolerance Window": "", - "Toolbar": "", - "Tools": "", + "Toolbar": "Barre d'outils", + "Tools": "Outils", "Top": "", "Top bar": "", "Total": "", @@ -782,7 +782,7 @@ "Translate App": "", "Transmit Hash Policy": "", "Transport Encryption Behavior": "", - "Troubleshooting Issues": "", + "Troubleshooting Issues": "Résolution des problèmes", "TrueCommand Read": "", "TrueCommand Write": "", "Tunable": "", @@ -790,14 +790,14 @@ "UNIX Charset": "", "URL": "", "Unix NSS Info": "", - "Unix Primary Group": "", - "Unix Socket": "", + "Unix Primary Group": "Groupe primaire Unix", + "Unix Socket": "Socket Unix", "Unlink": "", - "Updating pool settings": "", + "Updating pool settings": "Mise à jour des paramètres du volume", "Usage Collection": "", "Usages": "", - "Use an existing port": "", - "Use current port": "", + "Use an existing port": "Utiliser un port existant", + "Use current port": "Utiliser le port actuel", "User API Keys": "", "User Bind Path": "", "User CN": "", @@ -819,8 +819,8 @@ "VMWare Sync": "", "VMware Snapshot": "", "Validate Certificates": "", - "Validate Remote Path": "", - "Validate effective ACL": "", + "Validate Remote Path": "Valider le chemin d'accès distant", + "Validate effective ACL": "Valider l'ACL effective", "Variant": "", "Vdevs spans enclosure": "", "Vendor ID": "", @@ -838,7 +838,7 @@ "Voltage": "", "WWPN": "", "WWPN (B)": "", - "Warning: iSCSI Target is currently in use.
": "", + "Warning: iSCSI Target is currently in use.
": "Avertissement : la cible iSCSI est en cours d'utilisation.
.", "Watch List": "", "Weak Ciphers": "", "WebDAV": "", @@ -3674,7 +3674,7 @@ "Power Off": "Éteindre", "Power Off UPS": "Mettre l'onduleur hors tension", "Power On Hours Ago": "Mise sous tension il y a quelques heures", - "Power Outage": "Panne de courant", + "Power Outage": "Coupure de courant", "Power Supply": "Alimentation électrique", "Pre Init": "Initialisation préalable", "Pre-script": "Pré-script", @@ -5318,4 +5318,4 @@ "{used} of {total} ({used_pct})": "{used} de {total} ({used_pct})", "{version} is available!": "{version} est disponible !", "{view} on {enclosure}": "{view} sur {enclosure}" -} \ No newline at end of file +} From 9e504297300ef3486506de3d5442daa4fb7c0194 Mon Sep 17 00:00:00 2001 From: Evgeny Stepanovych Date: Mon, 6 Jan 2025 16:33:27 -0500 Subject: [PATCH 17/18] NAS-133365 / 25.04 / Reporting exporters form is broken (#11282) --- .../api/api-call-directory.interface.ts | 4 +- .../reporting-exporters.interface.ts | 4 +- src/app/interfaces/schema.interface.ts | 2 +- ...reporting-exporters-form.component.spec.ts | 7 +- .../reporting-exporters-form.component.ts | 16 ++-- ...reporting-exporters-list.component.spec.ts | 13 ++- .../reporting-exporters-list.component.ts | 12 +-- src/assets/i18n/fr.json | 96 +++++++++---------- 8 files changed, 84 insertions(+), 70 deletions(-) diff --git a/src/app/interfaces/api/api-call-directory.interface.ts b/src/app/interfaces/api/api-call-directory.interface.ts index b536d92a127..378f7e124a0 100644 --- a/src/app/interfaces/api/api-call-directory.interface.ts +++ b/src/app/interfaces/api/api-call-directory.interface.ts @@ -199,7 +199,7 @@ import { ReplicationTask, } from 'app/interfaces/replication-task.interface'; import { - CreateReportingExporter, ReportingExporter, ReportingExporterSchema, UpdateReportingExporter, + ReportingExporter, ReportingExporterSchema, UpdateReportingExporter, } from 'app/interfaces/reporting-exporters.interface'; import { ReportingGraph } from 'app/interfaces/reporting-graph.interface'; import { @@ -695,7 +695,7 @@ export interface ApiCallDirectory { 'replication.update': { params: [id: number, update: Partial]; response: ReplicationTask }; // Reporting - 'reporting.exporters.create': { params: [CreateReportingExporter]; response: ReportingExporter }; + 'reporting.exporters.create': { params: [UpdateReportingExporter]; response: ReportingExporter }; 'reporting.exporters.delete': { params: [id: number]; response: boolean }; 'reporting.exporters.exporter_schemas': { params: void; response: ReportingExporterSchema[] }; 'reporting.exporters.query': { params: QueryParams; response: ReportingExporter[] }; diff --git a/src/app/interfaces/reporting-exporters.interface.ts b/src/app/interfaces/reporting-exporters.interface.ts index 2d0acff06ef..504917c6684 100644 --- a/src/app/interfaces/reporting-exporters.interface.ts +++ b/src/app/interfaces/reporting-exporters.interface.ts @@ -17,10 +17,8 @@ export interface ReportingExporterList { export interface ReportingExporter { name: string; id: number; - type: string; enabled: boolean; attributes: Record; } -export type CreateReportingExporter = Omit; -export type UpdateReportingExporter = Omit; +export type UpdateReportingExporter = Partial>; diff --git a/src/app/interfaces/schema.interface.ts b/src/app/interfaces/schema.interface.ts index 842ffe17c46..19a4fca0274 100644 --- a/src/app/interfaces/schema.interface.ts +++ b/src/app/interfaces/schema.interface.ts @@ -12,7 +12,7 @@ export interface OldSchema { type: SchemaType | SchemaType[]; _name_: string; _required_: boolean; - + const?: string; } export interface SchemaProperties { diff --git a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-form/reporting-exporters-form.component.spec.ts b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-form/reporting-exporters-form.component.spec.ts index 6994bab2afe..7871c4ac9b9 100644 --- a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-form/reporting-exporters-form.component.spec.ts +++ b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-form/reporting-exporters-form.component.spec.ts @@ -23,8 +23,8 @@ describe('ReportingExportersFormComponent', () => { const existingExporter: ReportingExporter = { name: 'test', id: 123, - type: ReportingExporterKey.Graphite, attributes: { + exporter_type: ReportingExporterKey.Graphite, access_key_id: 'access_key_id', secret_access_key: 'secret_access_key', }, @@ -94,11 +94,11 @@ describe('ReportingExportersFormComponent', () => { expect(spectator.inject(ApiService).call).toHaveBeenCalledWith('reporting.exporters.create', [{ name: 'exporter1', - type: ReportingExporterKey.Graphite, enabled: true, attributes: { access_key_id: 'abcde', secret_access_key: 'abcd', + exporter_type: ReportingExporterKey.Graphite, }, }]); expect(spectator.inject(SlideInRef).close).toHaveBeenCalled(); @@ -130,7 +130,7 @@ describe('ReportingExportersFormComponent', () => { expect(values).toEqual({ Name: existingExporter.name, - Type: existingExporter.type, + Type: existingExporter.attributes.exporter_type, Enable: existingExporter.enabled, 'Secret Access Key ID': existingExporter.attributes.secret_access_key, 'Access Key ID': existingExporter.attributes.access_key_id, @@ -163,6 +163,7 @@ describe('ReportingExportersFormComponent', () => { attributes: { secret_access_key: existingExporter.attributes.secret_access_key, access_key_id: 'efghi', + exporter_type: ReportingExporterKey.Graphite, }, }, ], diff --git a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-form/reporting-exporters-form.component.ts b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-form/reporting-exporters-form.component.ts index 9d044425eb7..bd30c3b310c 100644 --- a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-form/reporting-exporters-form.component.ts +++ b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-form/reporting-exporters-form.component.ts @@ -131,7 +131,10 @@ export class ReportingExportersFormComponent implements OnInit { this.createExporterControls(schemas); if (!this.isNew) { - this.form.patchValue(this.editingExporter); + this.form.patchValue({ + ...this.editingExporter, + type: this.editingExporter.attributes['exporter_type'] as string, + }); } this.isLoading = false; @@ -162,7 +165,7 @@ export class ReportingExportersFormComponent implements OnInit { for (const input of schema.schema) { this.form.controls.attributes.addControl( input._name_, - new FormControl('', input._required_ ? [Validators.required] : []), + new FormControl(input.const || '', input._required_ ? [Validators.required] : []), ); } } @@ -180,7 +183,9 @@ export class ReportingExportersFormComponent implements OnInit { } parseSchemaForDynamicSchema(schema: ReportingExporterSchema): DynamicFormSchemaNode[] { - return schema.schema.map((input) => getDynamicFormSchemaNode(input)); + return schema.schema + .filter((input) => !input.const) + .map((input) => getDynamicFormSchemaNode(input)); } parseSchemaForExporterList(schema: ReportingExporterSchema): ReportingExporterList { @@ -217,9 +222,8 @@ export class ReportingExportersFormComponent implements OnInit { ...this.form.value, }; - if (!this.isNew) { - delete values.type; - } + values.attributes['exporter_type'] = values.type; + delete values.type; for (const [key, value] of Object.entries(values.attributes)) { if (value == null || value === '') { diff --git a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.component.spec.ts b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.component.spec.ts index bc478c4578b..3b7299c51ea 100644 --- a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.component.spec.ts +++ b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.component.spec.ts @@ -1,6 +1,7 @@ import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { MatButtonHarness } from '@angular/material/button/testing'; +import { MatSlideToggleHarness } from '@angular/material/slide-toggle/testing'; import { Spectator, createComponentFactory, mockProvider } from '@ngneat/spectator/jest'; import { of } from 'rxjs'; import { mockCall, mockApi } from 'app/core/testing/utils/mock-api.utils'; @@ -23,10 +24,10 @@ const exporters: ReportingExporter[] = [ attributes: { secret: 'abcd', email: 'testemail', + exporter_type: ReportingExporterKey.Graphite, }, enabled: true, name: 'test', - type: ReportingExporterKey.Graphite, }, ]; @@ -98,6 +99,16 @@ describe('ReportingExportersListComponent', () => { expect(spectator.inject(ApiService).call).toHaveBeenCalledWith('reporting.exporters.delete', [1]); }); + it('updates a reporting exporter when Enabled checkbox is toggled', async () => { + const toggle = await table.getHarnessInCell(MatSlideToggleHarness, 1, 2); + await toggle.toggle(); + + expect(spectator.inject(ApiService).call).toHaveBeenCalledWith('reporting.exporters.update', [ + 1, + { enabled: false }, + ]); + }); + it('should show table rows', async () => { const expectedRows = [ ['Name', 'Type', 'Enabled', ''], diff --git a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.component.ts b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.component.ts index 86b4328beb1..1346943151e 100644 --- a/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.component.ts +++ b/src/app/pages/reports-dashboard/components/exporters/reporting-exporters-list/reporting-exporters-list.component.ts @@ -80,7 +80,7 @@ export class ReportingExporterListComponent implements OnInit { }), textColumn({ title: this.translate.instant('Type'), - propertyName: 'type', + getValue: (row) => row.attributes['exporter_type'], }), toggleColumn({ title: this.translate.instant('Enabled'), @@ -93,13 +93,13 @@ export class ReportingExporterListComponent implements OnInit { { name: row.name, checked: checked ? 'Enabling' : 'Disabling' }, ), ); - const exporter = { ...row }; - delete exporter.type; - delete exporter.id; - this.api.call('reporting.exporters.update', [row.id, { ...exporter, enabled: checked }]).pipe( + this.api.call('reporting.exporters.update', [row.id, { enabled: checked }]).pipe( untilDestroyed(this), ).subscribe({ - complete: () => this.appLoader.close(), + complete: () => { + this.appLoader.close(); + this.getExporters(); + }, error: (error: unknown) => this.errorCaught(error), }); }, diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index bf72ea89fb9..bf9a5ea2e4e 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -97,7 +97,6 @@ "Certificate Subject": "", "Certificate Write": "", "Check": "", - "Choose a new virtual port": "Choisissez un nouveau port virtuel", "Clear": "", "Client ID": "", "Close Instance Form": "", @@ -273,10 +272,8 @@ "HTTP Proxy:": "", "HTTPS Port": "", "HTTPS Redirect": "", - "Hardware Change": "Changement de matériel", "Has Allow List": "", "Healthy": "", - "Hide Password": "Masquer le mot de passe", "Home Widgets": "", "Host Model": "", "Host Mounts": "", @@ -390,7 +387,6 @@ "Machine": "", "Macvlan NICs": "", "Main menu": "", - "Maintenance Window": "Maintenance programmée", "Manual Test": "", "Manual Upgrade": "", "Mapall Group": "", @@ -444,12 +440,7 @@ "Network Interface Read": "", "Network Interface Write": "", "Network Reconnection Issue": "", - "Network Reset": "Réinitialisation du réseau", - "Network Settings": "Paramètres réseau", - "Network Stats": "Stats réseau", - "Network Traffic": "Trafic réseau", "Network Usage": "", - "Network Utilization": "Utilisation du réseau", "New Backup Credential": "", "New Bucket Name": "", "New CSR": "", @@ -503,7 +494,6 @@ "Parent Interface": "", "Passive Controller": "", "Passthrough": "", - "Password Login": "Mot de passe de connexion", "Pattern": "", "Pause Scrub": "", "Pending": "", @@ -519,9 +509,6 @@ "Privilege": "", "Privileges": "", "Processor": "", - "Product": "Produit", - "Product ID": "ID produit", - "Promote": "Promouvoir", "Prompt": "", "Properties Exclude": "", "Properties Override": "", @@ -540,15 +527,9 @@ "RAM": "", "Range Size": "", "Raw Filesize": "", - "Re-Open": "Réouvrir", - "Re-Open All Alerts": "Réouvrir toutes les alertes", "Read Only": "", "Readonly Admin": "", "Rear": "", - "Reason": "Raison", - "Reboot Local": "Redémarrage local", - "Reboot Remote": "Redémarrage à distance", - "Reboot Required": "Redémarrage requis", "Release": "", "Remove extent association": "", "Replication Admin": "", @@ -577,7 +558,6 @@ "Routing": "", "Rsync": "", "Run As Context": "", - "Running Jobs": "Tâches en cours", "S.M.A.R.T.": "", "SAS Connector": "", "SAS Expander": "", @@ -705,7 +685,6 @@ "Snapshot Time": "", "Snapshot Time {time}": "", "Snapshot Write": "", - "Software Installation": "Installation logiciel", "Sort": "", "Source Path": "", "Spares": "", @@ -731,10 +710,7 @@ "Support License": "", "Support Read": "", "Support Write": "", - "Switch To Advanced": "Basculer vers les optiond avancées", - "Switch To Wizard": "Basculer vers l'assistant", "Synced": "", - "Syslog Settings": "Paramètres Syslog", "Syslog TLS Certificate": "", "Syslog TLS Certificate Authority": "", "System Advanced Read": "", @@ -743,25 +719,13 @@ "System Audit Write": "", "System Data Pool": "", "System Dataset": "", - "System Freeze": "Freeze du système", "System General Read": "", "System General Write": "", - "System Image": "Image système", "System Information – Active": "", "System Information – Standby": "", - "System Overload": "Surcharge du système", - "System Reports": "Rapports système", - "System Security Settings": "Paramètres de sécurité du système", "System Serial": "", - "System Stats": "Stats du système", - "System Update": "Mise à jour du système", - "System Uptime": "Durée de fonctionnement du système", - "System Utilization": "Utilisation du système", - "System Version": "Version du Système", - "TLS No Empty Fragments": "TLS Aucun fragment vide", "TLS Policy": "", "Table Actions of Expandable Table": "", - "Task": "Tâche", "Tenant Domain": "", "Terminal": "", "Test": "", @@ -771,8 +735,6 @@ "Timestamp": "", "Toggle Sidenav": "", "Tolerance Window": "", - "Toolbar": "Barre d'outils", - "Tools": "Outils", "Top": "", "Top bar": "", "Total": "", @@ -782,7 +744,6 @@ "Translate App": "", "Transmit Hash Policy": "", "Transport Encryption Behavior": "", - "Troubleshooting Issues": "Résolution des problèmes", "TrueCommand Read": "", "TrueCommand Write": "", "Tunable": "", @@ -790,14 +751,9 @@ "UNIX Charset": "", "URL": "", "Unix NSS Info": "", - "Unix Primary Group": "Groupe primaire Unix", - "Unix Socket": "Socket Unix", "Unlink": "", - "Updating pool settings": "Mise à jour des paramètres du volume", "Usage Collection": "", "Usages": "", - "Use an existing port": "Utiliser un port existant", - "Use current port": "Utiliser le port actuel", "User API Keys": "", "User Bind Path": "", "User CN": "", @@ -819,8 +775,6 @@ "VMWare Sync": "", "VMware Snapshot": "", "Validate Certificates": "", - "Validate Remote Path": "Valider le chemin d'accès distant", - "Validate effective ACL": "Valider l'ACL effective", "Variant": "", "Vdevs spans enclosure": "", "Vendor ID": "", @@ -838,7 +792,6 @@ "Voltage": "", "WWPN": "", "WWPN (B)": "", - "Warning: iSCSI Target is currently in use.
": "Avertissement : la cible iSCSI est en cours d'utilisation.
.", "Watch List": "", "Weak Ciphers": "", "WebDAV": "", @@ -1624,6 +1577,7 @@ "Choose a date format.": "Choisissez un format de date.", "Choose a location to store the installer image file.": "Choisissez un emplacement pour stocker le fichier image du programme d'installation.", "Choose a new disk for the pool. To protect any existing data, adding the selected disk is stopped when the disk is already in use or has partitions present.": "Choisissez un nouveau disque pour la volume. Pour protéger les données existantes, l'ajout du disque sélectionné est arrêté lorsque le disque est déjà utilisé ou qu'il y a des partitions présentes.", + "Choose a new virtual port": "Choisissez un nouveau port virtuel", "Choose a path to the user's home directory. If the directory exists and matches the username, it is set as the user's home directory. When the path does not end with a subdirectory matching the username, a new subdirectory is created only if the 'Create Home Directory' field is marked checked. The full path to the user's home directory is shown here when editing a user.": "Choisissez un chemin vers le répertoire personnel de l'utilisateur. Si le répertoire existe et correspond au nom d'utilisateur, il est défini comme répertoire personnel de l'utilisateur. Lorsque le chemin ne se termine pas par un sous-répertoire correspondant au nom d'utilisateur, un nouveau sous-répertoire est créé uniquement si le champ 'Créer un répertoire personnel' est coché. Le chemin complet vers le répertoire personnel de l'utilisateur est affiché ici lors de l'édition d'un utilisateur.", "Choose a pool for Apps": "Choisissez un volume pour les applications", "Choose a pool to scrub.": "Choisissez un volume à nettoyer.", @@ -2738,6 +2692,7 @@ "HTTP host URL.": "URL de l'hôte HTTP.", "HTTPS Protocols": "Protocoles HTTPS", "Hardware": "Matériel", + "Hardware Change": "Changement de matériel", "Hardware Disk Encryption": "Chiffrement matériel du disque", "Help": "Aide", "Hidden": "Caché", @@ -2745,6 +2700,7 @@ "Hide": "Cacher", "Hide Extra Columns": "Cacher les colonnes supplémentaires", "Hide Job": "Cacher la tâche", + "Hide Password": "Masquer le mot de passe", "Hide Standard Error": "Masquer l'erreur standard", "Hide Standard Output": "Masquer la sortie standard", "Hide Stderr": "Masquer Stderr", @@ -3133,6 +3089,7 @@ "Mac Address": "Adresse Mac", "Machine Time: {machineTime} \n Browser Time: {browserTime}": "Machine Time: {machineTime} \n Heure du navigateur : {browserTime}", "Mail Server Port": "Port du serveur de messagerie", + "Maintenance Window": "Maintenance programmée", "Major": "Majeure", "Make Destination Dataset Read-only?": "Rendre le dataset de destination en lecture seule ?", "Make the currently active TrueNAS controller the default when both TrueNAS controllers are online and HA is enabled. To change the default TrueNAS controller, unset this option on the default TrueNAS controller and allow the system to fail over. This briefly interrupts system services.": "Faire du contrôleur TrueNAS actuellement actif le contrôleur par défaut lorsque les deux contrôleurs TrueNAS sont en ligne et que HA est activé. Pour changer le contrôleur TrueNAS par défaut, désactivez cette option sur le contrôleur TrueNAS par défaut et permettez au système de basculer. Cela interrompt brièvement les services du système.", @@ -3310,7 +3267,12 @@ "Network Configuration": "Configuration réseau", "Network Interface": "Interface réseau", "Network Reports": "Rapports réseau", + "Network Reset": "Réinitialisation du réseau", + "Network Settings": "Paramètres réseau", + "Network Stats": "Stats réseau", "Network Timeout Before Initiating Failover": "Délai d'attente réseau avant d'initier le basculement", + "Network Traffic": "Trafic réseau", + "Network Utilization": "Utilisation du réseau", "Network addresses allowed to use this initiator. Leave blank to allow all networks or list network addresses with a CIDR mask. Separate entries by pressing Enter.": "Les adresses de réseau sont autorisées à utiliser cet initiateur. Laissez vide pour autoriser tous les réseaux ou pour énumérer les adresses de réseau avec un masque CIDR. Séparez les entrées en appuyant sur la touche Entrée.", "Network addresses allowed use this initiator. Each address can include an optional CIDR netmask. Click + to add the network address to the list. Example: 192.168.2.0/24.": "Les adresses réseau autorisées utilisent cet initiateur. Chaque adresse peut inclure un masque de réseau CIDR en option. Cliquez sur + pour ajouter l'adresse réseau à la liste. Exemple : 192.168.2.0.0/24.", "Network changes applied successfully.": "Les modifications apportées au réseau ont été appliquées avec succès.", @@ -3560,6 +3522,7 @@ "Passphrase value must match Confirm Passphrase": "La valeur de la passphrase doit correspondre à : Confirmer la passphrase", "Password": "Mot de passe", "Password Disabled": "Mot de passe désactivé", + "Password Login": "Mot de passe de connexion", "Password Login Groups": "Groupes de connexion par mot de passe", "Password Server": "Serveur de mots de passe", "Password Servers": "Serveurs de mots de passe", @@ -3701,10 +3664,13 @@ "Proactive support settings is not available.": "Les paramètres de support proactif ne sont pas disponibles.", "Proceed": "Procéder", "Proceed with upgrading the pool? WARNING: Upgrading a pool is a one-way operation that might make some features of the pool incompatible with older versions of TrueNAS: ": "Procéder à la mise à niveau du volume ? AVERTISSEMENT : La mise à niveau d'un volume est une opération à sens unique qui pourrait rendre certaines fonctionnalités du volume incompatibles avec les anciennes versions de TrueNAS: ", + "Product": "Produit", + "Product ID": "ID produit", "Production": "Production", "Production status successfully updated": "Statut de production mis à jour avec succès", "Profile": "Profil", "Prohibits writes to this share.": "Interdit les écritures sur ce partage.", + "Promote": "Promouvoir", "Prototyping": "Prototypage", "Provide helpful notations related to the share, e.g. ‘Shared to everybody’. Maximum length is 120 characters.": "Fournissez des notations utiles liées au partage, par ex. « Partagé avec tout le monde ». La longueur maximale est de 120 caractères.", "Provide keys/passphrases manually": "Fournir manuellement les clés/phrases de passe", @@ -3744,6 +3710,8 @@ "Range Low and Range High set the range of UID/GID numbers which this IDMap backend translates. If an external credential like a Windows SID maps to a UID or GID number outside this range, the external credential is ignored.": "Range Low et Range High définissent la plage de numéros UID/GID que ce backend IDMap traduit. Si un identifiant externe tel qu'un SID Windows correspond à un numéro UID ou GID en dehors de cette plage, l'identifiant externe est ignoré.", "Rate this page": "Évaluer cette page", "Raw File": "Fichier brut", + "Re-Open": "Réouvrir", + "Re-Open All Alerts": "Réouvrir toutes les alertes", "Read": "Lire", "Read ACL": "Lire ACL", "Read Attributes": "Lire les attributs", @@ -3752,7 +3720,11 @@ "Read Named Attributes": "Lire les attributs nommés", "Read-only": "Lecture seule", "Realm": "Domaine", + "Reason": "Raison", "Reboot": "Redémarrer", + "Reboot Local": "Redémarrage local", + "Reboot Remote": "Redémarrage à distance", + "Reboot Required": "Redémarrage requis", "Reboot of the other node is required for FIPS changes.": "Le redémarrage de l'autre nœud est requis pour les modifications FIPS.", "Reboot of this node is required for FIPS changes.": "Le redémarrage de ce nœud est requis pour les modifications FIPS.", "Rebuild Directory Service Cache": "Reconstruction du cache du service d'annuaire", @@ -3959,6 +3931,7 @@ "Run «{name}» Cloud Sync now?": "Exécuter cette synchronisation cloud maintenant ?", "Run «{name}» Rsync now?": "Exécuter «{name}» Rsync maintenant?", "Running": "Actif", + "Running Jobs": "Tâches en cours", "S.M.A.R.T. Extra Options": "Options supplémentaires S.M.A.R.T.", "S.M.A.R.T. Info for {disk}": "Informations S.M.A.R.T. pour {disk}", "S.M.A.R.T. Options": "Options S.M.A.R.T", @@ -4396,6 +4369,7 @@ "Snapshots could not be loaded": "Les instantanés ne peuvent pas être chargés", "Snapshots must not have dependent clones": "Les instantanés ne doivent pas avoir de clones dépendants", "Snapshots will be created automatically.": "Les instantanés seront créés automatiquement.", + "Software Installation": "Installation logiciel", "Some of the disks are attached to the exported pools\n mentioned in this list. Checking a pool name means you want to\n allow reallocation of the disks attached to that pool.": "Certains des disques sont attachés aux volumes exportés\n mentionnés dans cette liste. Cocher un nom de volume signifie que vous souhaitez\n autoriser la réallocation des disques attachés à ce volume.", "Some of the selected disks have exported pools on them. Using those disks will make existing pools on them unable to be imported. You will lose any and all data in selected disks.": "Certains des disques sélectionnés contiennent des volumes exportés. L'utilisation de ces disques empêchera l'importation des volumes existants sur ces disques. Vous perdrez toutes les données des disques sélectionnés.", "Source": "Source", @@ -4503,7 +4477,9 @@ "Sun": "Dim", "Sunday": "Dimanche", "Support": "Support", + "Switch To Advanced": "Basculer vers les optiond avancées", "Switch To Basic": "Basculé sur Basique", + "Switch To Wizard": "Basculer vers l'assistant", "Switch Train": "Changer de canal", "Switch to Advanced Options": "Basculer sur Options avancées", "Switch update trains?": "Changer de canal de mise à jour ?", @@ -4524,14 +4500,25 @@ "Syslog": "Syslog", "Syslog Level": "Niveau Syslog", "Syslog Server": "Serveur syslog", + "Syslog Settings": "Paramètres Syslog", "Syslog Transport": "Transport Syslog", "System": "Système", "System Clock": "Horloge système", "System Dataset Pool": "Volume du dataset système", + "System Freeze": "Freeze du système", + "System Image": "Image système", "System Information": "Informations système", + "System Overload": "Surcharge du système", + "System Reports": "Rapports système", "System Security": "Sécurité du système", + "System Security Settings": "Paramètres de sécurité du système", "System Security Settings Updated.": "Paramètres de sécurité du système mis à jour.", + "System Stats": "Stats du système", "System Time Zone:": "Fuseau horaire du système :", + "System Update": "Mise à jour du système", + "System Uptime": "Durée de fonctionnement du système", + "System Utilization": "Utilisation du système", + "System Version": "Version du Système", "System dataset updated.": "Le dataset système a été mis à jour.", "System domain name, like example.com": "Nom de domaine système, comme exemple.com", "System hostname.": "Nom d'hôte du système.", @@ -4550,6 +4537,7 @@ "TLS Export Certificate Data": "TLS - Données du certificat d'exportation", "TLS Export Standard Vars": "TLS - Variables standard d'exportation", "TLS IP Address Required": "TLS - Aucun fragment vide", + "TLS No Empty Fragments": "TLS Aucun fragment vide", "TLS No Session Reuse Required": "TLS - Aucune réutilisation de session requise", "Tag": "Étiquette", "Tags": "Tags", @@ -4565,6 +4553,7 @@ "Target dataset encryption will be inherited from its parent dataset.": "Le chiffrement du dataset cible sera hérité de son dataset parent.", "Target with this name already exists": "La cible portant ce nom existe déjà", "Targets": "Cibles", + "Task": "Tâche", "Task Details for {task}": "Détails de la tâche pour {task}", "Task Name": "Nom de la tâche", "Task Settings": "Paramètres des tâches", @@ -4793,6 +4782,8 @@ "Token created with Google Drive.": "Jeton créé avec Google Drive.", "Token created with Google Drive. Access Tokens expire periodically and must be refreshed.": "Token créé avec Google Drive. Les jetons d'accès expirent périodiquement et doivent être actualisés.", "Token expired": "Le jeton a expiré", + "Toolbar": "Barre d'outils", + "Tools": "Outils", "Top level of the LDAP directory tree to be used when searching for resources. Example: dc=test,dc=org.": "Niveau supérieur de l'arborescence LDAP à utiliser lors de la recherche de ressources. Exemple : dc=test,dc=org.", "Topic Amazon Resource Name (ARN) for publishing. Example: arn:aws:sns:us-west-2:111122223333:MyTopic.": "Sujet Amazon Resource Name (ARN) pour la publication. Exemple : arn:aws:aws:sns:us-west-2:11112122223333:MyTopic.", "Topology": "Topologie", @@ -4814,6 +4805,7 @@ "Transport Options": "Options de transport", "Traverse": "Traverser", "Treat Disk Size as Minimum": "Considérer la taille du disque comme minimum", + "Troubleshooting Issues": "Résolution des problèmes", "TrueCloud Backup Tasks": "Tâches de sauvegarde TrueCloud", "TrueCommand": "TrueCommand", "TrueCommand Cloud Service": "Service Cloud TrueCommand", @@ -4878,6 +4870,8 @@ "Unit": "Unité", "Unix Permissions": "Permissions Unix", "Unix Permissions Editor": "Éditeur de permissions Unix", + "Unix Primary Group": "Groupe primaire Unix", + "Unix Socket": "Socket Unix", "Unkeep": "Enlever", "Unknown": "Inconnu", "Unknown CPU": "CPU inconnu", @@ -4936,6 +4930,7 @@ "Updating Instance": "Mise à jour de l'instance", "Updating custom app": "Mise à jour de l'app personnalisée", "Updating key type": "Mise à jour du type de clé", + "Updating pool settings": "Mise à jour des paramètres du volume", "Updating settings": "Mise à jour des paramètres", "Upgrade": "Mise à niveau ", "Upgrade All Selected": "Mettre à jour tous les éléments sélectionnés", @@ -4977,9 +4972,11 @@ "Use Sudo For ZFS Commands": "Utiliser Sudo pour les commandes ZFS", "Use Syslog Only": "Utiliser uniquement Syslog", "Use all disk space": "Utiliser tout l'espace disque", + "Use an existing port": "Utiliser un port existant", "Use an exported encryption key file to unlock datasets.": "Utilisez un fichier de clé de chiffrement exporté pour déverrouiller les datasets.", "Use as Home Share": "Utiliser comme partage d'accueil (home)", "Use compressed WRITE records to make the stream more efficient. The destination system must also support compressed WRITE records. See zfs(8).": "Utilisez des enregistrements WRITE compressés pour rendre le flux plus efficace. Le système de destination doit également prendre en charge les enregistrements WRITE compressés. Voir zfs(8).", + "Use current port": "Utiliser le port actuel", "Use default network settings": "Utiliser les paramètres réseau par défaut", "Use existing disk image": "Utiliser l'image disque existante", "Use settings from a saved replication.": "Utilisez les paramètres d'une réplication enregistrée.", @@ -5053,6 +5050,8 @@ "VMware Sync": "VMWare Sync", "VMware: Extent block size 512b, TPC enabled, no Xen compat mode, SSD speed": "VMware : taille de bloc étendue 512b, TPC activé, pas de mode de compatibilité Xen, vitesse SSD", "Validate Connection": "Valider la connexion", + "Validate Remote Path": "Valider le chemin d'accès distant", + "Validate effective ACL": "Valider l'ACL effective", "Value": "Valeur", "Value must be a number": "La valeur doit être un nombre", "Value must be a valid email address": "La valeur doit être une adresse email valide", @@ -5119,6 +5118,7 @@ "Warning": "Attention", "Warning!": "Attention !", "Warning: Debugs may contain log files with personal information such as usernames or other identifying information about your system. Please review debugs and redact any sensitive information before sharing with external entities.": "Avertissement : les debugs peuvent contenir des fichiers journaux contenant des informations personnelles telles que des noms d'utilisateur ou d'autres informations d'identification sur votre système. Veuillez examiner les debugs et rédiger toute information sensible avant de la partager avec des entités externes.", + "Warning: iSCSI Target is currently in use.
": "Avertissement : la cible iSCSI est en cours d'utilisation.
.", "Warning: {n} of {total} boot environments could not be deleted.": "Avertissement: {n} sur {total} environments de boot n'ont pas pu être supprimés.", "Warning: {n} of {total} docker images could not be deleted.": "Avertissement: {n} sur {total} images docker n'ont pas pu être supprimées.", "Warning: {n} of {total} snapshots could not be deleted.": "Avertissement: {n} sur {total} instantanés n'ont pas pu être supprimés.", @@ -5318,4 +5318,4 @@ "{used} of {total} ({used_pct})": "{used} de {total} ({used_pct})", "{version} is available!": "{version} est disponible !", "{view} on {enclosure}": "{view} sur {enclosure}" -} +} \ No newline at end of file From 474b93c39dab57ab09851e572f6b3284876941fa Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Tue, 7 Jan 2025 12:08:20 +0500 Subject: [PATCH 18/18] NAS-133118: Add tests (#11288) --- .../ix-forms/services/ix-form.service.spec.ts | 122 ++++++++++-------- .../ix-forms/services/ix-form.service.ts | 8 +- 2 files changed, 71 insertions(+), 59 deletions(-) diff --git a/src/app/modules/forms/ix-forms/services/ix-form.service.spec.ts b/src/app/modules/forms/ix-forms/services/ix-form.service.spec.ts index f1cf85bdf7e..bd78c0e4e4d 100644 --- a/src/app/modules/forms/ix-forms/services/ix-form.service.spec.ts +++ b/src/app/modules/forms/ix-forms/services/ix-form.service.spec.ts @@ -1,78 +1,90 @@ -import { NgControl } from '@angular/forms'; +import { ElementRef } from '@angular/core'; +import { FormControl, NgControl } from '@angular/forms'; import { SpectatorService, createServiceFactory } from '@ngneat/spectator/jest'; +import { TestScheduler } from 'rxjs/testing'; +import { getTestScheduler } from 'app/core/testing/utils/get-test-scheduler.utils'; +import { IxFormSectionComponent } from 'app/modules/forms/ix-forms/components/ix-form-section/ix-form-section.component'; +import { ixControlLabelTag } from 'app/modules/forms/ix-forms/directives/registered-control.directive'; import { IxFormService } from 'app/modules/forms/ix-forms/services/ix-form.service'; -// TODO: https://ixsystems.atlassian.net/browse/NAS-133118 -describe.skip('IxFormService', () => { +class MockNgControl extends NgControl { + override control = new FormControl('mock-value'); + + override viewToModelUpdate(newValue: string): void { + this.control.setValue(newValue); + } +} + +describe('IxFormService', () => { let spectator: SpectatorService; + let testScheduler: TestScheduler; const createService = createServiceFactory({ service: IxFormService, }); - const fakeComponents = [ - { - control: { - name: 'test_control_1', - }, - element: { - nativeElement: { - id: 'test_element_1', - }, - getAttribute: () => 'Test Element 1', - }, - }, - { - control: { - name: 'test_control_2', - }, - element: { - nativeElement: { - id: 'test_element_2', - }, - getAttribute: () => 'Test Element 2', - }, - }, - ] as { - control: NgControl; - element: { nativeElement: HTMLElement; getAttribute: () => string }; - }[]; - beforeEach(() => { spectator = createService(); - fakeComponents.forEach((component) => { - spectator.service.registerControl(component.control.name!.toString(), component.element); - }); + testScheduler = getTestScheduler(); }); - describe('getControlsNames', () => { - it('returns a list of control names', () => { - expect(spectator.service.getControlNames()).toEqual([ - 'test_control_1', - 'test_control_2', - ]); + describe('handles control register/unregister', () => { + it('registers control', () => { + const elRef = new ElementRef(document.createElement('input')); + elRef.nativeElement.setAttribute('id', 'control1'); + elRef.nativeElement.setAttribute(ixControlLabelTag, 'Control1'); + spectator.service.registerControl( + 'control1', + elRef, + ); + + expect(spectator.service.getControlNames()).toEqual(['control1']); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.controlNamesWithLabels$).toBe('a', { + a: [{ label: 'Control1', name: 'control1' }], + }); + }); + expect(spectator.service.getElementByControlName('control1')).toEqual(elRef.nativeElement); + expect(spectator.service.getElementByLabel('Control1')).toEqual(elRef.nativeElement); }); - }); - describe('getControls', () => { - it('returns a list of controls', () => { - expect(spectator.service.getControlNames()).toEqual([ - 'test_control_1', - 'test_control_2', - ]); + it('unregisters control', () => { + const elRef = new ElementRef(document.createElement('input')); + elRef.nativeElement.setAttribute('id', 'control1'); + elRef.nativeElement.setAttribute(ixControlLabelTag, 'Control1'); + spectator.service.registerControl( + 'control1', + elRef, + ); + + expect(spectator.service.getControlNames()).toEqual(['control1']); + spectator.service.unregisterControl('control1'); + expect(spectator.service.getControlNames()).toEqual([]); }); }); - describe('getControlByName', () => { - it('returns control by name', () => { - expect(spectator.service.getControlNames()).toEqual(['test_control_2']); + it('registers section control', () => { + const ngControl = new MockNgControl(); + const formSection = { + label(): string { return 'Form Section'; }, + } as IxFormSectionComponent; + spectator.service.registerSectionControl( + ngControl, + formSection, + ); + + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.controlSections$).toBe('a', { + a: [ + { section: formSection, controls: [ngControl] }, + ], + }); }); - }); - describe('getElementByControlName', () => { - it('returns element by control name', () => { - expect(spectator.service.getElementByControlName('test_control_2')).toEqual({ - id: 'test_element_2', + spectator.service.unregisterSectionControl(formSection, ngControl); + testScheduler.run(({ expectObservable }) => { + expectObservable(spectator.service.controlSections$).toBe('a', { + a: [], }); }); }); diff --git a/src/app/modules/forms/ix-forms/services/ix-form.service.ts b/src/app/modules/forms/ix-forms/services/ix-form.service.ts index 7ad6aa8a009..a2978b3270e 100644 --- a/src/app/modules/forms/ix-forms/services/ix-form.service.ts +++ b/src/app/modules/forms/ix-forms/services/ix-form.service.ts @@ -7,13 +7,13 @@ import { ixControlLabelTag } from 'app/modules/forms/ix-forms/directives/registe @Injectable({ providedIn: 'root' }) export class IxFormService { - private controls = new Map(); - private sections = new Map(); + private readonly controls = new Map(); + private readonly sections = new Map(); private readonly controlNamesWithlabels = new BehaviorSubject([]); private readonly controlSections = new BehaviorSubject([]); - controlNamesWithLabels$: Observable = this.controlNamesWithlabels.asObservable(); - controlSections$: Observable = this.controlSections.asObservable(); + readonly controlNamesWithLabels$: Observable = this.controlNamesWithlabels.asObservable(); + readonly controlSections$: Observable = this.controlSections.asObservable(); getControlNames(): (string | number | null)[] { return [...this.controls.keys()];