diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7df3892 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "composer" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/README.md b/README.md index 88d95ef..495a451 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,25 @@ Namingo is equipped with a comprehensive suite of features to meet the diverse n - **Automation Scripts**: Ensures the continuous and smooth operation of the registry by performing routine checks and operations. Advanced scripting capabilities also facilitate the generation of RDE deposits, the creation of ICANN's monthly reports, Spec 11 abuse monitoring, and ensure full compliance with other ICANN gTLD requirements for streamlined regulatory adherence. -## Installation Instructions +## Documentation -For detailed installation steps, please refer to [Install.md](docs/install.md). +We invite you to thoroughly review all the `.md` files within the `docs` directory to familiarize yourself with the various aspects of Namingo. These documents provide comprehensive guidance on installation, configuration, and initial operation, ensuring you have all the information you need to successfully manage your domain registry. + +### Installation Instructions + +#### Automated Install + +For a quick and easy installation, you can use the automated install script by running the following command: + +```bash +wget https://namingo.org/install.sh -O install.sh && chmod +x install.sh && ./install.sh +``` + +This command will download and execute the `install.sh` script from Namingo's website, which automates the installation process. + +#### Manual Installation Steps + +For detailed installation steps, please refer to [install.md](docs/install.md). ## Support diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d261e5d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,37 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 1.0-betaX | :white_check_mark: | +| < 1.0 | :x: | + +## Reporting a Vulnerability + +The Namingo team takes the security of our software seriously. If you believe you have found a security vulnerability in any version of our software, we would like you to let us know right away. We will investigate all legitimate reports and do our best to quickly fix the problem. + +Please follow these steps to report a vulnerability: + +1. **Do not report security vulnerabilities through public GitHub issues.** +2. Instead, please email us directly at [help@namingo.org](mailto:help@namingo.org). Provide a detailed description of the issue, including the following information: + - The version of the software that you are using + - A description of the vulnerability and how it can be reproduced + - The potential impact of the vulnerability +3. We will respond to your report within 48 hours to acknowledge receipt of your report and to outline the next steps in handling your submission. +4. After the initial reply to your report, the security team will endeavor to keep you informed of the progress being made towards a fix and full announcement. We may also ask for additional information or guidance. + +### Disclosure Policy + +When we receive a vulnerability report, our immediate priority is to confirm and fix the vulnerability. We ask that you do not publicly disclose the issue until we have had the chance to resolve it. + +We aim to handle all security issues transparently after the initial assessment phase. Once the issue is resolved, we will release a security advisory detailing the vulnerability, our response, and acknowledgments to the reporters. + +### Comments on this Policy + +If you have suggestions on how this process could be improved, please submit a pull request or issue. + +Thank you for helping to keep Namingo and its users safe. diff --git a/automation/audit.json b/automation/audit.json index bd39c78..e44ecaa 100644 --- a/automation/audit.json +++ b/automation/audit.json @@ -204,6 +204,10 @@ "audit": true, "skip": null }, + "users_audit": { + "audit": true, + "skip": null + }, "users_confirmations": { "audit": null, "skip": null diff --git a/automation/composer.json b/automation/composer.json index 6dfdd6b..8f51b88 100644 --- a/automation/composer.json +++ b/automation/composer.json @@ -6,6 +6,7 @@ "setbased/php-audit": "^1.9", "monolog/monolog": "^3.5", "league/flysystem": "^3.23", - "league/flysystem-sftp-v3": "^3.22" + "league/flysystem-sftp-v3": "^3.22", + "peppeocchi/php-cron-scheduler": "^4.0" } } diff --git a/automation/config.php.dist b/automation/config.php.dist index c2b318a..8f885a9 100644 --- a/automation/config.php.dist +++ b/automation/config.php.dist @@ -5,7 +5,7 @@ return [ 'db_type' => 'mysql', 'db_host' => 'localhost', 'db_port' => 3306, - 'db_database' => 'your_database_name', + 'db_database' => 'registry', 'db_username' => 'your_username', 'db_password' => 'your_password', @@ -50,7 +50,7 @@ return [ 'urs_imap_username' => 'your_username', 'urs_imap_password' => 'your_password', - // Notifications Configuration + // Message Broker Configuration 'mailer' => 'phpmailer', // sendgrid, mailgun are also available 'mailer_api_key' => 'YOUR_API_KEY', 'mailer_domain' => 'example.com', diff --git a/automation/cron.php b/automation/cron.php new file mode 100644 index 0000000..0ccab93 --- /dev/null +++ b/automation/cron.php @@ -0,0 +1,48 @@ + false, // Set to true to enable + 'backup' => false, // Set to true to enable + 'gtld_mode' => false, // Set to true to enable + 'spec11' => false, // Set to true to enable +]; + +require __DIR__ . '/vendor/autoload.php'; + +use GO\Scheduler; +$scheduler = new Scheduler(); + +$scheduler->php('/opt/registry/automation/write-zone.php')->at('*/15 * * * *'); +$scheduler->php('/opt/registry/automation/registrar.php')->at('35 * * * *'); +$scheduler->php('/opt/registry/automation/statistics.php')->at('59 * * * *'); +$scheduler->php('/opt/registry/automation/rdap-urls.php')->at('50 1 * * *'); +$scheduler->php('/var/www/cp/bin/file_cache.php')->at('0 0 * * 1'); + +$scheduler->php('/opt/registry/automation/change-domain-status.php')->at('30 * * * *'); +$scheduler->php('/opt/registry/automation/auto-approve-transfer.php')->at('45 * * * *'); +$scheduler->php('/opt/registry/automation/auto-clean-unused-contact-and-host.php')->at('5 0 * * *'); + +if ($cronJobConfig['accounting']) { + $scheduler->php('/opt/registry/automation/send-invoice.php')->at('1 0 1 * *'); +} + +if ($cronJobConfig['backup']) { + $scheduler->raw('/opt/registry/automation/vendor/bin/phpbu --configuration=/opt/registry/automation/backup.json')->at('15 * * * *'); + $scheduler->php('/opt/registry/automation/backup-upload.php')->at('30 * * * *'); +} + +if ($cronJobConfig['spec11']) { + $scheduler->php('/opt/registry/automation/abusemonitor.php')->at('30 * * * *'); + $scheduler->php('/opt/registry/automation/abusereport.php')->at('5 0 * * *'); +} + +if ($cronJobConfig['gtld_mode']) { + $scheduler->php('/opt/registry/automation/lordn.php')->at('10 0 * * *'); + $scheduler->php('/opt/registry/automation/tmch.php')->at('0 0,12 * * *'); + $scheduler->php('/opt/registry/automation/urs.php')->at('45 * * * *'); + $scheduler->php('/opt/registry/automation/escrow.php')->at('5 0 * * *'); + $scheduler->php('/opt/registry/automation/reporting.php')->at('1 0 1 * *'); +} + +$scheduler->run(); diff --git a/automation/crontab.example b/automation/crontab.example deleted file mode 100644 index 685128e..0000000 --- a/automation/crontab.example +++ /dev/null @@ -1,57 +0,0 @@ -#----------> Crontab Configuration for Namingo <--------------------------------# - -# run write-zone.php every 15 min. -*/15 * * * * root /usr/bin/php8.2 /opt/registry/automation/write-zone.php - -# run statistics.php at 59 min, every hour -59 * * * * root /usr/bin/php8.2 /opt/registry/automation/statistics.php - -# run backup at 15 min, every hour -15 * * * * /opt/registry/automation/vendor/bin/phpbu --configuration=/opt/registry/automation/backup.json - -# run backup-upload.php at 30 min, every hour -30 * * * * root /usr/bin/php8.2 /opt/registry/automation/backup-upload.php - -# run change-domain-status.php every hour -30 * * * * root /usr/bin/php8.2 /opt/registry/automation/change-domain-status.php - -# run auto-approve-transfer.php every hour -45 * * * * root /usr/bin/php8.2 /opt/registry/automation/auto-approve-transfer.php - -# run registrar.php every hour -35 * * * * root /usr/bin/php8.2 /opt/registry/automation/registrar.php - -# run abusemonitor.php every hour -30 * * * * root /usr/bin/php8.2 /opt/registry/automation/abusemonitor.php - -# run abusereport.php every day -5 0 * * * root /usr/bin/php8.2 /opt/registry/automation/abusereport.php - -# run lordn.php every day -10 0 * * * root /usr/bin/php8.2 /opt/registry/automation/lordn.php - -# run send-invoice.php every 1st day -1 0 1 * * root /usr/bin/php8.2 /opt/registry/automation/send-invoice.php - -# run auto-clean-unused-contact-and-host.php every day -5 0 * * * root /usr/bin/php8.2 /opt/registry/automation/auto-clean-unused-contact-and-host.php - -# run tmch.php twice a day -0 0,12 * * * root /usr/bin/php8.2 /opt/registry/automation/tmch.php - -# run escrow.php every day -5 0 * * * root /usr/bin/php8.2 /opt/registry/automation/escrow.php - -# run rdap-urls.php every day -50 1 * * * root /usr/bin/php8.2 /opt/registry/automation/rdap-urls.php - -# run file_cache.php every day -0 2 * * * root /usr/bin/php8.2 /var/www/cp/bin/file_cache.php - -# run reporting.php every 1st day -1 0 1 * * root /usr/bin/php8.2 /opt/registry/automation/reporting.php - -# run urs.php every hour -45 * * * * root /usr/bin/php8.2 /opt/registry/automation/urs.php - -#----------> End Crontab Configuration for Namingo <----------------------------# \ No newline at end of file diff --git a/automation/write-zone.php b/automation/write-zone.php index 47da7d0..3154160 100644 --- a/automation/write-zone.php +++ b/automation/write-zone.php @@ -140,14 +140,14 @@ Coroutine::create(function () use ($pool, $log, $c) { $completed_zone = $builder->build($zone); if ($c['dns_server'] == 'bind') { - $basePath = '/etc/bind/zones'; + $basePath = '/var/lib/bind'; } elseif ($c['dns_server'] == 'nsd') { $basePath = '/etc/nsd'; } elseif ($c['dns_server'] == 'knot') { $basePath = '/etc/knot'; } else { // Default path - $basePath = '/etc/bind/zones'; + $basePath = '/var/lib/bind'; } file_put_contents("{$basePath}/{$cleanedTld}.zone", $completed_zone); diff --git a/cp/app/Auth/Auth.php b/cp/app/Auth/Auth.php index 3017b7a..ae1643d 100644 --- a/cp/app/Auth/Auth.php +++ b/cp/app/Auth/Auth.php @@ -277,6 +277,23 @@ class Auth public static function changeCurrentPassword($oldPassword, $newPassword){ $auth = self::$auth; try { + global $container; + $db = $container->get('db'); + $currentDateTime = new \DateTime(); + $currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp + $db->insert( + 'users_audit', + [ + 'user_id' => $_SESSION['auth_user_id'], + 'user_event' => 'user.update.password', + 'user_resource' => 'control.panel', + 'user_agent' => $_SERVER['HTTP_USER_AGENT'], + 'user_ip' => get_client_ip(), + 'user_location' => get_client_location(), + 'event_time' => $currentDate, + 'user_data' => null + ] + ); $auth->changePassword($oldPassword, $newPassword); redirect()->route('profile')->with('success','Password has been changed'); } diff --git a/cp/app/Controllers/ApplicationsController.php b/cp/app/Controllers/ApplicationsController.php index 27a31fe..e8e11d5 100644 --- a/cp/app/Controllers/ApplicationsController.php +++ b/cp/app/Controllers/ApplicationsController.php @@ -5,6 +5,9 @@ namespace App\Controllers; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Container\ContainerInterface; +use Selective\XmlDSig\PublicKeyStore; +use Selective\XmlDSig\CryptoVerifier; +use Selective\XmlDSig\XmlSignatureVerifier; class ApplicationsController extends Controller { @@ -65,12 +68,8 @@ class ApplicationsController extends Controller $invalid_domain = validate_label($domainName, $db); if ($invalid_domain) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid domain name in application', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Invalid domain name'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } $valid_tld = false; @@ -85,12 +84,8 @@ class ApplicationsController extends Controller } if (!$valid_tld) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid domain extension in application', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Invalid domain extension'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } $domain_already_exist = $db->selectValue( @@ -99,12 +94,8 @@ class ApplicationsController extends Controller ); if ($domain_already_exist) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Application already exists', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Application already exists'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } $currentDateTime = new \DateTime(); @@ -122,25 +113,16 @@ class ApplicationsController extends Controller ); if ($phase_details !== 'Application') { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'The launch phase ' . $phaseType . ' is improperly configured. Please check the settings or contact support.', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: The launch phase ' . $phaseType . ' is improperly configured. Please check the settings or contact support.'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if ($phaseType === 'claims') { if (!isset($data['noticeid']) || $data['noticeid'] === '' || !isset($data['notafter']) || $data['notafter'] === '' || !isset($data['accepted']) || $data['accepted'] === '') { - // Trigger an error or handle the situation as needed - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => "Error: 'noticeid', 'notafter', or 'accepted' cannot be empty when phaseType is 'claims'", - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', "Error creating application: 'noticeid', 'notafter', or 'accepted' cannot be empty when phaseType is 'claims'"); + return $response->withHeader('Location', '/application/create')->withStatus(302); } $noticeid = $data['noticeid']; @@ -151,6 +133,66 @@ class ApplicationsController extends Controller $notafter = null; $accepted = null; } + + if ($phaseType === 'sunrise') { + if ($smd !== null && $smd !== '') { + // Extract the BASE64 encoded part + $beginMarker = "-----BEGIN ENCODED SMD-----"; + $endMarker = "-----END ENCODED SMD-----"; + $beginPos = strpos($smd, $beginMarker) + strlen($beginMarker); + $endPos = strpos($smd, $endMarker); + $encodedSMD = trim(substr($smd, $beginPos, $endPos - $beginPos)); + + // Decode the BASE64 content + $xmlContent = base64_decode($encodedSMD); + + // Load the XML content using DOMDocument + $domDocument = new \DOMDocument(); + $domDocument->preserveWhiteSpace = false; + $domDocument->formatOutput = true; + $domDocument->loadXML($xmlContent); + + // Parse data + $xpath = new \DOMXPath($domDocument); + $xpath->registerNamespace('smd', 'urn:ietf:params:xml:ns:signedMark-1.0'); + $xpath->registerNamespace('mark', 'urn:ietf:params:xml:ns:mark-1.0'); + + $notBefore = new \DateTime($xpath->evaluate('string(//smd:notBefore)')); + $notAfter = new \DateTime($xpath->evaluate('string(//smd:notAfter)')); + $markName = $xpath->evaluate('string(//mark:markName)'); + $labels = []; + foreach ($xpath->query('//mark:label') as $x_label) { + $labels[] = $x_label->nodeValue; + } + + if (!in_array($label, $labels)) { + $this->container->get('flash')->addMessage('error', 'Error creating application: SMD file is not valid for the application being created'); + return $response->withHeader('Location', '/application/create')->withStatus(302); + } + + // Check if current date and time is between notBefore and notAfter + $now = new \DateTime(); + if (!($now >= $notBefore && $now <= $notAfter)) { + $this->container->get('flash')->addMessage('error', 'Error creating application: Current time is outside the valid range in the SMD file'); + return $response->withHeader('Location', '/application/create')->withStatus(302); + } + + // Verify the signature + $publicKeyStore = new PublicKeyStore(); + $publicKeyStore->loadFromDocument($domDocument); + $cryptoVerifier = new CryptoVerifier($publicKeyStore); + $xmlSignatureVerifier = new XmlSignatureVerifier($cryptoVerifier); + $isValid = $xmlSignatureVerifier->verifyXml($xmlContent); + + if (!$isValid) { + $this->container->get('flash')->addMessage('error', 'Error creating application: The XML signature of the SMD file is not valid'); + return $response->withHeader('Location', '/application/create')->withStatus(302); + } + } else { + $this->container->get('flash')->addMessage('error', "Error creating application: SMD upload is required in the 'sunrise' phase."); + return $response->withHeader('Location', '/application/create')->withStatus(302); + } + } $domain_already_reserved = $db->selectValue( 'SELECT id FROM reserved_domain_names WHERE name = ? LIMIT 1', @@ -158,12 +200,8 @@ class ApplicationsController extends Controller ); if ($domain_already_reserved) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Domain name in application is reserved or restricted', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Domain name in application is reserved or restricted'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } $date_add = 12; @@ -177,21 +215,13 @@ class ApplicationsController extends Controller $price = $returnValue['price']; if (!$price) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'The price, period and currency for such TLD are not declared', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: The price, period and currency for such TLD are not declared'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if (($registrar_balance + $creditLimit) < $price) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Low credit: minimum threshold reached', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Low credit: minimum threshold reached'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } $nameservers = array_filter($data['nameserver'] ?? [], function($value) { @@ -206,31 +236,19 @@ class ApplicationsController extends Controller if (!empty($nameservers)) { if (count($nameservers) !== count(array_unique($nameservers))) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Duplicate nameservers detected. Please provide unique nameservers.', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Duplicate nameservers detected. Please provide unique nameservers.'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } foreach ($nameservers as $index => $nameserver) { if (preg_match("/^-|^\.-|-\.$|^\.$/", $nameserver)) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid hostName', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Invalid hostName'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if (!preg_match('/^([A-Z0-9]([A-Z0-9-]{0,61}[A-Z0-9]){0,1}\.){1,125}[A-Z0-9]([A-Z0-9-]{0,61}[A-Z0-9])$/i', $nameserver) && strlen($nameserver) < 254) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid hostName', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Invalid hostName'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } } } @@ -240,21 +258,13 @@ class ApplicationsController extends Controller $row = $db->selectRow('SELECT id, clid FROM contact WHERE identifier = ?', [$contactRegistrant]); if (!$row) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Registrant does not exist', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Registrant does not exist'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if ($clid != $row['clid']) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'The contact requested in the command does NOT belong to the current registrar', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: The contact requested in the command does NOT belong to the current registrar'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } } @@ -263,21 +273,13 @@ class ApplicationsController extends Controller $row = $db->selectRow('SELECT id, clid FROM contact WHERE identifier = ?', [$contactAdmin]); if (!$row) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Admin contact does not exist', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Admin contact does not exist'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if ($clid != $row['clid']) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'The contact requested in the command does NOT belong to the current registrar', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: The contact requested in the command does NOT belong to the current registrar'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } } @@ -286,21 +288,13 @@ class ApplicationsController extends Controller $row = $db->selectRow('SELECT id, clid FROM contact WHERE identifier = ?', [$contactTech]); if (!$row) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Tech contact does not exist', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Tech contact does not exist'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if ($clid != $row['clid']) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'The contact requested in the command does NOT belong to the current registrar', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: The contact requested in the command does NOT belong to the current registrar'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } } @@ -309,49 +303,29 @@ class ApplicationsController extends Controller $row = $db->selectRow('SELECT id, clid FROM contact WHERE identifier = ?', [$contactBilling]); if (!$row) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Billing contact does not exist', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Billing contact does not exist'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if ($clid != $row['clid']) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'The contact requested in the command does NOT belong to the current registrar', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: The contact requested in the command does NOT belong to the current registrar'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } } if (!$authInfo) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Missing application authinfo', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Missing application authinfo'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if (strlen($authInfo) < 6 || strlen($authInfo) > 16) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Password needs to be at least 6 and up to 16 characters long', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Password needs to be at least 6 and up to 16 characters long'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if (!preg_match('/[A-Z]/', $authInfo)) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Password should have both upper and lower case characters', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: Password should have both upper and lower case characters'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } $registrant_id = $db->selectValue( @@ -535,12 +509,8 @@ class ApplicationsController extends Controller if ($internal_host) { if (empty($nameserver_ipv4[$index]) && empty($nameserver_ipv6[$index])) { - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Error: No IPv4 or IPv6 addresses provided for internal host', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating application: No IPv4 or IPv6 addresses provided for internal host'); + return $response->withHeader('Location', '/application/create')->withStatus(302); } if (isset($nameserver_ipv4[$index]) && !empty($nameserver_ipv4[$index])) { @@ -604,20 +574,12 @@ class ApplicationsController extends Controller $db->commit(); } catch (Exception $e) { $db->rollBack(); - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Database failure: ' . $e->getMessage(), - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Database failure: ' . $e->getMessage()); + return $response->withHeader('Location', '/application/create')->withStatus(302); } catch (\Pinga\Db\Throwable\IntegrityConstraintViolationException $e) { $db->rollBack(); - return view($response, 'admin/domains/createApplication.twig', [ - 'domainName' => $domainName, - 'error' => 'Database failure: ' . $e->getMessage(), - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Database failure: ' . $e->getMessage()); + return $response->withHeader('Location', '/application/create')->withStatus(302); } $crdate = $db->selectValue( diff --git a/cp/app/Controllers/Auth/AuthController.php b/cp/app/Controllers/Auth/AuthController.php index 916d919..b4e3a2e 100644 --- a/cp/app/Controllers/Auth/AuthController.php +++ b/cp/app/Controllers/Auth/AuthController.php @@ -59,6 +59,8 @@ class AuthController extends Controller * @throws \Pinga\Auth\AuthError */ public function login(Request $request, Response $response){ + global $container; + $data = $request->getParsedBody(); if(isset($data['remember'])){ $remember = $data['remember']; @@ -71,8 +73,25 @@ class AuthController extends Controller $code = null; } $login = Auth::login($data['email'], $data['password'], $remember, $code); - if($login===true) + if($login===true) { + $db = $container->get('db'); + $currentDateTime = new \DateTime(); + $currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp + $db->insert( + 'users_audit', + [ + 'user_id' => $_SESSION['auth_user_id'], + 'user_event' => 'user.login', + 'user_resource' => 'control.panel', + 'user_agent' => $_SERVER['HTTP_USER_AGENT'], + 'user_ip' => get_client_ip(), + 'user_location' => get_client_location(), + 'event_time' => $currentDate, + 'user_data' => null + ] + ); redirect()->route('home'); + } } /** @@ -80,6 +99,23 @@ class AuthController extends Controller */ public function logout() { + global $container; + $db = $container->get('db'); + $currentDateTime = new \DateTime(); + $currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp + $db->insert( + 'users_audit', + [ + 'user_id' => $_SESSION['auth_user_id'], + 'user_event' => 'user.logout', + 'user_resource' => 'control.panel', + 'user_agent' => $_SERVER['HTTP_USER_AGENT'], + 'user_ip' => get_client_ip(), + 'user_location' => get_client_location(), + 'event_time' => $currentDate, + 'user_data' => null + ] + ); Auth::logout(); redirect()->route('login'); } diff --git a/cp/app/Controllers/ContactsController.php b/cp/app/Controllers/ContactsController.php index 1f4b535..73ba9e8 100644 --- a/cp/app/Controllers/ContactsController.php +++ b/cp/app/Controllers/ContactsController.php @@ -58,36 +58,21 @@ class ContactsController extends Controller $authInfo_pw = $data['authInfo'] ?? null; if (!$contactID) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Please provide a contact ID', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Please provide a contact ID'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } // Validation for contact ID $invalid_identifier = validate_identifier($contactID); if ($invalid_identifier) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => $invalid_identifier, - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: ' . $invalid_identifier); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } $contact = $db->select('SELECT * FROM contact WHERE identifier = ?', [$contactID]); if ($contact) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Contact ID already exists', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Contact ID already exists'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } $result = $db->selectRow('SELECT registrar_id FROM registrar_users WHERE user_id = ?', [$_SESSION['auth_user_id']]); @@ -100,104 +85,59 @@ class ContactsController extends Controller if ($postalInfoIntName) { if (!$postalInfoIntName) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Missing contact name', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Missing contact name'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoIntName) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoIntName)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid contact name', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid contact name'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if ($postalInfoIntOrg) { if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoIntOrg) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoIntOrg)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid contact org', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid contact org'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if ($postalInfoIntStreet1) { if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoIntStreet1) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoIntStreet1)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid contact street', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid contact street'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if ($postalInfoIntStreet2) { if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoIntStreet2) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoIntStreet2)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid contact street', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid contact street 2'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if ($postalInfoIntStreet3) { if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoIntStreet3) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoIntStreet3)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid contact street', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid contact street 3'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if (preg_match('/(^\-)|(^\.)|(\-\-)|(\.\.)|(\.\-)|(\-\.)|(\-$)|(\.$)/', $postalInfoIntCity) || !preg_match('/^[a-z][a-z\-\.\s]{3,}$/i', $postalInfoIntCity)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid contact city', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid contact city'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if ($postalInfoIntSp) { if (preg_match('/(^\-)|(^\.)|(\-\-)|(\.\.)|(\.\-)|(\-\.)|(\-$)|(\.$)/', $postalInfoIntSp) || !preg_match('/^[A-Z][a-zA-Z\-\.\s]{1,}$/', $postalInfoIntSp)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid contact state/province', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid contact state/province'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if ($postalInfoIntPc) { if (preg_match('/(^\-)|(\-\-)|(\-$)/', $postalInfoIntPc) || !preg_match('/^[A-Z0-9\-\s]{3,}$/', $postalInfoIntPc)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid contact postal code', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid contact postal code'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } @@ -205,167 +145,92 @@ class ContactsController extends Controller if ($postalInfoLocName) { if (!$postalInfoLocName) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Missing loc contact name', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Missing loc contact name'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoLocName) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoLocName)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid loc contact name', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid loc contact name'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if ($postalInfoLocOrg) { if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoLocOrg) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoLocOrg)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid loc contact org', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid loc contact org'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if ($postalInfoLocStreet1) { if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoLocStreet1) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoLocStreet1)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid loc contact street', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid loc contact street'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if ($postalInfoLocStreet2) { if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoLocStreet2) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoLocStreet2)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid loc contact street', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid loc contact street 2'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if ($postalInfoLocStreet3) { if (preg_match('/(^\-)|(^\,)|(^\.)|(\-\-)|(\,\,)|(\.\.)|(\-$)/', $postalInfoLocStreet3) || !preg_match('/^[a-zA-Z0-9\-\&\,\.\/\s]{5,}$/', $postalInfoLocStreet3)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid loc contact street', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid loc contact street 3'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if (preg_match('/(^\-)|(^\.)|(\-\-)|(\.\.)|(\.\-)|(\-\.)|(\-$)|(\.$)/', $postalInfoLocCity) || !preg_match('/^[a-z][a-z\-\.\s]{3,}$/i', $postalInfoLocCity)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid loc contact city', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid loc contact city'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if ($postalInfoLocSp) { if (preg_match('/(^\-)|(^\.)|(\-\-)|(\.\.)|(\.\-)|(\-\.)|(\-$)|(\.$)/', $postalInfoLocSp) || !preg_match('/^[A-Z][a-zA-Z\-\.\s]{1,}$/', $postalInfoLocSp)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid loc contact state/province', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid loc contact state/province'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } if ($postalInfoLocPc) { if (preg_match('/(^\-)|(\-\-)|(\-$)/', $postalInfoLocPc) || !preg_match('/^[A-Z0-9\-\s]{3,}$/', $postalInfoLocPc)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Invalid loc contact postal code', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Invalid loc contact postal code'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } } if ($voice && (!preg_match('/^\+\d{1,3}\.\d{1,14}$/', $voice) || strlen($voice) > 17)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Voice must be (\+[0-9]{1,3}\.[0-9]{1,14})', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Voice must be (\+[0-9]{1,3}\.[0-9]{1,14})'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if ($fax && (!preg_match('/^\+\d{1,3}\.\d{1,14}$/', $fax) || strlen($fax) > 17)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Fax must be (\+[0-9]{1,3}\.[0-9]{1,14})', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Fax must be (\+[0-9]{1,3}\.[0-9]{1,14})'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Email address failed check', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Email address failed check'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if (!$authInfo_pw) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Email contact authinfo', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Email contact authinfo missing'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if ((strlen($authInfo_pw) < 6) || (strlen($authInfo_pw) > 16)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Password needs to be at least 6 and up to 16 characters long', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Password needs to be at least 6 and up to 16 characters long'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } if (!preg_match('/[A-Z]/', $authInfo_pw)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'Password should have both upper and lower case characters', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: Password should have both upper and lower case characters'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } $disclose_voice = isset($data['disclose_voice']) ? 1 : 0; @@ -383,13 +248,8 @@ class ContactsController extends Controller $nin_type = (isset($data['isBusiness']) && $data['isBusiness'] === 'on') ? 'business' : 'personal'; if (!preg_match('/\d/', $nin)) { - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => 'NIN should contain one or more numbers', - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Unable to create contact: NIN should contain one or more numbers'); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } } @@ -480,13 +340,8 @@ class ContactsController extends Controller $db->commit(); } catch (Exception $e) { $db->rollBack(); - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'error' => $e->getMessage(), - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Database failure: ' . $e->getMessage()); + return $response->withHeader('Location', '/contact/create')->withStatus(302); } $crdate = $db->selectValue( @@ -494,13 +349,8 @@ class ContactsController extends Controller [$contact_id] ); - return view($response, 'admin/contacts/createContact.twig', [ - 'contactID' => $contactID, - 'crdate' => $crdate, - 'registrars' => $registrars, - 'countries' => $countries, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('success', 'Contact ' . $contactID . ' has been created successfully on ' . $crdate); + return $response->withHeader('Location', '/contacts')->withStatus(302); } $iso3166 = new ISO3166(); diff --git a/cp/app/Controllers/DomainsController.php b/cp/app/Controllers/DomainsController.php index f53c782..cb50d87 100644 --- a/cp/app/Controllers/DomainsController.php +++ b/cp/app/Controllers/DomainsController.php @@ -6,6 +6,9 @@ use App\Models\Domain; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Container\ContainerInterface; +use Selective\XmlDSig\PublicKeyStore; +use Selective\XmlDSig\CryptoVerifier; +use Selective\XmlDSig\XmlSignatureVerifier; class DomainsController extends Controller { @@ -145,13 +148,8 @@ class DomainsController extends Controller $invalid_domain = validate_label($domainName, $db); if ($invalid_domain) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid domain name', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid domain name'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } $valid_tld = false; @@ -166,13 +164,8 @@ class DomainsController extends Controller } if (!$valid_tld) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid domain extension', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid domain extension'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } $domain_already_exist = $db->selectValue( @@ -181,13 +174,8 @@ class DomainsController extends Controller ); if ($domain_already_exist) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Domain name already exists', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Domain name already exists'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } $currentDateTime = new \DateTime(); @@ -207,40 +195,20 @@ class DomainsController extends Controller if ($phase_details !== 'First-Come-First-Serve') { if ($phaseType !== 'none') { if ($phaseType == null && $phaseType == '') { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The launch phase ' . $phaseType . ' is improperly configured. Please check the settings or contact support.', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The launch phase ' . $phaseType . ' is improperly configured. Please check the settings or contact support.'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } else if ($phase_details == null) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The launch phase ' . $phaseType . ' is currently not active.', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The launch phase ' . $phaseType . ' is currently not active.'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } } else if ($phaseType !== 'none') { if ($phaseType == null && $phaseType == '') { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The launch phase ' . $phaseType . ' is improperly configured. Please check the settings or contact support.', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The launch phase ' . $phaseType . ' is improperly configured. Please check the settings or contact support.'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } else if ($phase_details == null) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The launch phase ' . $phaseType . ' is currently not active.', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The launch phase ' . $phaseType . ' is currently not active.'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } @@ -248,14 +216,8 @@ class DomainsController extends Controller if (!isset($data['noticeid']) || $data['noticeid'] === '' || !isset($data['notafter']) || $data['notafter'] === '' || !isset($data['accepted']) || $data['accepted'] === '') { - // Trigger an error or handle the situation as needed - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => "Error: 'noticeid', 'notafter', or 'accepted' cannot be empty when phaseType is 'claims'", - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', "Error creating domain: 'noticeid', 'notafter', or 'accepted' cannot be empty when phaseType is 'claims'"); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } $noticeid = $data['noticeid']; @@ -266,6 +228,66 @@ class DomainsController extends Controller $notafter = null; $accepted = null; } + + if ($phaseType === 'sunrise') { + if ($smd !== null && $smd !== '') { + // Extract the BASE64 encoded part + $beginMarker = "-----BEGIN ENCODED SMD-----"; + $endMarker = "-----END ENCODED SMD-----"; + $beginPos = strpos($smd, $beginMarker) + strlen($beginMarker); + $endPos = strpos($smd, $endMarker); + $encodedSMD = trim(substr($smd, $beginPos, $endPos - $beginPos)); + + // Decode the BASE64 content + $xmlContent = base64_decode($encodedSMD); + + // Load the XML content using DOMDocument + $domDocument = new \DOMDocument(); + $domDocument->preserveWhiteSpace = false; + $domDocument->formatOutput = true; + $domDocument->loadXML($xmlContent); + + // Parse data + $xpath = new \DOMXPath($domDocument); + $xpath->registerNamespace('smd', 'urn:ietf:params:xml:ns:signedMark-1.0'); + $xpath->registerNamespace('mark', 'urn:ietf:params:xml:ns:mark-1.0'); + + $notBefore = new \DateTime($xpath->evaluate('string(//smd:notBefore)')); + $notAfter = new \DateTime($xpath->evaluate('string(//smd:notAfter)')); + $markName = $xpath->evaluate('string(//mark:markName)'); + $labels = []; + foreach ($xpath->query('//mark:label') as $x_label) { + $labels[] = $x_label->nodeValue; + } + + if (!in_array($label, $labels)) { + $this->container->get('flash')->addMessage('error', 'Error creating domain: SMD file is not valid for the domain name being registered.'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); + } + + // Check if current date and time is between notBefore and notAfter + $now = new \DateTime(); + if (!($now >= $notBefore && $now <= $notAfter)) { + $this->container->get('flash')->addMessage('error', 'Error creating domain: Current time is outside the valid range in the SMD.'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); + } + + // Verify the signature + $publicKeyStore = new PublicKeyStore(); + $publicKeyStore->loadFromDocument($domDocument); + $cryptoVerifier = new CryptoVerifier($publicKeyStore); + $xmlSignatureVerifier = new XmlSignatureVerifier($cryptoVerifier); + $isValid = $xmlSignatureVerifier->verifyXml($xmlContent); + + if (!$isValid) { + $this->container->get('flash')->addMessage('error', 'Error creating domain: The XML signature of the SMD file is not valid.'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); + } + } else { + $this->container->get('flash')->addMessage('error', "Error creating domain: SMD upload is required in the 'sunrise' phase."); + return $response->withHeader('Location', '/domain/create')->withStatus(302); + } + } $domain_already_reserved = $db->selectValue( 'SELECT id FROM reserved_domain_names WHERE name = ? LIMIT 1', @@ -280,25 +302,15 @@ class DomainsController extends Controller $this->container->get('flash')->addMessage('error', 'Domain ' . $domainName . ' is not available: Allocation Token mismatch'); return $response->withHeader('Location', '/domain/create')->withStatus(302); } - } else { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Domain name is reserved or restricted', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + } else { + $this->container->get('flash')->addMessage('error', 'Error creating domain: Domain name is reserved or restricted'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } if ($registrationYears && (($registrationYears < 1) || ($registrationYears > 10))) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Domain period must be from 1 to 10', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Domain period must be from 1 to 10'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } elseif (!$registrationYears) { $registrationYears = 1; } @@ -323,23 +335,13 @@ class DomainsController extends Controller $price = $returnValue['price']; if (!$price) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The price, period and currency for such TLD are not declared', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The price, period and currency for such TLD are not declared'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if (($registrar_balance + $creditLimit) < $price) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Low credit: minimum threshold reached', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Low credit: minimum threshold reached'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } $nameservers = array_filter($data['nameserver'] ?? [], function($value) { @@ -354,34 +356,19 @@ class DomainsController extends Controller if (!empty($nameservers)) { if (count($nameservers) !== count(array_unique($nameservers))) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Duplicate nameservers detected. Please provide unique nameservers.', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Duplicate nameservers detected. Please provide unique nameservers.'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } foreach ($nameservers as $index => $nameserver) { if (preg_match("/^-|^\.-|-\.$|^\.$/", $nameserver)) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid hostName', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid hostName'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if (!preg_match('/^([A-Z0-9]([A-Z0-9-]{0,61}[A-Z0-9]){0,1}\.){1,125}[A-Z0-9]([A-Z0-9-]{0,61}[A-Z0-9])$/i', $nameserver) && strlen($nameserver) < 254) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid hostName', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid hostName'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } } @@ -391,23 +378,13 @@ class DomainsController extends Controller $row = $db->selectRow('SELECT id, clid FROM contact WHERE identifier = ?', [$contactRegistrant]); if (!$row) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Registrant does not exist', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Registrant does not exist'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if ($clid != $row['clid']) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The contact requested in the command does NOT belong to the current registrar', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The contact requested in the command does NOT belong to the current registrar'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } @@ -416,23 +393,13 @@ class DomainsController extends Controller $row = $db->selectRow('SELECT id, clid FROM contact WHERE identifier = ?', [$contactAdmin]); if (!$row) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Admin contact does not exist', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Admin contact does not exist'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if ($clid != $row['clid']) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The contact requested in the command does NOT belong to the current registrar', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The contact requested in the command does NOT belong to the current registrar'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } @@ -441,23 +408,13 @@ class DomainsController extends Controller $row = $db->selectRow('SELECT id, clid FROM contact WHERE identifier = ?', [$contactTech]); if (!$row) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Tech contact does not exist', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Tech contact does not exist'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if ($clid != $row['clid']) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The contact requested in the command does NOT belong to the current registrar', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The contact requested in the command does NOT belong to the current registrar'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } @@ -466,54 +423,29 @@ class DomainsController extends Controller $row = $db->selectRow('SELECT id, clid FROM contact WHERE identifier = ?', [$contactBilling]); if (!$row) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Billing contact does not exist', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Billing contact does not exist'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if ($clid != $row['clid']) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'The contact requested in the command does NOT belong to the current registrar', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: The contact requested in the command does NOT belong to the current registrar'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } if (!$authInfo) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Missing domain authinfo', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Missing domain authinfo'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if (strlen($authInfo) < 6 || strlen($authInfo) > 16) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Password needs to be at least 6 and up to 16 characters long', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Password needs to be at least 6 and up to 16 characters long'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if (!preg_match('/[A-Z]/', $authInfo)) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Password should have both upper and lower case characters', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Password should have both upper and lower case characters'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } $registrant_id = $db->selectValue( @@ -617,47 +549,27 @@ class DomainsController extends Controller // Validate keyTag if (!empty($dsKeyTag)) { if (!is_int($dsKeyTag)) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Incomplete key tag provided', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Incomplete key tag provided'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if ($dsKeyTag < 0 || $dsKeyTag > 65535) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Incomplete key tag provided', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Incomplete key tag provided'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } // Validate alg $validAlgorithms = [8, 13, 14, 15, 16]; if (!empty($dsAlg) && !in_array($dsAlg, $validAlgorithms)) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Incomplete algorithm provided', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Incomplete algorithm provided'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } // Validate digestType and digest if (!empty($dsDigestType) && !is_int($dsDigestType)) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Incomplete digest type provided', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Incomplete digest type provided'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } $validDigests = [ 2 => 64, // SHA-256 @@ -665,13 +577,8 @@ class DomainsController extends Controller ]; if (!empty($dsDigest)) { if (strlen($dsDigest) != $validDigests[$dsDigestType] || !ctype_xdigit($dsDigest)) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid digest length or format', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid digest length or format'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } } @@ -679,46 +586,26 @@ class DomainsController extends Controller // Validate flags $validFlags = [256, 257]; if (!empty($dnskeyFlags) && !in_array($dnskeyFlags, $validFlags)) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid flags provided', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid flags provided'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } // Validate protocol if (!empty($dnskeyProtocol) && $dnskeyProtocol != 3) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid protocol provided', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid protocol provided'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } // Validate algKeyData if (!empty($dnskeyAlg)) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid algorithm encoding', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid algorithm encoding'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } // Validate pubKey if (!empty($dnskeyPubKey) && base64_encode(base64_decode($dnskeyPubKey, true)) !== $dnskeyPubKey) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Invalid public key encoding', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: Invalid public key encoding'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if (!empty($dsKeyTag)) { @@ -874,13 +761,8 @@ class DomainsController extends Controller if ($internal_host) { if (empty($nameserver_ipv4[$index]) && empty($nameserver_ipv6[$index])) { - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Error: No IPv4 or IPv6 addresses provided for internal host', - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Error creating domain: No IPv4 or IPv6 addresses provided for internal host'); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } if (isset($nameserver_ipv4[$index]) && !empty($nameserver_ipv4[$index])) { @@ -965,22 +847,12 @@ class DomainsController extends Controller $db->commit(); } catch (Exception $e) { $db->rollBack(); - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Database failure: ' . $e->getMessage(), - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Database failure: ' . $e->getMessage()); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } catch (\Pinga\Db\Throwable\IntegrityConstraintViolationException $e) { $db->rollBack(); - return view($response, 'admin/domains/createDomain.twig', [ - 'domainName' => $domainName, - 'error' => 'Database failure: ' . $e->getMessage(), - 'registrars' => $registrars, - 'registrar' => $registrar, - 'launch_phases' => $launch_phases - ]); + $this->container->get('flash')->addMessage('error', 'Database failure: ' . $e->getMessage()); + return $response->withHeader('Location', '/domain/create')->withStatus(302); } $crdate = $db->selectValue( @@ -1020,7 +892,8 @@ class DomainsController extends Controller 'currencySymbol' => $symbol, 'currencyPosition' => $position, 'registrar' => $registrar, - 'launch_phases' => $launch_phases + 'launch_phases' => $launch_phases, + 'currency' => $currency, ]); } @@ -2000,7 +1873,8 @@ class DomainsController extends Controller 'maxYears' => $maxYears, 'currentUri' => $uri, 'currencySymbol' => $symbol, - 'currencyPosition' => $position + 'currencyPosition' => $position, + 'currency' => $currency ]); } else { // Domain does not exist, redirect to the domains view @@ -2635,6 +2509,7 @@ class DomainsController extends Controller 'registrars' => $registrars, 'currencySymbol' => $symbol, 'currencyPosition' => $position, + 'currency' => $currency ]); } diff --git a/cp/app/Controllers/FinancialsController.php b/cp/app/Controllers/FinancialsController.php index 38d3a3e..fad6b7e 100644 --- a/cp/app/Controllers/FinancialsController.php +++ b/cp/app/Controllers/FinancialsController.php @@ -105,9 +105,13 @@ class FinancialsController extends Controller $balance = $db->selectRow('SELECT name, accountBalance, creditLimit FROM registrar WHERE id = ?', [ $_SESSION["auth_registrar_id"] ] ); + $currency = $_SESSION['_currency']; + $stripe_key = envi('STRIPE_PUBLISHABLE_KEY'); return view($response,'admin/financials/deposit-registrar.twig', [ - 'balance' => $balance + 'balance' => $balance, + 'currency' => $currency, + 'stripe_key' => $stripe_key ]); } @@ -163,29 +167,25 @@ class FinancialsController extends Controller $db->commit(); } catch (Exception $e) { $db->rollBack(); - return view($response, 'admin/financials/deposit.twig', [ - 'error' => $e->getMessage(), - 'registrars' => $registrars - ]); + $this->container->get('flash')->addMessage('error', 'Database failure: '.$e->getMessage()); + return $response->withHeader('Location', '/deposit')->withStatus(302); } - return view($response, 'admin/financials/deposit.twig', [ - 'deposit' => $amount, - 'registrars' => $registrars - ]); + $this->container->get('flash')->addMessage('success', 'Deposit successfully added. The registrar\'s account balance has been updated.'); + return $response->withHeader('Location', '/deposit')->withStatus(302); } else { - return view($response, 'admin/financials/deposit.twig', [ - 'error' => 'Invalid entry: Deposit amount must be positive. Please enter a valid amount.', - 'registrars' => $registrars - ]); + $this->container->get('flash')->addMessage('error', 'Invalid entry: Deposit amount must be positive. Please enter a valid amount.'); + return $response->withHeader('Location', '/deposit')->withStatus(302); } } $db = $this->container->get('db'); $registrars = $db->select("SELECT id, clid, name FROM registrar"); + $currency = $_SESSION['_currency']; return view($response,'admin/financials/deposit.twig', [ - 'registrars' => $registrars + 'registrars' => $registrars, + 'currency' => $currency ]); } diff --git a/cp/app/Controllers/HostsController.php b/cp/app/Controllers/HostsController.php index 4ababfc..8c54cc7 100644 --- a/cp/app/Controllers/HostsController.php +++ b/cp/app/Controllers/HostsController.php @@ -37,20 +37,12 @@ class HostsController extends Controller if (preg_match('/^([A-Z0-9]([A-Z0-9-]{0,61}[A-Z0-9]){0,1}\.){1,125}[A-Z0-9]([A-Z0-9-]{0,61}[A-Z0-9])$/i', $hostName) && strlen($hostName) < 254) { $host_id_already_exist = $hostModel->getHostByNom($hostName); if ($host_id_already_exist) { - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'error' => 'host name already exists', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating host: host name ' . $hostName . ' already exists'); + return $response->withHeader('Location', '/host/create')->withStatus(302); } } else { - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'error' => 'Invalid host name', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating host: Invalid host name'); + return $response->withHeader('Location', '/host/create')->withStatus(302); } $result = $db->selectRow('SELECT registrar_id FROM registrar_users WHERE user_id = ?', [$_SESSION['auth_user_id']]); @@ -64,24 +56,16 @@ class HostsController extends Controller if ($ipv4) { $ipv4 = normalize_v4_address($ipv4); if (!filter_var($ipv4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'error' => 'Invalid host addr v4', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating host: Invalid host addr v4'); + return $response->withHeader('Location', '/host/create')->withStatus(302); } } if ($ipv6) { $ipv6 = normalize_v6_address($ipv6); if (!filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'error' => 'Invalid host addr v6', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating host: Invalid host addr v6'); + return $response->withHeader('Location', '/host/create')->withStatus(302); } } @@ -114,22 +98,14 @@ class HostsController extends Controller } if (!$domain_exist) { - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'error' => 'A host name object can NOT be created in a repository for which no superordinate domain name object exists', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating host: A host name object can NOT be created in a repository for which no superordinate domain name object exists'); + return $response->withHeader('Location', '/host/create')->withStatus(302); } if ($_SESSION['auth_roles'] !== 0) { if ($clid != $clid_domain) { - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'error' => 'The domain name belongs to another registrar, you are not allowed to create hosts for it', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating host: The domain name belongs to another registrar, you are not allowed to create hosts for it'); + return $response->withHeader('Location', '/host/create')->withStatus(302); } } @@ -151,12 +127,8 @@ class HostsController extends Controller $host_id = $db->getLastInsertId(); if (!$ipv4 && !$ipv6) { - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'error' => 'At least one of IPv4 or IPv6 must be provided', - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating host: At least one of IPv4 or IPv6 must be provided'); + return $response->withHeader('Location', '/host/create')->withStatus(302); } if ($ipv4) { @@ -195,12 +167,8 @@ class HostsController extends Controller $db->commit(); } catch (Exception $e) { $db->rollBack(); - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'error' => $e->getMessage(), - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('error', 'Database failure: ' . $e->getMessage()); + return $response->withHeader('Location', '/host/create')->withStatus(302); } $crdate = $db->selectValue( @@ -208,12 +176,8 @@ class HostsController extends Controller [$hostName] ); - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'crdate' => $crdate, - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('success', 'Host ' . $hostName . ' has been created successfully on ' . $crdate); + return $response->withHeader('Location', '/hosts')->withStatus(302); } else { $currentDateTime = new \DateTime(); $crdate = $currentDateTime->format('Y-m-d H:i:s.v'); @@ -242,12 +206,8 @@ class HostsController extends Controller [$hostName] ); - return view($response, 'admin/hosts/createHost.twig', [ - 'hostName' => $hostName, - 'crdate' => $crdate, - 'registrars' => $registrars, - 'registrar' => $registrar, - ]); + $this->container->get('flash')->addMessage('success', 'Host ' . $hostName . ' has been created successfully on ' . $crdate); + return $response->withHeader('Location', '/hosts')->withStatus(302); } } } diff --git a/cp/app/Controllers/ProfileController.php b/cp/app/Controllers/ProfileController.php index dad0ad8..fd2c1b6 100644 --- a/cp/app/Controllers/ProfileController.php +++ b/cp/app/Controllers/ProfileController.php @@ -73,12 +73,16 @@ class ProfileController extends Controller 'SELECT * FROM users_webauthn WHERE user_id = ?', [$userId] ); + $user_audit = $db->select( + 'SELECT * FROM users_audit WHERE user_id = ? ORDER BY event_time DESC', + [$userId] + ); if ($is_2fa_activated) { - return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue]); + return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'userAudit' => $user_audit]); } else if ($is_weba_activated) { - return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'qrcodeDataUri' => $qrcodeDataUri, 'secret' => $secret, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'weba' => $is_weba_activated]); + return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'qrcodeDataUri' => $qrcodeDataUri, 'secret' => $secret, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'weba' => $is_weba_activated, 'userAudit' => $user_audit]); } else { - return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'qrcodeDataUri' => $qrcodeDataUri, 'secret' => $secret, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue]); + return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'qrcodeDataUri' => $qrcodeDataUri, 'secret' => $secret, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'userAudit' => $user_audit]); } } @@ -114,6 +118,21 @@ class ProfileController extends Controller } try { + $currentDateTime = new \DateTime(); + $currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp + $db->insert( + 'users_audit', + [ + 'user_id' => $_SESSION['auth_user_id'], + 'user_event' => 'user.enable.2fa', + 'user_resource' => 'control.panel', + 'user_agent' => $_SERVER['HTTP_USER_AGENT'], + 'user_ip' => get_client_ip(), + 'user_location' => get_client_location(), + 'event_time' => $currentDate, + 'user_data' => null + ] + ); $db->update( 'users', [ diff --git a/cp/app/Controllers/RegistrarsController.php b/cp/app/Controllers/RegistrarsController.php index dae12d3..e62561f 100644 --- a/cp/app/Controllers/RegistrarsController.php +++ b/cp/app/Controllers/RegistrarsController.php @@ -98,10 +98,8 @@ class RegistrarsController extends Controller // Trim the final semicolon and space $errorText = rtrim($errorText, '; '); - return view($response, 'admin/registrars/create.twig', [ - 'countries' => $countries, - 'error' => $errorText, - ]); + $this->container->get('flash')->addMessage('error', 'Error creating registrar: ' . $errorText); + return $response->withHeader('Location', '/registrar/create')->withStatus(302); } $db->beginTransaction(); @@ -270,16 +268,12 @@ class RegistrarsController extends Controller $db->commit(); } catch (Exception $e) { $db->rollBack(); - return view($response, 'admin/registrars/create.twig', [ - 'error' => $e->getMessage(), - 'countries' => $countries, - ]); + $this->container->get('flash')->addMessage('error', 'Database failure: ' . $e->getMessage()); + return $response->withHeader('Location', '/registrar/create')->withStatus(302); } - return view($response,'admin/registrars/create.twig', [ - 'registrar' => $data['name'], - 'countries' => $countries, - ]); + $this->container->get('flash')->addMessage('success', 'Registrar ' . $data['name'] . ' successfully created and is now active.'); + return $response->withHeader('Location', '/registrars')->withStatus(302); } $iso3166 = new ISO3166(); @@ -334,7 +328,7 @@ class RegistrarsController extends Controller $registrarWhitelist = $db->select('SELECT addr FROM registrar_whitelist WHERE registrar_id = ?', [ $registrar['id'] ]); // Check if RegistrarOTE is not empty - if (!empty($registrarOte)) { + if (is_array($registrarOte) && !empty($registrarOte)) { // Split the results into two groups $firstHalf = array_slice($registrarOte, 0, 5); $secondHalf = array_slice($registrarOte, 5); diff --git a/cp/app/Controllers/SupportController.php b/cp/app/Controllers/SupportController.php index c3bb86f..89f846f 100644 --- a/cp/app/Controllers/SupportController.php +++ b/cp/app/Controllers/SupportController.php @@ -72,12 +72,9 @@ class SupportController extends Controller 'categories' => $categories ]); } - - return view($response, 'admin/support/view.twig', [ - 'categories' => $categories, - 'subject' => $subject, - ]); - + + $this->container->get('flash')->addMessage('success', 'Support ticket ' . $subject . ' has been created successfully!'); + return $response->withHeader('Location', '/support')->withStatus(302); } $db = $this->container->get('db'); diff --git a/cp/app/Controllers/SystemController.php b/cp/app/Controllers/SystemController.php index 695d8c7..3b4cd4e 100644 --- a/cp/app/Controllers/SystemController.php +++ b/cp/app/Controllers/SystemController.php @@ -6,6 +6,7 @@ use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Container\ContainerInterface; use Respect\Validation\Validator as v; +use League\ISO3166\ISO3166; class SystemController extends Controller { @@ -142,8 +143,19 @@ class SystemController extends Controller 'name' => "rdap_server" ] ); + + $db->update( + 'settings', + [ + 'value' => $data['currency'] + ], + [ + 'name' => "currency" + ] + ); $db->commit(); + $_SESSION['_currency'] = $data['currency']; } catch (Exception $e) { $db->rollBack(); $this->container->get('flash')->addMessage('error', 'Database failure: ' . $e->getMessage()); @@ -154,7 +166,9 @@ class SystemController extends Controller return $response->withHeader('Location', '/registry')->withStatus(302); } - + + $iso3166 = new ISO3166(); + $countries = $iso3166->all(); $db = $this->container->get('db'); $company_name = $db->selectValue("SELECT value FROM settings WHERE name = 'company_name'"); $vat_number = $db->selectValue("SELECT value FROM settings WHERE name = 'vat_number'"); @@ -166,6 +180,17 @@ class SystemController extends Controller $launch_phases = $db->selectValue("SELECT value FROM settings WHERE name = 'launch_phases'"); $whois_server = $db->selectValue("SELECT value FROM settings WHERE name = 'whois_server'"); $rdap_server = $db->selectValue("SELECT value FROM settings WHERE name = 'rdap_server'"); + $currency = $db->selectValue("SELECT value FROM settings WHERE name = 'currency'"); + + $uniqueCurrencies = []; + foreach ($countries as $country) { + // Assuming each country has a 'currency' field with an array of currencies + foreach ($country['currency'] as $currencyCode) { + if (!array_key_exists($currencyCode, $uniqueCurrencies)) { + $uniqueCurrencies[$currencyCode] = $currencyCode; // Or any other currency detail you have + } + } + } return view($response,'admin/system/registry.twig', [ 'company_name' => $company_name, @@ -177,7 +202,9 @@ class SystemController extends Controller 'handle' => $handle, 'launch_phases' => $launch_phases, 'whois_server' => $whois_server, - 'rdap_server' => $rdap_server + 'rdap_server' => $rdap_server, + 'uniqueCurrencies' => $uniqueCurrencies, + 'currency' => $currency ]); } @@ -930,19 +957,29 @@ class SystemController extends Controller return $response->withHeader('Location', '/registry/reserved')->withStatus(302); } - + $db = $this->container->get('db'); $types = $db->select("SELECT DISTINCT type FROM reserved_domain_names"); // Get the current URI $uri = $request->getUri()->getPath(); + // Set default types if $types is empty + if (empty($types)) { + $types = [ + ['type' => 'reserved'], + ['type' => 'restricted'] + ]; + } + $categories = []; foreach ($types as $type) { $typeNames = $db->select( 'SELECT name FROM reserved_domain_names WHERE type = ?', [ $type['type'] ] - ); - $categories[$type['type']] = array_column($typeNames, 'name'); + ); + + // Initialize the type with an empty array if no names are found + $categories[$type['type']] = $typeNames ? array_column($typeNames, 'name') : []; } return view($response,'admin/system/manageReserved.twig', [ diff --git a/cp/bin/create_admin_user.php b/cp/bin/create_admin_user.php index 9ccb24d..6e77b47 100644 --- a/cp/bin/create_admin_user.php +++ b/cp/bin/create_admin_user.php @@ -6,6 +6,7 @@ $dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..'); $dotenv->load(); // Retrieve database connection details from environment variables +$dbDriver = $_ENV['DB_DRIVER']; $dbHost = $_ENV['DB_HOST']; $dbName = $_ENV['DB_DATABASE']; $dbUser = $_ENV['DB_USERNAME']; @@ -26,12 +27,17 @@ $hashedPassword = password_hash($newPW, PASSWORD_ARGON2ID, $options); try { // Create PDO instance - $pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8", $dbUser, $dbPass); + if ($dbDriver == 'mysql') { + $dsn = "mysql:host=$dbHost;dbname=$dbName;charset=utf8"; + } elseif ($dbDriver == 'pgsql') { + $dsn = "pgsql:host=$dbHost;dbname=$dbName"; + } + $pdo = new PDO($dsn, $dbUser, $dbPass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // SQL query $sql = "INSERT INTO users (email, password, username, status, verified, resettable, roles_mask, registered, last_login, force_logout, tfa_secret, tfa_enabled, auth_method, backup_codes) - VALUES (:email, :password, :username, 0, 1, 1, 0, 1, NULL, 0, NULL, 0, 'password', NULL)"; + VALUES (:email, :password, :username, 0, 1, 1, 0, 1, NULL, 0, NULL, false, 'password', NULL)"; // Prepare and execute SQL statement $stmt = $pdo->prepare($sql); diff --git a/cp/bin/generate_translation.php b/cp/bin/generate_translation.php new file mode 100644 index 0000000..2b2f030 --- /dev/null +++ b/cp/bin/generate_translation.php @@ -0,0 +1,34 @@ +getRouteCollector()->getRouteParser(); require_once __DIR__ . '/database.php'; -// Known set of languages -$allowedLanguages = ['en_US', 'uk_UA', 'es_ES']; // Add more as needed - -if (isset($_SESSION['_lang']) && in_array($_SESSION['_lang'], $allowedLanguages)) { - // Use regex to validate the format: two letters, underscore, two letters - if (preg_match('/^[a-z]{2}_[A-Z]{2}$/', $_SESSION['_lang'])) { - $desiredLanguage = $_SESSION['_lang']; - $parts = explode('_', $_SESSION['_lang']); - if (isset($parts[1])) { - $uiLang = strtolower($parts[1]); - } - } else { - $desiredLanguage = 'en_US'; - $uiLang = 'us'; - } -} else { - $desiredLanguage = 'en_US'; - $uiLang = 'us'; -} -$lang_full = Language::getName($desiredLanguage, 'en'); -$lang = trim(strstr($lang_full, ' (', true)); - -$languageFile = '../lang/' . $desiredLanguage . '/messages.po'; -if (!file_exists($languageFile)) { - $desiredLanguage = 'en_US'; // Fallback - $languageFile = '../lang/en_US/messages.po'; -} - -$loader = new PoLoader(); -$translations = $loader->loadFile($languageFile); - $container->set('router', function () use ($routeParser) { return $routeParser; }); @@ -89,6 +66,11 @@ $container->set('pdo', function () use ($pdo) { }); $container->set('auth', function() { + //$responseFactory = new \Nyholm\Psr7\Factory\Psr17Factory(); + //$response = $responseFactory->createResponse(); + //$autoLogout = new \Pinga\Auth\AutoLogout(); + //$autoLogout->watch(900, '/', null, 301, $response); + return new \App\Auth\Auth; }); @@ -96,7 +78,7 @@ $container->set('flash', function() { return new \Slim\Flash\Messages; }); -$container->set('view', function ($container) use ($translations, $uiLang, $lang) { +$container->set('view', function ($container) { $view = Twig::create(__DIR__ . '/../resources/views', [ 'cache' => false, ]); @@ -104,6 +86,38 @@ $container->set('view', function ($container) use ($translations, $uiLang, $lang 'isLogin' => $container->get('auth')->isLogin(), 'user' => $container->get('auth')->user(), ]); + + // Known set of languages + $allowedLanguages = ['en_US', 'uk_UA', 'es_ES']; // Add more as needed + + if (isset($_SESSION['_lang']) && in_array($_SESSION['_lang'], $allowedLanguages)) { + // Use regex to validate the format: two letters, underscore, two letters + if (preg_match('/^[a-z]{2}_[A-Z]{2}$/', $_SESSION['_lang'])) { + $desiredLanguage = $_SESSION['_lang']; + $parts = explode('_', $_SESSION['_lang']); + if (isset($parts[1])) { + $uiLang = strtolower($parts[1]); + } + } else { + $desiredLanguage = 'en_US'; + $uiLang = 'us'; + } + } else { + $desiredLanguage = 'en_US'; + $uiLang = 'us'; + } + $lang_full = Language::getName($desiredLanguage, 'en'); + $lang = trim(strstr($lang_full, ' (', true)); + + $languageFile = '../lang/' . $desiredLanguage . '/messages.po'; + if (!file_exists($languageFile)) { + $desiredLanguage = 'en_US'; // Fallback + $languageFile = '../lang/en_US/messages.po'; + } + + $loader = new PoLoader(); + $translations = $loader->loadFile($languageFile); + $view->getEnvironment()->addGlobal('uiLang', $uiLang); $view->getEnvironment()->addGlobal('lang', $lang); $view->getEnvironment()->addGlobal('flash', $container->get('flash')); @@ -117,22 +131,20 @@ $container->set('view', function ($container) use ($translations, $uiLang, $lang } $db = $container->get('db'); - $query = 'SELECT r.currency, ru.registrar_id + $user_data = 'SELECT ru.registrar_id FROM registrar_users ru JOIN registrar r ON ru.registrar_id = r.id WHERE ru.user_id = ?'; + $currency_data = "SELECT value FROM settings WHERE name = 'currency'"; if (isset($_SESSION['auth_user_id'])) { - $result = $db->select($query, [$_SESSION['auth_user_id']]); + $result = $db->select($user_data, [$_SESSION['auth_user_id']]); + $db_currency = $db->select($currency_data); - // Default values - $_SESSION['_currency'] = 'USD'; + $_SESSION['_currency'] = $db_currency[0]['value']; $_SESSION['auth_registrar_id'] = null; // Default registrar_id if ($result !== null && count($result) > 0) { - if (isset($result[0]['currency'])) { - $_SESSION['_currency'] = $result[0]['currency']; - } if (isset($result[0]['registrar_id'])) { $_SESSION['auth_registrar_id'] = $result[0]['registrar_id']; } diff --git a/cp/bootstrap/database.php b/cp/bootstrap/database.php index 3e953d8..9e34d90 100644 --- a/cp/bootstrap/database.php +++ b/cp/bootstrap/database.php @@ -13,7 +13,7 @@ elseif (config('default') == 'sqlite') { } // PostgreSQL Connection elseif (config('default') == 'pgsql') { - $pdo = new \PDO($config['pgsql']['driver'].':dbname='.$config['pgsql']['database'].';host='.$config['pgsql']['host'].';charset='.$config['pgsql']['charset'].'', $config['pgsql']['username'], $config['pgsql']['password']); + $pdo = new \PDO($config['pgsql']['driver'].':dbname='.$config['pgsql']['database'].';host='.$config['pgsql']['host'].';', $config['pgsql']['username'], $config['pgsql']['password']); $db = \Pinga\Db\PdoDatabase::fromPdo($pdo); } // SQL Server Connection diff --git a/cp/bootstrap/helper.php b/cp/bootstrap/helper.php index cf4fde0..6078694 100644 --- a/cp/bootstrap/helper.php +++ b/cp/bootstrap/helper.php @@ -396,4 +396,33 @@ function createUuidFromId($id) { // Handle exception return null; } +} + +// Function to get the client IP address +function get_client_ip() { + $ipaddress = ''; + if (getenv('HTTP_CLIENT_IP')) + $ipaddress = getenv('HTTP_CLIENT_IP'); + else if(getenv('HTTP_X_FORWARDED_FOR')) + $ipaddress = getenv('HTTP_X_FORWARDED_FOR'); + else if(getenv('HTTP_X_FORWARDED')) + $ipaddress = getenv('HTTP_X_FORWARDED'); + else if(getenv('HTTP_FORWARDED_FOR')) + $ipaddress = getenv('HTTP_FORWARDED_FOR'); + else if(getenv('HTTP_FORWARDED')) + $ipaddress = getenv('HTTP_FORWARDED'); + else if(getenv('REMOTE_ADDR')) + $ipaddress = getenv('REMOTE_ADDR'); + else + $ipaddress = 'UNKNOWN'; + return $ipaddress; +} + +function get_client_location() { + $PublicIP = get_client_ip(); + $json = file_get_contents("http://ipinfo.io/$PublicIP/geo"); + $json = json_decode($json, true); + $country = $json['country']; + + return $country; } \ No newline at end of file diff --git a/cp/composer.json b/cp/composer.json index 9317730..6239389 100644 --- a/cp/composer.json +++ b/cp/composer.json @@ -16,17 +16,15 @@ "ext-pdo": "*", "slim/slim": "4.12.0", "slim/twig-view": "^3.3.0", - "monolog/monolog": "^3.4.0", + "monolog/monolog": "^3.5.0", "respect/validation": "^2.2.4", "slim/csrf": "^1.3", "slim/flash": "^0.4", - "vlucas/phpdotenv": "^5.5", - "php-di/php-di": "^7.0.5", - "nyholm/psr7": "^1.8", - "nyholm/psr7-server": "^1.0.2", - "pinga/auth": "^0.2.1", - "phpmailer/phpmailer": "^6.8.1", - "utopia-php/messaging": "^0.3.0", + "vlucas/phpdotenv": "^5.6", + "php-di/php-di": "^7.0.6", + "nyholm/psr7": "^1.8.1", + "nyholm/psr7-server": "^1.1.0", + "pinga/auth": "^0.3.3", "filp/whoops": "^2.15.3", "imefisto/psr-swoole-native": "^1.1.2", "chubbyphp/chubbyphp-static-file": "^1.2", @@ -43,12 +41,13 @@ "matthiasmullie/scrapbook": "^1.5", "guzzlehttp/guzzle": "^7.8", "league/flysystem": "^3.23", - "mpociot/vat-calculator": "^3.6", - "ramsey/uuid": "^4.7" + "mpociot/vat-calculator": "^3.7", + "ramsey/uuid": "^4.7", + "selective/xmldsig": "^3.1" }, "autoload": { "psr-4": { "App\\": "app/" } } -} \ No newline at end of file +} diff --git a/cp/config/app.php b/cp/config/app.php index 20230e1..b252e9e 100644 --- a/cp/config/app.php +++ b/cp/config/app.php @@ -43,7 +43,6 @@ return [ 'database' => $_ENV['DB_DATABASE'] ?? 'db_username', 'username' => $_ENV['DB_USERNAME'] ?? 'db_password', 'password' => $_ENV['DB_PASSWORD'] ?? '', - 'charset' => 'utf8', 'prefix' => '', 'schema' => 'public', 'sslmode' => 'prefer', diff --git a/cp/env-sample b/cp/env-sample index bec2acd..2b10326 100644 --- a/cp/env-sample +++ b/cp/env-sample @@ -25,5 +25,6 @@ MAIL_API_KEY='test-api-key' MAIL_API_PROVIDER='sendgrid' STRIPE_SECRET_KEY='stripe-secret-key' +STRIPE_PUBLISHABLE_KEY='stripe-publishable-key' TEST_TLDS=.test,.com.test \ No newline at end of file diff --git a/cp/lang/en_US/messages.po b/cp/lang/en_US/messages.po index 7fa1411..044b58a 100644 --- a/cp/lang/en_US/messages.po +++ b/cp/lang/en_US/messages.po @@ -1,179 +1,1346 @@ -msgid "Dashboard" -msgstr "Dashboard" +msgid "Approve Transfer" +msgstr "Approve Transfer" -msgid "Enable dark mode" -msgstr "Enable dark mode" +msgid "Cancel Transfer" +msgstr "Cancel Transfer" -msgid "Enable light mode" -msgstr "Enable light mode" +msgid "Reject Transfer" +msgstr "Reject Transfer" + +msgid "Completed" +msgstr "Completed" + +msgid "Name" +msgstr "Name" + +msgid "Initiated Date" +msgstr "Initiated Date" + +msgid "Expiry Date" +msgstr "Expiry Date" + +msgid "Status" +msgstr "Status" + +msgid "Actions" +msgstr "Actions" + +msgid "Are you sure you want to approve this transfer?" +msgstr "Are you sure you want to approve this transfer?" + +msgid "Confirm" +msgstr "Confirm" + +msgid "Are you sure you want to cancel this transfer?" +msgstr "Are you sure you want to cancel this transfer?" + +msgid "Are you sure you want to reject this transfer?" +msgstr "Are you sure you want to reject this transfer?" + +msgid "No Data" +msgstr "No Data" + +msgid "Registrar" +msgstr "Registrar" + +msgid "Date" +msgstr "Date" + +msgid "Log" +msgstr "Log" + +msgid "Command" +msgstr "Command" + +msgid "Domain" +msgstr "Domain" + +msgid "Length" +msgstr "Length" + +msgid "From" +msgstr "From" + +msgid "To" +msgstr "To" + +msgid "Amount" +msgstr "Amount" + +msgid "Host Name" +msgstr "Host Name" + +msgid "Creation Date" +msgstr "Creation Date" + +msgid "Are you sure you want to delete this host?" +msgstr "Are you sure you want to delete this host?" + +msgid "Message" +msgstr "Message" + +msgid "Message Type" +msgstr "Message Type" + +msgid "Object" +msgstr "Object" + +msgid "Subject" +msgstr "Subject" + +msgid "Category" +msgstr "Category" + +msgid "Priority" +msgstr "Priority" + +msgid "Email" +msgstr "Email" + +msgid "Balance" +msgstr "Balance" + +msgid "Description" +msgstr "Description" + +msgid "Identifier" +msgstr "Identifier" + +msgid "Phone" +msgstr "Phone" + +msgid "Are you sure you want to delete this contact?" +msgstr "Are you sure you want to delete this contact?" + +msgid "Object Type" +msgstr "Object Type" + +msgid "Result" +msgstr "Result" + +msgid "clTRID" +msgstr "clTRID" + +msgid "Milliseconds" +msgstr "Milliseconds" + +msgid "Registrant" +msgstr "Registrant" + +msgid "Expiration Date" +msgstr "Expiration Date" + +msgid "Are you sure you want to delete this domain?" +msgstr "Are you sure you want to delete this domain?" + +msgid "Are you sure you want to restore this domain?" +msgstr "Are you sure you want to restore this domain?" + +msgid "Are you sure you want to submit restore report for this domain?" +msgstr "Are you sure you want to submit restore report for this domain?" + +msgid "Number" +msgstr "Number" + +msgid "Total Domains" +msgstr "Total Domains" + +msgid "Created Domains" +msgstr "Created Domains" + +msgid "Renewed Domains" +msgstr "Renewed Domains" + +msgid "Transferred Domains" +msgstr "Transferred Domains" + +msgid "Deleted Domains" +msgstr "Deleted Domains" + +msgid "Restored Domains" +msgstr "Restored Domains" + +msgid "Script" +msgstr "Script" + +msgid "Roles" +msgstr "Roles" + +msgid "Verified" +msgstr "Verified" + +msgid "Applicant" +msgstr "Applicant" + +msgid "Phase" +msgstr "Phase" + +msgid "Are you sure you want to delete this application?" +msgstr "Are you sure you want to delete this application?" msgid "My Profile" msgstr "My Profile" -msgid "Logout" -msgstr "Logout" +msgid "Overview" +msgstr "Overview" + +msgid "Details" +msgstr "Details" + +msgid "User Name" +msgstr "User Name" + +msgid "Role" +msgstr "Role" + +msgid "Change Password" +msgstr "Change Password" + +msgid "Old Password" +msgstr "Old Password" + +msgid "New Password" +msgstr "New Password" + +msgid "Back" +msgstr "Back" + +msgid "Update" +msgstr "Update" + +msgid "Two-Factor Authentication" +msgstr "Two-Factor Authentication" + +msgid "Set up 2FA for additional security. Scan the QR code with your authentication app and enter the provided code below to verify." +msgstr "Set up 2FA for additional security. Scan the QR code with your authentication app and enter the provided code below to verify." + +msgid "Manual Entry Secret" +msgstr "Manual Entry Secret" + +msgid "If you're unable to scan the QR code, enter this secret manually into your authentication app. The secret is case-sensitive and should be entered exactly as shown." +msgstr "If you're unable to scan the QR code, enter this secret manually into your authentication app. The secret is case-sensitive and should be entered exactly as shown." + +msgid "Verification Code" +msgstr "Verification Code" + +msgid "Enter code" +msgstr "Enter code" + +msgid "Enter the code generated by your authentication app. This code verifies that your 2FA setup is working correctly. Once entered, click" +msgstr "Enter the code generated by your authentication app. This code verifies that your 2FA setup is working correctly. Once entered, click" + +msgid "Save 2FA Settings" +msgstr "Save 2FA Settings" + +msgid "to activate two-factor authentication for your account." +msgstr "to activate two-factor authentication for your account." + +msgid "Your account is secured with an additional layer of protection." +msgstr "Your account is secured with an additional layer of protection." + +msgid "2FA is currently" +msgstr "2FA is currently" + +msgid "enabled" +msgstr "enabled" + +msgid "for your account. If you encounter any issues or need to disable 2FA, please contact our support team for assistance." +msgstr "for your account. If you encounter any issues or need to disable 2FA, please contact our support team for assistance." + +msgid "WebAuthn Authentication" +msgstr "WebAuthn Authentication" + +msgid "Secure your account with WebAuthn. Click the button below to register your device for passwordless sign-in." +msgstr "Secure your account with WebAuthn. Click the button below to register your device for passwordless sign-in." + +msgid "Connect WebAuthn Device" +msgstr "Connect WebAuthn Device" + +msgid "Device/Browser Info" +msgstr "Device/Browser Info" + +msgid "Registration Date" +msgstr "Registration Date" + +msgid "Action" +msgstr "Action" + +msgid "Edit" +msgstr "Edit" + +msgid "No devices found." +msgstr "No devices found." + +msgid "User Audit Log" +msgstr "User Audit Log" + +msgid "Track and review all user activities in your account below. Monitor logins, profile changes, and other key actions to ensure security and transparency." +msgstr "Track and review all user activities in your account below. Monitor logins, profile changes, and other key actions to ensure security and transparency." + +msgid "Event" +msgstr "Event" + +msgid "User Agent" +msgstr "User Agent" + +msgid "Location" +msgstr "Location" + +msgid "Timestamp" +msgstr "Timestamp" + +msgid "No log data for user." +msgstr "No log data for user." + +msgid "Documentation" +msgstr "Documentation" + +msgid "Support Tickets" +msgstr "Support Tickets" + +msgid "Media Kit" +msgstr "Media Kit" + +msgid "Create New Ticket" +msgstr "Create New Ticket" + +msgid "Search" +msgstr "Search" + +msgid "Search tickets" +msgstr "Search tickets" + +msgid "Ticket Overview" +msgstr "Ticket Overview" + +msgid "Ticket" +msgstr "Ticket" + +msgid "Unknown Status" +msgstr "Unknown Status" + +msgid "Ticket Details" +msgstr "Ticket Details" + +msgid "Created On" +msgstr "Created On" + +msgid "Conversation" +msgstr "Conversation" + +msgid "Your Response" +msgstr "Your Response" + +msgid "Submit Response" +msgstr "Submit Response" + +msgid "New Support Ticket" +msgstr "New Support Ticket" + +msgid "Support ticket can not be created" +msgstr "Support ticket can not be created" + +msgid "Select a category" +msgstr "Select a category" + +msgid "Create Ticket" +msgstr "Create Ticket" + +msgid "Create Domain" +msgstr "Create Domain" + +msgid "Your Domain Name" +msgstr "Your Domain Name" + +msgid "Select Registrar" +msgstr "Select Registrar" + +msgid "Registration Years" +msgstr "Registration Years" + +msgid "Estimated Price" +msgstr "Estimated Price" + +msgid "Contacts" +msgstr "Contacts" + +msgid "Registrant Contact" +msgstr "Registrant Contact" + +msgid "Admin Contact" +msgstr "Admin Contact" + +msgid "Tech Contact" +msgstr "Tech Contact" + +msgid "Billing Contact" +msgstr "Billing Contact" + +msgid "Nameservers" +msgstr "Nameservers" + +msgid "Nameserver" +msgstr "Nameserver" + +msgid "Add DNSSEC Data" +msgstr "Add DNSSEC Data" + +msgid "DS Record" +msgstr "DS Record" + +msgid "Key Tag" +msgstr "Key Tag" + +msgid "Select Algorithm" +msgstr "Select Algorithm" + +msgid "Select Digest Type" +msgstr "Select Digest Type" + +msgid "Digest" +msgstr "Digest" + +msgid "DNSKEY Record" +msgstr "DNSKEY Record" + +msgid "Flags" +msgstr "Flags" + +msgid "Protocol" +msgstr "Protocol" + +msgid "Public Key" +msgstr "Public Key" + +msgid "Auth Info" +msgstr "Auth Info" + +msgid "Statuses" +msgstr "Statuses" + +msgid "Server Statuses" +msgstr "Server Statuses" + +msgid "Phase Type" +msgstr "Phase Type" + +msgid "Signed Mark Information" +msgstr "Signed Mark Information" + +msgid "Paste SMD contents" +msgstr "Paste SMD contents" + +msgid "Notice ID" +msgstr "Notice ID" + +msgid "Not After Date" +msgstr "Not After Date" + +msgid "Accepted Date" +msgstr "Accepted Date" + +msgid "Allocation Token" +msgstr "Allocation Token" + +msgid "Allocation token" +msgstr "Allocation token" + +msgid "Domain Details" +msgstr "Domain Details" + +msgid "Registered On" +msgstr "Registered On" + +msgid "Last Updated" +msgstr "Last Updated" + +msgid "contact" +msgstr "contact" + +msgid "Auth Type" +msgstr "Auth Type" + +msgid "DNSSEC Data" +msgstr "DNSSEC Data" msgid "Domains" msgstr "Domains" -msgid "List Domains" -msgstr "List Domains" - msgid "Check Domain" msgstr "Check Domain" +msgid "Search domains" +msgstr "Search domains" + +msgid "Application Details" +msgstr "Application Details" + +msgid "Application for" +msgstr "Application for" + +msgid "Application ID" +msgstr "Application ID" + +msgid "Launch Phase" +msgstr "Launch Phase" + +msgid "Phase Name" +msgstr "Phase Name" + +msgid "Updating Application" +msgstr "Updating Application" + +msgid "Update Application" +msgstr "Update Application" + +msgid "Create Application" +msgstr "Create Application" + +msgid "Renew Domain" +msgstr "Renew Domain" + +msgid "Domain Name" +msgstr "Domain Name" + +msgid "Your domain is currently renewed to its maximum term. At this time, no additional renewal is possible." +msgstr "Your domain is currently renewed to its maximum term. At this time, no additional renewal is possible." + +msgid "Applications" +msgstr "Applications" + +msgid "Search applications" +msgstr "Search applications" + +msgid "Updating Domain" +msgstr "Updating Domain" + +msgid "Domain Security" +msgstr "Domain Security" + +msgid "Update Domain" +msgstr "Update Domain" + +msgid "Request Domain Transfer" +msgstr "Request Domain Transfer" + +msgid "Gaining Registrar" +msgstr "Gaining Registrar" + +msgid "Transfer And Renew" +msgstr "Transfer And Renew" + +msgid "Request Transfer" +msgstr "Request Transfer" + +msgid "Transfers" +msgstr "Transfers" + +msgid "Search transfers" +msgstr "Search transfers" + msgid "Enter the domain name you want to check:" msgstr "Enter the domain name you want to check:" msgid "Check Availability" msgstr "Check Availability" -msgid "is available" -msgstr "is available" - -msgid "is not available" -msgstr "is not available" - -msgid "Create Domain" -msgstr "Create Domain" - -msgid "List Applications" -msgstr "List Applications" - -msgid "Create Application" -msgstr "Create Application" - -msgid "Transfers" -msgstr "Transfers" - -msgid "Contacts" -msgstr "Contacts" - -msgid "List Contacts" -msgstr "List Contacts" - -msgid "Check Contact" -msgstr "Check Contact" +msgid "Check claims" +msgstr "Check claims" msgid "Create Contact" msgstr "Create Contact" -msgid "Abuse Prevention" -msgstr "Abuse Prevention" +msgid "General & Internationalized Info" +msgstr "General & Internationalized Info" -msgid "Communication" -msgstr "Communication" +msgid "Disclose in WHOIS" +msgstr "Disclose in WHOIS" + +msgid "Organization" +msgstr "Organization" + +msgid "Street" +msgstr "Street" + +msgid "City" +msgstr "City" + +msgid "State/Province" +msgstr "State/Province" + +msgid "Postal Code" +msgstr "Postal Code" + +msgid "Country" +msgstr "Country" + +msgid "Disclose Address in WHOIS" +msgstr "Disclose Address in WHOIS" + +msgid "Contact Details" +msgstr "Contact Details" + +msgid "Contact ID" +msgstr "Contact ID" + +msgid "Voice" +msgstr "Voice" + +msgid "Fax" +msgstr "Fax" + +msgid "Contact AuthInfo" +msgstr "Contact AuthInfo" + +msgid "Auto-generated authentication information for the contact" +msgstr "Auto-generated authentication information for the contact" + +msgid "NIN - National Identification Number" +msgstr "NIN - National Identification Number" + +msgid "This is a Business Contact" +msgstr "This is a Business Contact" + +msgid "Verify by Phone" +msgstr "Verify by Phone" + +msgid "Verify by Email" +msgstr "Verify by Email" + +msgid "Verify by Postal Mail" +msgstr "Verify by Postal Mail" + +msgid "Include Localized Info" +msgstr "Include Localized Info" + +msgid "Localized Postal Info: Personal Details" +msgstr "Localized Postal Info: Personal Details" + +msgid "Localized Postal Info: Address Details" +msgstr "Localized Postal Info: Address Details" + +msgid "Search contacts" +msgstr "Search contacts" + +msgid "Update Contact" +msgstr "Update Contact" + +msgid "Authentication information for the contact" +msgstr "Authentication information for the contact" + +msgid "Contact" +msgstr "Contact" + +msgid "linked" +msgstr "linked" + +msgid "Visible in Public" +msgstr "Visible in Public" + +msgid "Hidden from Public" +msgstr "Hidden from Public" + +msgid "International" +msgstr "International" + +msgid "Localized" +msgstr "Localized" + +msgid "International Contact Details" +msgstr "International Contact Details" + +msgid "Localized Contact Details" +msgstr "Localized Contact Details" + +msgid "No Localized Contact Information Available" +msgstr "No Localized Contact Information Available" + +msgid "System Log" +msgstr "System Log" + +msgid "Search logs" +msgstr "Search logs" + +msgid "Message Queue" +msgstr "Message Queue" + +msgid "Search queue" +msgstr "Search queue" + +msgid "EPP History" +msgstr "EPP History" + +msgid "List Users" +msgstr "List Users" + +msgid "Search users" +msgstr "Search users" + +msgid "Host Details" +msgstr "Host Details" msgid "Hosts" msgstr "Hosts" -msgid "List Hosts" -msgstr "List Hosts" - -msgid "Check Host" -msgstr "Check Host" - msgid "Create Host" msgstr "Create Host" -msgid "Host Name" -msgstr "Host Name" +msgid "Search hosts" +msgstr "Search hosts" -msgid "Select Registrar" -msgstr "Select Registrar" +msgid "Update Host" +msgstr "Update Host" + +msgid "The host you're trying to update" +msgstr "The host you're trying to update" + +msgid "is external to the registry. Consequently, it does not store any IP addresses within our system, and therefore, does not require any updates from your end. This means there are no associated IP addresses under our management that need your attention." +msgstr "is external to the registry. Consequently, it does not store any IP addresses within our system, and therefore, does not require any updates from your end. This means there are no associated IP addresses under our management that need your attention." msgid "IPv4 Address" msgstr "IPv4 Address" -msgid "IPv6 Address" -msgstr "IPv6 Address" - -msgid "Optional" -msgstr "Optional" - msgid "Please enter a valid IPv4 address." msgstr "Please enter a valid IPv4 address." +msgid "IPv6 Address" +msgstr "IPv6 Address" + msgid "Please enter a valid IPv6 address." msgstr "Please enter a valid IPv6 address." -msgid "Create" -msgstr "Create" +msgid "Optional" +msgstr "Optional" -msgid "Host" -msgstr "Host" +msgid "Deposit Payment Unsuccessful" +msgstr "Deposit Payment Unsuccessful" -msgid "has been created successfully on" -msgstr "has been created successfully on" +msgid "Ready to try again? When you're set to proceed with your deposit, simply return to the" +msgstr "Ready to try again? When you're set to proceed with your deposit, simply return to the" -msgid "is not available" -msgstr "is not available" +msgid "Deposit Page" +msgstr "Deposit Page" -msgid "Logs" -msgstr "Logs" +msgid "to initiate a new payment. We value your partnership and are committed to assisting you every step of the way." +msgstr "to initiate a new payment. We value your partnership and are committed to assisting you every step of the way." -msgid "Registrars" -msgstr "Registrars" +msgid "Registrar Deposit" +msgstr "Registrar Deposit" -msgid "List Registrars" -msgstr "List Registrars" +msgid "Choose Registrar..." +msgstr "Choose Registrar..." -msgid "Create Registrar" -msgstr "Create Registrar" +msgid "Current Balance for" +msgstr "Current Balance for" -msgid "Compliance" -msgstr "Compliance" +msgid "Enter deposit amount" +msgstr "Enter deposit amount" -msgid "Financials" -msgstr "Financials" - -msgid "Pricing" -msgstr "Pricing" +msgid "Optional deposit description" +msgstr "Optional deposit description" msgid "Add Deposit" msgstr "Add Deposit" +msgid "Account Overview" +msgstr "Account Overview" + +msgid "Search account" +msgstr "Search account" + +msgid "View Invoice" +msgstr "View Invoice" + +msgid "Invoice" +msgstr "Invoice" + +msgid "Print Invoice" +msgstr "Print Invoice" + +msgid "Provider" +msgstr "Provider" + +msgid "Registry" +msgstr "Registry" + +msgid "Client" +msgstr "Client" + +msgid "Invoice Issued On" +msgstr "Invoice Issued On" + +msgid "Due Date" +msgstr "Due Date" + +msgid "Product" +msgstr "Product" + +msgid "No items found." +msgstr "No items found." + +msgid "Subtotal" +msgstr "Subtotal" + +msgid "Vat Rate" +msgstr "Vat Rate" + +msgid "Vat Due" +msgstr "Vat Due" + +msgid "Total Due" +msgstr "Total Due" + +msgid "Notes" +msgstr "Notes" + +msgid "Thank you very much for doing business with us. We look forward to working with you again!" +msgstr "Thank you very much for doing business with us. We look forward to working with you again!" + msgid "Transactions" msgstr "Transactions" -msgid "Account Overview" -msgstr "Account Overview" +msgid "Search transactions" +msgstr "Search transactions" msgid "Invoices" msgstr "Invoices" +msgid "Search invoices" +msgstr "Search invoices" + +msgid "Deposit with Stripe" +msgstr "Deposit with Stripe" + +msgid "Registrar Details" +msgstr "Registrar Details" + +msgid "Contact Email" +msgstr "Contact Email" + +msgid "Website" +msgstr "Website" + +msgid "Abuse Phone" +msgstr "Abuse Phone" + +msgid "Abuse Email" +msgstr "Abuse Email" + +msgid "WHOIS Server" +msgstr "WHOIS Server" + +msgid "RDAP Server" +msgstr "RDAP Server" + +msgid "Currency" +msgstr "Currency" + +msgid "Account Balance" +msgstr "Account Balance" + +msgid "Credit Limit" +msgstr "Credit Limit" + +msgid "Credit Threshold" +msgstr "Credit Threshold" + +msgid "Threshold Type" +msgstr "Threshold Type" + +msgid "IP Whitelisting" +msgstr "IP Whitelisting" + +msgid "No data available." +msgstr "No data available." + +msgid "Registrar User" +msgstr "Registrar User" + +msgid "EPP Username/CLID" +msgstr "EPP Username/CLID" + +msgid "Panel Login Email" +msgstr "Panel Login Email" + +msgid "Operational Test and Evaluation (OTE)" +msgstr "Operational Test and Evaluation (OTE)" + +msgid "Successfully passing the Operational Test and Evaluation (OTE) is a mandatory requirement for registrars. The OTE process evaluates the interaction and compliance of the registrar's system with registry operations through a series of EPP command tests in a controlled environment. Below you can find the results of these essential EPP command tests for your account:" +msgstr "Successfully passing the Operational Test and Evaluation (OTE) is a mandatory requirement for registrars. The OTE process evaluates the interaction and compliance of the registrar's system with registry operations through a series of EPP command tests in a controlled environment. Below you can find the results of these essential EPP command tests for your account:" + +msgid "Pending" +msgstr "Pending" + +msgid "Failed" +msgstr "Failed" + +msgid "Update Registrar" +msgstr "Update Registrar" + +msgid "The official name of the registrar." +msgstr "The official name of the registrar." + +msgid "Unique identifier assigned by IANA." +msgstr "Unique identifier assigned by IANA." + +msgid "Primary contact email of the registrar." +msgstr "Primary contact email of the registrar." + +msgid "URL" +msgstr "URL" + +msgid "Registrar's official website URL." +msgstr "Registrar's official website URL." + +msgid "Address of the registrar's WHOIS server." +msgstr "Address of the registrar's WHOIS server." + +msgid "Address of the registrar's RDAP server." +msgstr "Address of the registrar's RDAP server." + +msgid "Email address for reporting abuse." +msgstr "Email address for reporting abuse." + +msgid "Phone number for reporting abuse." +msgstr "Phone number for reporting abuse." + +msgid "Financial Information" +msgstr "Financial Information" + +msgid "Current balance in the registrar's account." +msgstr "Current balance in the registrar's account." + +msgid "Maximum credit limit for the registrar." +msgstr "Maximum credit limit for the registrar." + +msgid "Credit threshold triggering alerts or actions." +msgstr "Credit threshold triggering alerts or actions." + +msgid "Type of threshold: fixed value or percentage." +msgstr "Type of threshold: fixed value or percentage." + +msgid "Registrar Contacts" +msgstr "Registrar Contacts" + +msgid "Owner" +msgstr "Owner" + +msgid "Billing" +msgstr "Billing" + +msgid "Abuse" +msgstr "Abuse" + +msgid "Copy data to other contacts" +msgstr "Copy data to other contacts" + +msgid "First Name" +msgstr "First Name" + +msgid "Last Name" +msgstr "Last Name" + +msgid "Street Address" +msgstr "Street Address" + +msgid "Whitelist IP addresses for secure access. Up to 5 IP addresses (IPv4 or IPv6) can be added." +msgstr "Whitelist IP addresses for secure access. Up to 5 IP addresses (IPv4 or IPv6) can be added." + +msgid "For an existing registrar user, you can view the current EPP username (also known as CLID) and panel access email. Additionally, you have the option to reset the passwords for EPP and panel access as needed." +msgstr "For an existing registrar user, you can view the current EPP username (also known as CLID) and panel access email. Additionally, you have the option to reset the passwords for EPP and panel access as needed." + +msgid "Username/CLID" +msgstr "Username/CLID" + +msgid "Login Email" +msgstr "Login Email" + +msgid "Panel Password" +msgstr "Panel Password" + +msgid "EPP Password" +msgstr "EPP Password" + +msgid "Update Panel Password" +msgstr "Update Panel Password" + +msgid "Update EPP Password" +msgstr "Update EPP Password" + +msgid "Operational Test and Evaluation (OTE) assesses the functionality of EPP commands in a simulated environment, ensuring effective communication between the registrar's system and the registry. Below are key EPP commands and their test statuses:" +msgstr "Operational Test and Evaluation (OTE) assesses the functionality of EPP commands in a simulated environment, ensuring effective communication between the registrar's system and the registry. Below are key EPP commands and their test statuses:" + +msgid "Create Registrar" +msgstr "Create Registrar" + +msgid "Fixed" +msgstr "Fixed" + +msgid "Percent" +msgstr "Percent" + +msgid "Create a registrar user by specifying the username (also known as CLID), email, and passwords for EPP and panel access." +msgstr "Create a registrar user by specifying the username (also known as CLID), email, and passwords for EPP and panel access." + +msgid "Registrars" +msgstr "Registrars" + +msgid "Search registrars" +msgstr "Search registrars" + +msgid "Managed Reserved Names" +msgstr "Managed Reserved Names" + +msgid "Manage Reserved Names" +msgstr "Manage Reserved Names" + +msgid "Update Reserved Names" +msgstr "Update Reserved Names" + +msgid "Manage TLD" +msgstr "Manage TLD" + +msgid "General Details" +msgstr "General Details" + +msgid "TLD Extension" +msgstr "TLD Extension" + +msgid "TLD Type" +msgstr "TLD Type" + +msgid "Supported Script" +msgstr "Supported Script" + +msgid "Pricing" +msgstr "Pricing" + +msgid "Setup Fee" +msgstr "Setup Fee" + +msgid "Year" +msgstr "Year" + +msgid "Years" +msgstr "Years" + +msgid "Create" +msgstr "Create" + +msgid "Renew" +msgstr "Renew" + +msgid "Transfer" +msgstr "Transfer" + +msgid "When you type a price for 1 Year above, it will automatically get multiplied for subsequent years." +msgstr "When you type a price for 1 Year above, it will automatically get multiplied for subsequent years." + +msgid "Restore Price" +msgstr "Restore Price" + +msgid "Enter the price for restoring the TLD." +msgstr "Enter the price for restoring the TLD." + +msgid "Premium Names" +msgstr "Premium Names" + +msgid "Upload CSV File" +msgstr "Upload CSV File" + +msgid "Please upload a CSV file containing premium names. Each row should include a name and its corresponding pricing category, separated by a comma. Note: If one or more names in the file already exist in our system, they will be overwritten with the new information provided in the upload." +msgstr "Please upload a CSV file containing premium names. Each row should include a name and its corresponding pricing category, separated by a comma. Note: If one or more names in the file already exist in our system, they will be overwritten with the new information provided in the upload." + +msgid "Set Premium Name Price Categories" +msgstr "Set Premium Name Price Categories" + +msgid "Category Name" +msgstr "Category Name" + +msgid "Price" +msgstr "Price" + +msgid "Update existing premium categories as needed. Please note: currently, deletion of categories is not available. All updates will modify existing information without removing any categories." +msgstr "Update existing premium categories as needed. Please note: currently, deletion of categories is not available. All updates will modify existing information without removing any categories." + +msgid "Update TLD" +msgstr "Update TLD" + +msgid "Manage Promotions" +msgstr "Manage Promotions" + +msgid "Promotion Name" +msgstr "Promotion Name" + +msgid "Start Date" +msgstr "Start Date" + +msgid "End Date" +msgstr "End Date" + +msgid "Discount" +msgstr "Discount" + +msgid "Discount Amount" +msgstr "Discount Amount" + +msgid "No promotions found." +msgstr "No promotions found." + +msgid "Create New Promotion" +msgstr "Create New Promotion" + +msgid "Promotion Start Date" +msgstr "Promotion Start Date" + +msgid "Please Note:" +msgstr "Please Note:" + +msgid "All times displayed are in" +msgstr "All times displayed are in" + +msgid "Promotion End Date" +msgstr "Promotion End Date" + +msgid "Discount Type" +msgstr "Discount Type" + +msgid "Percentage" +msgstr "Percentage" + +msgid "Fixed Amount" +msgstr "Fixed Amount" + +msgid "Free Domains" +msgstr "Free Domains" + +msgid "Discount Value" +msgstr "Discount Value" + +msgid "Enter discount value" +msgstr "Enter discount value" + +msgid "Maximum Discounted Items" +msgstr "Maximum Discounted Items" + +msgid "Conditions" +msgstr "Conditions" + +msgid "Enter conditions" +msgstr "Enter conditions" + +msgid "Enter description" +msgstr "Enter description" + +msgid "Update Promotions" +msgstr "Update Promotions" + +msgid "Manage Launch Phases" +msgstr "Manage Launch Phases" + +msgid "Phase Category" +msgstr "Phase Category" + +msgid "Phase Description" +msgstr "Phase Description" + +msgid "No launch phases found." +msgstr "No launch phases found." + +msgid "Create New Phase" +msgstr "Create New Phase" + +msgid "Single application only." +msgstr "Single application only." + +msgid "Multiple applications allowed." +msgstr "Multiple applications allowed." + +msgid "Enter phase description" +msgstr "Enter phase description" + +msgid "Phase Start Date" +msgstr "Phase Start Date" + +msgid "Phase End Date" +msgstr "Phase End Date" + +msgid "Update Phases" +msgstr "Update Phases" + +msgid "Registry Configuration" +msgstr "Registry Configuration" + +msgid "System Settings" +msgstr "System Settings" + +msgid "Registry Operator Name" +msgstr "Registry Operator Name" + +msgid "Enter registry operator's name" +msgstr "Enter registry operator's name" + +msgid "The official name of the organization operating the registry." +msgstr "The official name of the organization operating the registry." + +msgid "Registry Handle" +msgstr "Registry Handle" + +msgid "Enter registry handle" +msgstr "Enter registry handle" + +msgid "A unique identifier for the registry which will be appended to each object handle." +msgstr "A unique identifier for the registry which will be appended to each object handle." + +msgid "Enter registry whois server" +msgstr "Enter registry whois server" + +msgid "Enter the URL of the registry's WHOIS server. Example:" +msgstr "Enter the URL of the registry's WHOIS server. Example:" + +msgid "Enter registry RDAP server" +msgstr "Enter registry RDAP server" + +msgid "Enter the URL of the registry's RDAP server. Example:" +msgstr "Enter the URL of the registry's RDAP server. Example:" + +msgid "Features" +msgstr "Features" + +msgid "Require Launch Phases" +msgstr "Require Launch Phases" + +msgid "Operator Details" +msgstr "Operator Details" + +msgid "Registry VAT/Company Number" +msgstr "Registry VAT/Company Number" + +msgid "Enter registry operator's VAT number" +msgstr "Enter registry operator's VAT number" + +msgid "Enter the VAT number of the organization operating the registry, or company number if VAT is unavailable." +msgstr "Enter the VAT number of the organization operating the registry, or company number if VAT is unavailable." + +msgid "Contact Address" +msgstr "Contact Address" + +msgid "Enter contact address" +msgstr "Enter contact address" + +msgid "The contact address of the registry." +msgstr "The contact address of the registry." + +msgid "Enter contact email" +msgstr "Enter contact email" + +msgid "The email address for general inquiries to the registry." +msgstr "The email address for general inquiries to the registry." + +msgid "Contact Phone" +msgstr "Contact Phone" + +msgid "Enter contact phone" +msgstr "Enter contact phone" + +msgid "The phone number for general inquiries to the registry." +msgstr "The phone number for general inquiries to the registry." + +msgid "Update Details" +msgstr "Update Details" + +msgid "TLD Management" +msgstr "TLD Management" + +msgid "Create New TLD" +msgstr "Create New TLD" + +msgid "Search TLDs" +msgstr "Search TLDs" + +msgid "Enter the desired top-level domain (TLD) name, like" +msgstr "Enter the desired top-level domain (TLD) name, like" + +msgid "or" +msgstr "or" + +msgid "DNSSEC is currently activated manually. This option is for display purposes only." +msgstr "DNSSEC is currently activated manually. This option is for display purposes only." + +msgid "Select the type of TLD: ccTLD (Country Code Top-Level Domain) or gTLD (Generic Top-Level Domain)." +msgstr "Select the type of TLD: ccTLD (Country Code Top-Level Domain) or gTLD (Generic Top-Level Domain)." + +msgid "Cyrillic" +msgstr "Cyrillic" + +msgid "Japanese" +msgstr "Japanese" + +msgid "Korean" +msgstr "Korean" + +msgid "Choose the script type that the TLD will support. Options include ASCII, Cyrillic, Japanese, and Korean." +msgstr "Choose the script type that the TLD will support. Options include ASCII, Cyrillic, Japanese, and Korean." + +msgid "Upload a CSV file with premium names. Each row should contain a name and its pricing category, separated by a comma." +msgstr "Upload a CSV file with premium names. Each row should contain a name and its pricing category, separated by a comma." + +msgid "Create TLD" +msgstr "Create TLD" + msgid "Reports" msgstr "Reports" -msgid "System" -msgstr "System" +msgid "Search reports" +msgstr "Search reports" + +msgid "Dashboard" +msgstr "Dashboard" + +msgid "View Reports" +msgstr "View Reports" + +msgid "Create new domain" +msgstr "Create new domain" + +msgid "Enable dark mode" +msgstr "Enable dark mode" + +msgid "Enable light mode" +msgstr "Enable light mode" + +msgid "Logout" +msgstr "Logout" + +msgid "List Domains" +msgstr "List Domains" + +msgid "List Applications" +msgstr "List Applications" + +msgid "List Contacts" +msgstr "List Contacts" + +msgid "List Hosts" +msgstr "List Hosts" + +msgid "List Registrars" +msgstr "List Registrars" + +msgid "Financials" +msgstr "Financials" msgid "Configuration" msgstr "Configuration" -msgid "User Management" -msgstr "User Management" - -msgid "EPP Log" -msgstr "EPP Log" - -msgid "Audit Log" -msgstr "Audit Log" - -msgid "Backup" -msgstr "Backup" +msgid "TLDs" +msgstr "TLDs" msgid "Support" msgstr "Support" -msgid "Support Tickets" -msgstr "Support Tickets" +msgid "Registry Currency" +msgstr "Registry Currency" -msgid "Create Ticket" -msgstr "Create Ticket" - -msgid "Documentation" -msgstr "Documentation" - -msgid "Media Kit" -msgstr "Media Kit" \ No newline at end of file +msgid "Choose the currency for all transactions. This impacts billing and financial reports. Inform stakeholders of changes." +msgstr "Choose the currency for all transactions. This impacts billing and financial reports. Inform stakeholders of changes." diff --git a/cp/lang/messages_template.po b/cp/lang/messages_template.po new file mode 100644 index 0000000..8dd6a95 --- /dev/null +++ b/cp/lang/messages_template.po @@ -0,0 +1,1346 @@ +msgid "Approve Transfer" +msgstr "" + +msgid "Cancel Transfer" +msgstr "" + +msgid "Reject Transfer" +msgstr "" + +msgid "Completed" +msgstr "" + +msgid "Name" +msgstr "" + +msgid "Initiated Date" +msgstr "" + +msgid "Expiry Date" +msgstr "" + +msgid "Status" +msgstr "" + +msgid "Actions" +msgstr "" + +msgid "Are you sure you want to approve this transfer?" +msgstr "" + +msgid "Confirm" +msgstr "" + +msgid "Are you sure you want to cancel this transfer?" +msgstr "" + +msgid "Are you sure you want to reject this transfer?" +msgstr "" + +msgid "No Data" +msgstr "" + +msgid "Registrar" +msgstr "" + +msgid "Date" +msgstr "" + +msgid "Log" +msgstr "" + +msgid "Command" +msgstr "" + +msgid "Domain" +msgstr "" + +msgid "Length" +msgstr "" + +msgid "From" +msgstr "" + +msgid "To" +msgstr "" + +msgid "Amount" +msgstr "" + +msgid "Host Name" +msgstr "" + +msgid "Creation Date" +msgstr "" + +msgid "Are you sure you want to delete this host?" +msgstr "" + +msgid "Message" +msgstr "" + +msgid "Message Type" +msgstr "" + +msgid "Object" +msgstr "" + +msgid "Subject" +msgstr "" + +msgid "Category" +msgstr "" + +msgid "Priority" +msgstr "" + +msgid "Email" +msgstr "" + +msgid "Balance" +msgstr "" + +msgid "Description" +msgstr "" + +msgid "Identifier" +msgstr "" + +msgid "Phone" +msgstr "" + +msgid "Are you sure you want to delete this contact?" +msgstr "" + +msgid "Object Type" +msgstr "" + +msgid "Result" +msgstr "" + +msgid "clTRID" +msgstr "" + +msgid "Milliseconds" +msgstr "" + +msgid "Registrant" +msgstr "" + +msgid "Expiration Date" +msgstr "" + +msgid "Are you sure you want to delete this domain?" +msgstr "" + +msgid "Are you sure you want to restore this domain?" +msgstr "" + +msgid "Are you sure you want to submit restore report for this domain?" +msgstr "" + +msgid "Number" +msgstr "" + +msgid "Total Domains" +msgstr "" + +msgid "Created Domains" +msgstr "" + +msgid "Renewed Domains" +msgstr "" + +msgid "Transferred Domains" +msgstr "" + +msgid "Deleted Domains" +msgstr "" + +msgid "Restored Domains" +msgstr "" + +msgid "Script" +msgstr "" + +msgid "Roles" +msgstr "" + +msgid "Verified" +msgstr "" + +msgid "Applicant" +msgstr "" + +msgid "Phase" +msgstr "" + +msgid "Are you sure you want to delete this application?" +msgstr "" + +msgid "My Profile" +msgstr "" + +msgid "Overview" +msgstr "" + +msgid "Details" +msgstr "" + +msgid "User Name" +msgstr "" + +msgid "Role" +msgstr "" + +msgid "Change Password" +msgstr "" + +msgid "Old Password" +msgstr "" + +msgid "New Password" +msgstr "" + +msgid "Back" +msgstr "" + +msgid "Update" +msgstr "" + +msgid "Two-Factor Authentication" +msgstr "" + +msgid "Set up 2FA for additional security. Scan the QR code with your authentication app and enter the provided code below to verify." +msgstr "" + +msgid "Manual Entry Secret" +msgstr "" + +msgid "If you're unable to scan the QR code, enter this secret manually into your authentication app. The secret is case-sensitive and should be entered exactly as shown." +msgstr "" + +msgid "Verification Code" +msgstr "" + +msgid "Enter code" +msgstr "" + +msgid "Enter the code generated by your authentication app. This code verifies that your 2FA setup is working correctly. Once entered, click" +msgstr "" + +msgid "Save 2FA Settings" +msgstr "" + +msgid "to activate two-factor authentication for your account." +msgstr "" + +msgid "Your account is secured with an additional layer of protection." +msgstr "" + +msgid "2FA is currently" +msgstr "" + +msgid "enabled" +msgstr "" + +msgid "for your account. If you encounter any issues or need to disable 2FA, please contact our support team for assistance." +msgstr "" + +msgid "WebAuthn Authentication" +msgstr "" + +msgid "Secure your account with WebAuthn. Click the button below to register your device for passwordless sign-in." +msgstr "" + +msgid "Connect WebAuthn Device" +msgstr "" + +msgid "Device/Browser Info" +msgstr "" + +msgid "Registration Date" +msgstr "" + +msgid "Action" +msgstr "" + +msgid "Edit" +msgstr "" + +msgid "No devices found." +msgstr "" + +msgid "User Audit Log" +msgstr "" + +msgid "Track and review all user activities in your account below. Monitor logins, profile changes, and other key actions to ensure security and transparency." +msgstr "" + +msgid "Event" +msgstr "" + +msgid "User Agent" +msgstr "" + +msgid "Location" +msgstr "" + +msgid "Timestamp" +msgstr "" + +msgid "No log data for user." +msgstr "" + +msgid "Documentation" +msgstr "" + +msgid "Support Tickets" +msgstr "" + +msgid "Media Kit" +msgstr "" + +msgid "Create New Ticket" +msgstr "" + +msgid "Search" +msgstr "" + +msgid "Search tickets" +msgstr "" + +msgid "Ticket Overview" +msgstr "" + +msgid "Ticket" +msgstr "" + +msgid "Unknown Status" +msgstr "" + +msgid "Ticket Details" +msgstr "" + +msgid "Created On" +msgstr "" + +msgid "Conversation" +msgstr "" + +msgid "Your Response" +msgstr "" + +msgid "Submit Response" +msgstr "" + +msgid "New Support Ticket" +msgstr "" + +msgid "Support ticket can not be created" +msgstr "" + +msgid "Select a category" +msgstr "" + +msgid "Create Ticket" +msgstr "" + +msgid "Create Domain" +msgstr "" + +msgid "Your Domain Name" +msgstr "" + +msgid "Select Registrar" +msgstr "" + +msgid "Registration Years" +msgstr "" + +msgid "Estimated Price" +msgstr "" + +msgid "Contacts" +msgstr "" + +msgid "Registrant Contact" +msgstr "" + +msgid "Admin Contact" +msgstr "" + +msgid "Tech Contact" +msgstr "" + +msgid "Billing Contact" +msgstr "" + +msgid "Nameservers" +msgstr "" + +msgid "Nameserver" +msgstr "" + +msgid "Add DNSSEC Data" +msgstr "" + +msgid "DS Record" +msgstr "" + +msgid "Key Tag" +msgstr "" + +msgid "Select Algorithm" +msgstr "" + +msgid "Select Digest Type" +msgstr "" + +msgid "Digest" +msgstr "" + +msgid "DNSKEY Record" +msgstr "" + +msgid "Flags" +msgstr "" + +msgid "Protocol" +msgstr "" + +msgid "Public Key" +msgstr "" + +msgid "Auth Info" +msgstr "" + +msgid "Statuses" +msgstr "" + +msgid "Server Statuses" +msgstr "" + +msgid "Phase Type" +msgstr "" + +msgid "Signed Mark Information" +msgstr "" + +msgid "Paste SMD contents" +msgstr "" + +msgid "Notice ID" +msgstr "" + +msgid "Not After Date" +msgstr "" + +msgid "Accepted Date" +msgstr "" + +msgid "Allocation Token" +msgstr "" + +msgid "Allocation token" +msgstr "" + +msgid "Domain Details" +msgstr "" + +msgid "Registered On" +msgstr "" + +msgid "Last Updated" +msgstr "" + +msgid "contact" +msgstr "" + +msgid "Auth Type" +msgstr "" + +msgid "DNSSEC Data" +msgstr "" + +msgid "Domains" +msgstr "" + +msgid "Check Domain" +msgstr "" + +msgid "Search domains" +msgstr "" + +msgid "Application Details" +msgstr "" + +msgid "Application for" +msgstr "" + +msgid "Application ID" +msgstr "" + +msgid "Launch Phase" +msgstr "" + +msgid "Phase Name" +msgstr "" + +msgid "Updating Application" +msgstr "" + +msgid "Update Application" +msgstr "" + +msgid "Create Application" +msgstr "" + +msgid "Renew Domain" +msgstr "" + +msgid "Domain Name" +msgstr "" + +msgid "Your domain is currently renewed to its maximum term. At this time, no additional renewal is possible." +msgstr "" + +msgid "Applications" +msgstr "" + +msgid "Search applications" +msgstr "" + +msgid "Updating Domain" +msgstr "" + +msgid "Domain Security" +msgstr "" + +msgid "Update Domain" +msgstr "" + +msgid "Request Domain Transfer" +msgstr "" + +msgid "Gaining Registrar" +msgstr "" + +msgid "Transfer And Renew" +msgstr "" + +msgid "Request Transfer" +msgstr "" + +msgid "Transfers" +msgstr "" + +msgid "Search transfers" +msgstr "" + +msgid "Enter the domain name you want to check:" +msgstr "" + +msgid "Check Availability" +msgstr "" + +msgid "Check claims" +msgstr "" + +msgid "Create Contact" +msgstr "" + +msgid "General & Internationalized Info" +msgstr "" + +msgid "Disclose in WHOIS" +msgstr "" + +msgid "Organization" +msgstr "" + +msgid "Street" +msgstr "" + +msgid "City" +msgstr "" + +msgid "State/Province" +msgstr "" + +msgid "Postal Code" +msgstr "" + +msgid "Country" +msgstr "" + +msgid "Disclose Address in WHOIS" +msgstr "" + +msgid "Contact Details" +msgstr "" + +msgid "Contact ID" +msgstr "" + +msgid "Voice" +msgstr "" + +msgid "Fax" +msgstr "" + +msgid "Contact AuthInfo" +msgstr "" + +msgid "Auto-generated authentication information for the contact" +msgstr "" + +msgid "NIN - National Identification Number" +msgstr "" + +msgid "This is a Business Contact" +msgstr "" + +msgid "Verify by Phone" +msgstr "" + +msgid "Verify by Email" +msgstr "" + +msgid "Verify by Postal Mail" +msgstr "" + +msgid "Include Localized Info" +msgstr "" + +msgid "Localized Postal Info: Personal Details" +msgstr "" + +msgid "Localized Postal Info: Address Details" +msgstr "" + +msgid "Search contacts" +msgstr "" + +msgid "Update Contact" +msgstr "" + +msgid "Authentication information for the contact" +msgstr "" + +msgid "Contact" +msgstr "" + +msgid "linked" +msgstr "" + +msgid "Visible in Public" +msgstr "" + +msgid "Hidden from Public" +msgstr "" + +msgid "International" +msgstr "" + +msgid "Localized" +msgstr "" + +msgid "International Contact Details" +msgstr "" + +msgid "Localized Contact Details" +msgstr "" + +msgid "No Localized Contact Information Available" +msgstr "" + +msgid "System Log" +msgstr "" + +msgid "Search logs" +msgstr "" + +msgid "Message Queue" +msgstr "" + +msgid "Search queue" +msgstr "" + +msgid "EPP History" +msgstr "" + +msgid "List Users" +msgstr "" + +msgid "Search users" +msgstr "" + +msgid "Host Details" +msgstr "" + +msgid "Hosts" +msgstr "" + +msgid "Create Host" +msgstr "" + +msgid "Search hosts" +msgstr "" + +msgid "Update Host" +msgstr "" + +msgid "The host you're trying to update" +msgstr "" + +msgid "is external to the registry. Consequently, it does not store any IP addresses within our system, and therefore, does not require any updates from your end. This means there are no associated IP addresses under our management that need your attention." +msgstr "" + +msgid "IPv4 Address" +msgstr "" + +msgid "Please enter a valid IPv4 address." +msgstr "" + +msgid "IPv6 Address" +msgstr "" + +msgid "Please enter a valid IPv6 address." +msgstr "" + +msgid "Optional" +msgstr "" + +msgid "Deposit Payment Unsuccessful" +msgstr "" + +msgid "Ready to try again? When you're set to proceed with your deposit, simply return to the" +msgstr "" + +msgid "Deposit Page" +msgstr "" + +msgid "to initiate a new payment. We value your partnership and are committed to assisting you every step of the way." +msgstr "" + +msgid "Registrar Deposit" +msgstr "" + +msgid "Choose Registrar..." +msgstr "" + +msgid "Current Balance for" +msgstr "" + +msgid "Enter deposit amount" +msgstr "" + +msgid "Optional deposit description" +msgstr "" + +msgid "Add Deposit" +msgstr "" + +msgid "Account Overview" +msgstr "" + +msgid "Search account" +msgstr "" + +msgid "View Invoice" +msgstr "" + +msgid "Invoice" +msgstr "" + +msgid "Print Invoice" +msgstr "" + +msgid "Provider" +msgstr "" + +msgid "Registry" +msgstr "" + +msgid "Client" +msgstr "" + +msgid "Invoice Issued On" +msgstr "" + +msgid "Due Date" +msgstr "" + +msgid "Product" +msgstr "" + +msgid "No items found." +msgstr "" + +msgid "Subtotal" +msgstr "" + +msgid "Vat Rate" +msgstr "" + +msgid "Vat Due" +msgstr "" + +msgid "Total Due" +msgstr "" + +msgid "Notes" +msgstr "" + +msgid "Thank you very much for doing business with us. We look forward to working with you again!" +msgstr "" + +msgid "Transactions" +msgstr "" + +msgid "Search transactions" +msgstr "" + +msgid "Invoices" +msgstr "" + +msgid "Search invoices" +msgstr "" + +msgid "Deposit with Stripe" +msgstr "" + +msgid "Registrar Details" +msgstr "" + +msgid "Contact Email" +msgstr "" + +msgid "Website" +msgstr "" + +msgid "Abuse Phone" +msgstr "" + +msgid "Abuse Email" +msgstr "" + +msgid "WHOIS Server" +msgstr "" + +msgid "RDAP Server" +msgstr "" + +msgid "Currency" +msgstr "" + +msgid "Account Balance" +msgstr "" + +msgid "Credit Limit" +msgstr "" + +msgid "Credit Threshold" +msgstr "" + +msgid "Threshold Type" +msgstr "" + +msgid "IP Whitelisting" +msgstr "" + +msgid "No data available." +msgstr "" + +msgid "Registrar User" +msgstr "" + +msgid "EPP Username/CLID" +msgstr "" + +msgid "Panel Login Email" +msgstr "" + +msgid "Operational Test and Evaluation (OTE)" +msgstr "" + +msgid "Successfully passing the Operational Test and Evaluation (OTE) is a mandatory requirement for registrars. The OTE process evaluates the interaction and compliance of the registrar's system with registry operations through a series of EPP command tests in a controlled environment. Below you can find the results of these essential EPP command tests for your account:" +msgstr "" + +msgid "Pending" +msgstr "" + +msgid "Failed" +msgstr "" + +msgid "Update Registrar" +msgstr "" + +msgid "The official name of the registrar." +msgstr "" + +msgid "Unique identifier assigned by IANA." +msgstr "" + +msgid "Primary contact email of the registrar." +msgstr "" + +msgid "URL" +msgstr "" + +msgid "Registrar's official website URL." +msgstr "" + +msgid "Address of the registrar's WHOIS server." +msgstr "" + +msgid "Address of the registrar's RDAP server." +msgstr "" + +msgid "Email address for reporting abuse." +msgstr "" + +msgid "Phone number for reporting abuse." +msgstr "" + +msgid "Financial Information" +msgstr "" + +msgid "Current balance in the registrar's account." +msgstr "" + +msgid "Maximum credit limit for the registrar." +msgstr "" + +msgid "Credit threshold triggering alerts or actions." +msgstr "" + +msgid "Type of threshold: fixed value or percentage." +msgstr "" + +msgid "Registrar Contacts" +msgstr "" + +msgid "Owner" +msgstr "" + +msgid "Billing" +msgstr "" + +msgid "Abuse" +msgstr "" + +msgid "Copy data to other contacts" +msgstr "" + +msgid "First Name" +msgstr "" + +msgid "Last Name" +msgstr "" + +msgid "Street Address" +msgstr "" + +msgid "Whitelist IP addresses for secure access. Up to 5 IP addresses (IPv4 or IPv6) can be added." +msgstr "" + +msgid "For an existing registrar user, you can view the current EPP username (also known as CLID) and panel access email. Additionally, you have the option to reset the passwords for EPP and panel access as needed." +msgstr "" + +msgid "Username/CLID" +msgstr "" + +msgid "Login Email" +msgstr "" + +msgid "Panel Password" +msgstr "" + +msgid "EPP Password" +msgstr "" + +msgid "Update Panel Password" +msgstr "" + +msgid "Update EPP Password" +msgstr "" + +msgid "Operational Test and Evaluation (OTE) assesses the functionality of EPP commands in a simulated environment, ensuring effective communication between the registrar's system and the registry. Below are key EPP commands and their test statuses:" +msgstr "" + +msgid "Create Registrar" +msgstr "" + +msgid "Fixed" +msgstr "" + +msgid "Percent" +msgstr "" + +msgid "Create a registrar user by specifying the username (also known as CLID), email, and passwords for EPP and panel access." +msgstr "" + +msgid "Registrars" +msgstr "" + +msgid "Search registrars" +msgstr "" + +msgid "Managed Reserved Names" +msgstr "" + +msgid "Manage Reserved Names" +msgstr "" + +msgid "Update Reserved Names" +msgstr "" + +msgid "Manage TLD" +msgstr "" + +msgid "General Details" +msgstr "" + +msgid "TLD Extension" +msgstr "" + +msgid "TLD Type" +msgstr "" + +msgid "Supported Script" +msgstr "" + +msgid "Pricing" +msgstr "" + +msgid "Setup Fee" +msgstr "" + +msgid "Year" +msgstr "" + +msgid "Years" +msgstr "" + +msgid "Create" +msgstr "" + +msgid "Renew" +msgstr "" + +msgid "Transfer" +msgstr "" + +msgid "When you type a price for 1 Year above, it will automatically get multiplied for subsequent years." +msgstr "" + +msgid "Restore Price" +msgstr "" + +msgid "Enter the price for restoring the TLD." +msgstr "" + +msgid "Premium Names" +msgstr "" + +msgid "Upload CSV File" +msgstr "" + +msgid "Please upload a CSV file containing premium names. Each row should include a name and its corresponding pricing category, separated by a comma. Note: If one or more names in the file already exist in our system, they will be overwritten with the new information provided in the upload." +msgstr "" + +msgid "Set Premium Name Price Categories" +msgstr "" + +msgid "Category Name" +msgstr "" + +msgid "Price" +msgstr "" + +msgid "Update existing premium categories as needed. Please note: currently, deletion of categories is not available. All updates will modify existing information without removing any categories." +msgstr "" + +msgid "Update TLD" +msgstr "" + +msgid "Manage Promotions" +msgstr "" + +msgid "Promotion Name" +msgstr "" + +msgid "Start Date" +msgstr "" + +msgid "End Date" +msgstr "" + +msgid "Discount" +msgstr "" + +msgid "Discount Amount" +msgstr "" + +msgid "No promotions found." +msgstr "" + +msgid "Create New Promotion" +msgstr "" + +msgid "Promotion Start Date" +msgstr "" + +msgid "Please Note:" +msgstr "" + +msgid "All times displayed are in" +msgstr "" + +msgid "Promotion End Date" +msgstr "" + +msgid "Discount Type" +msgstr "" + +msgid "Percentage" +msgstr "" + +msgid "Fixed Amount" +msgstr "" + +msgid "Free Domains" +msgstr "" + +msgid "Discount Value" +msgstr "" + +msgid "Enter discount value" +msgstr "" + +msgid "Maximum Discounted Items" +msgstr "" + +msgid "Conditions" +msgstr "" + +msgid "Enter conditions" +msgstr "" + +msgid "Enter description" +msgstr "" + +msgid "Update Promotions" +msgstr "" + +msgid "Manage Launch Phases" +msgstr "" + +msgid "Phase Category" +msgstr "" + +msgid "Phase Description" +msgstr "" + +msgid "No launch phases found." +msgstr "" + +msgid "Create New Phase" +msgstr "" + +msgid "Single application only." +msgstr "" + +msgid "Multiple applications allowed." +msgstr "" + +msgid "Enter phase description" +msgstr "" + +msgid "Phase Start Date" +msgstr "" + +msgid "Phase End Date" +msgstr "" + +msgid "Update Phases" +msgstr "" + +msgid "Registry Configuration" +msgstr "" + +msgid "System Settings" +msgstr "" + +msgid "Registry Operator Name" +msgstr "" + +msgid "Enter registry operator's name" +msgstr "" + +msgid "The official name of the organization operating the registry." +msgstr "" + +msgid "Registry Handle" +msgstr "" + +msgid "Enter registry handle" +msgstr "" + +msgid "A unique identifier for the registry which will be appended to each object handle." +msgstr "" + +msgid "Enter registry whois server" +msgstr "" + +msgid "Enter the URL of the registry's WHOIS server. Example:" +msgstr "" + +msgid "Enter registry RDAP server" +msgstr "" + +msgid "Enter the URL of the registry's RDAP server. Example:" +msgstr "" + +msgid "Features" +msgstr "" + +msgid "Require Launch Phases" +msgstr "" + +msgid "Operator Details" +msgstr "" + +msgid "Registry VAT/Company Number" +msgstr "" + +msgid "Enter registry operator's VAT number" +msgstr "" + +msgid "Enter the VAT number of the organization operating the registry, or company number if VAT is unavailable." +msgstr "" + +msgid "Contact Address" +msgstr "" + +msgid "Enter contact address" +msgstr "" + +msgid "The contact address of the registry." +msgstr "" + +msgid "Enter contact email" +msgstr "" + +msgid "The email address for general inquiries to the registry." +msgstr "" + +msgid "Contact Phone" +msgstr "" + +msgid "Enter contact phone" +msgstr "" + +msgid "The phone number for general inquiries to the registry." +msgstr "" + +msgid "Update Details" +msgstr "" + +msgid "TLD Management" +msgstr "" + +msgid "Create New TLD" +msgstr "" + +msgid "Search TLDs" +msgstr "" + +msgid "Enter the desired top-level domain (TLD) name, like" +msgstr "" + +msgid "or" +msgstr "" + +msgid "DNSSEC is currently activated manually. This option is for display purposes only." +msgstr "" + +msgid "Select the type of TLD: ccTLD (Country Code Top-Level Domain) or gTLD (Generic Top-Level Domain)." +msgstr "" + +msgid "Cyrillic" +msgstr "" + +msgid "Japanese" +msgstr "" + +msgid "Korean" +msgstr "" + +msgid "Choose the script type that the TLD will support. Options include ASCII, Cyrillic, Japanese, and Korean." +msgstr "" + +msgid "Upload a CSV file with premium names. Each row should contain a name and its pricing category, separated by a comma." +msgstr "" + +msgid "Create TLD" +msgstr "" + +msgid "Reports" +msgstr "" + +msgid "Search reports" +msgstr "" + +msgid "Dashboard" +msgstr "" + +msgid "View Reports" +msgstr "" + +msgid "Create new domain" +msgstr "" + +msgid "Enable dark mode" +msgstr "" + +msgid "Enable light mode" +msgstr "" + +msgid "Logout" +msgstr "" + +msgid "List Domains" +msgstr "" + +msgid "List Applications" +msgstr "" + +msgid "List Contacts" +msgstr "" + +msgid "List Hosts" +msgstr "" + +msgid "List Registrars" +msgstr "" + +msgid "Financials" +msgstr "" + +msgid "Configuration" +msgstr "" + +msgid "TLDs" +msgstr "" + +msgid "Support" +msgstr "" + +msgid "Registry Currency" +msgstr "" + +msgid "Choose the currency for all transactions. This impacts billing and financial reports. Inform stakeholders of changes." +msgstr "" diff --git a/cp/lang/uk_UA/messages.po b/cp/lang/uk_UA/messages.po index 7f8b889..4450f25 100644 --- a/cp/lang/uk_UA/messages.po +++ b/cp/lang/uk_UA/messages.po @@ -1,224 +1,1346 @@ -msgid "Dashboard" -msgstr "Інформаційна панель" +msgid "Approve Transfer" +msgstr "Затвердити передачу" -msgid "Overview" -msgstr "Огляд" +msgid "Cancel Transfer" +msgstr "Скасувати передачу" -msgid "View Reports" -msgstr "Переглянути звіти" +msgid "Reject Transfer" +msgstr "Відхилити передачу" -msgid "Create new domain" -msgstr "Створіть новий домен" +msgid "Completed" +msgstr "Завершено" -msgid "Enable dark mode" -msgstr "Увімкніть темний режим" +msgid "Name" +msgstr "Назва" -msgid "Enable light mode" -msgstr "Увімкніть світлий режим" +msgid "Initiated Date" +msgstr "Дата початку" -msgid "My Profile" -msgstr "Мій профіль" +msgid "Expiry Date" +msgstr "Термін дії" -msgid "Logout" -msgstr "Вийти" +msgid "Status" +msgstr "Стан" -msgid "Domains" -msgstr "Домени" +msgid "Actions" +msgstr "Дії" -msgid "List Domains" -msgstr "Список доменів" +msgid "Are you sure you want to approve this transfer?" +msgstr "Ви впевнені, що хочете схвалити цю передачу?" -msgid "Check Domain" -msgstr "Перевірте домен" +msgid "Confirm" +msgstr "Підтвердити" -msgid "Enter the domain name you want to check:" -msgstr "Введіть доменне ім’я, яке потрібно перевірити:" +msgid "Are you sure you want to cancel this transfer?" +msgstr "Ви впевнені, що бажаєте скасувати цю передачу?" -msgid "Check Availability" -msgstr "Перевірте наявність" +msgid "Are you sure you want to reject this transfer?" +msgstr "Ви впевнені, що бажаєте відхилити цю передачу?" -msgid "is available" -msgstr "доступний" +msgid "No Data" +msgstr "Немає даних" -msgid "is not available" -msgstr "недоступний" +msgid "Registrar" +msgstr "Реєстратор" -msgid "Create Domain" -msgstr "Створити домен" +msgid "Date" +msgstr "Дата" -msgid "Your Domain Name" -msgstr "Ваше доменне ім'я" +msgid "Log" +msgstr "Журнал" -msgid "Registration Years" -msgstr "Реєстрація на скільки років" +msgid "Command" +msgstr "Команда" -msgid "Nameservers" -msgstr "Сервери імен" +msgid "Domain" +msgstr "Домен" -msgid "Add DNSSEC Data" -msgstr "Додати дані DNSSEC" +msgid "Length" +msgstr "Довжина" -msgid "List Applications" -msgstr "Список заяв" +msgid "From" +msgstr "Від" -msgid "Create Application" -msgstr "Створити заявку" +msgid "To" +msgstr "До" -msgid "Transfers" -msgstr "Трансфери" - -msgid "Request Transfer" -msgstr "Запит на перенесення" - -msgid "Contacts" -msgstr "Контакти" - -msgid "List Contacts" -msgstr "Список контактів" - -msgid "Check Contact" -msgstr "Перевірте контакт" - -msgid "Create Contact" -msgstr "Створити контакт" - -msgid "Abuse Prevention" -msgstr "Запобігання зловживанню" - -msgid "Communication" -msgstr "Комунікація" - -msgid "Hosts" -msgstr "Хости" - -msgid "List Hosts" -msgstr "Список хостів" - -msgid "Check Host" -msgstr "Перевірте хост" - -msgid "Create Host" -msgstr "Створити хост" +msgid "Amount" +msgstr "Сума" msgid "Host Name" msgstr "Ім'я хоста" -msgid "Select Registrar" -msgstr "Виберіть Реєстратор" +msgid "Creation Date" +msgstr "Дата створення" -msgid "IPv4 Address" -msgstr "Адреса IPv4" +msgid "Are you sure you want to delete this host?" +msgstr "Ви дійсно бажаєте видалити цього хоста?" -msgid "IPv6 Address" -msgstr "Адреса IPv6" +msgid "Message" +msgstr "Повідомлення" -msgid "Optional" -msgstr "Необов'язковий" +msgid "Message Type" +msgstr "Тип повідомлення" -msgid "Please enter a valid IPv4 address." -msgstr "Введіть дійсну адресу IPv4." +msgid "Object" +msgstr "Об'єкт" -msgid "Please enter a valid IPv6 address." -msgstr "Введіть дійсну адресу IPv6." +msgid "Subject" +msgstr "Тема" -msgid "Create" -msgstr "Створити" +msgid "Category" +msgstr "Категорія" -msgid "Host" -msgstr "Хост" +msgid "Priority" +msgstr "Пріоритет" -msgid "has been created successfully on" -msgstr "було успішно створено на" +msgid "Email" +msgstr "Е-пошта" -msgid "is not available" -msgstr "недоступний" +msgid "Balance" +msgstr "Баланс" -msgid "Logs" -msgstr "Логи" +msgid "Description" +msgstr "Опис" -msgid "Registrars" -msgstr "Реєстратори" +msgid "Identifier" +msgstr "Ідентифікатор" -msgid "List Registrars" -msgstr "Список реєстраторів" +msgid "Phone" +msgstr "Телефон" -msgid "Create Registrar" -msgstr "Створити реєстратора" +msgid "Are you sure you want to delete this contact?" +msgstr "Ви дійсно хочете видалити цей контакт?" -msgid "List Users" -msgstr "Список користувачів" +msgid "Object Type" +msgstr "Тип об'єкта" -msgid "Create User" -msgstr "Створити користувача" +msgid "Result" +msgstr "Результат" -msgid "Users" -msgstr "Користувачі" +msgid "clTRID" +msgstr "clTRID" -msgid "Compliance" -msgstr "Відповідність" +msgid "Milliseconds" +msgstr "Мілісекунди" -msgid "Financials" -msgstr "Фінанси" +msgid "Registrant" +msgstr "Реєстрант" -msgid "Pricing" -msgstr "Ціноутворення" +msgid "Expiration Date" +msgstr "Термін дії" -msgid "Add Deposit" -msgstr "Додати депозит" +msgid "Are you sure you want to delete this domain?" +msgstr "Ви впевнені, що хочете видалити цей домен?" -msgid "Transactions" -msgstr "Транзакції" +msgid "Are you sure you want to restore this domain?" +msgstr "Ви впевнені, що бажаєте відновити цей домен?" -msgid "Account Overview" -msgstr "Огляд рахунку" +msgid "Are you sure you want to submit restore report for this domain?" +msgstr "Ви впевнені, що хочете надіслати звіт про відновлення для цього домену?" -msgid "Invoices" -msgstr "Рахунки-фактури" +msgid "Number" +msgstr "Номер" -msgid "Reports" -msgstr "Звіти" +msgid "Total Domains" +msgstr "Всього доменів" -msgid "Registry" -msgstr "Реєстр" +msgid "Created Domains" +msgstr "Створені домени" -msgid "Settings" -msgstr "Налаштування" +msgid "Renewed Domains" +msgstr "Поновлений домени" -msgid "Activity" -msgstr "Діяльність" +msgid "Transferred Domains" +msgstr "Передані домени" -msgid "Configuration" -msgstr "Конфігурація" +msgid "Deleted Domains" +msgstr "Видалені домени" -msgid "User Management" -msgstr "Керування користувачами" +msgid "Restored Domains" +msgstr "Відновлені домени" -msgid "EPP History" -msgstr "Журнал EPP" +msgid "Script" +msgstr "Алфавіт" -msgid "System Log" -msgstr "Системний журнал" +msgid "Roles" +msgstr "Ролі" -msgid "Message Queue" -msgstr "Черга повідомлень" +msgid "Verified" +msgstr "Перевірено" -msgid "Backup" -msgstr "Резервна копія" +msgid "Applicant" +msgstr "Заявник" -msgid "Support" -msgstr "Підтримка" +msgid "Phase" +msgstr "Фаза" -msgid "Support Tickets" -msgstr "Заявки на підтримку" +msgid "Are you sure you want to delete this application?" +msgstr "Ви впевнені, що хочете видалити цю заявку?" -msgid "Create Ticket" -msgstr "Створити заявку" +msgid "My Profile" +msgstr "Мій профіль" -msgid "Create New Ticket" -msgstr "Створити новий запит" +msgid "Overview" +msgstr "Огляд" + +msgid "Details" +msgstr "Деталі" + +msgid "User Name" +msgstr "Ім'я користувача" + +msgid "Role" +msgstr "Роль" + +msgid "Change Password" +msgstr "Змінити пароль" + +msgid "Old Password" +msgstr "Старий пароль" + +msgid "New Password" +msgstr "Новий пароль" + +msgid "Back" +msgstr "Назад" + +msgid "Update" +msgstr "Оновити" + +msgid "Two-Factor Authentication" +msgstr "Двофакторна аутентифікація" + +msgid "Set up 2FA for additional security. Scan the QR code with your authentication app and enter the provided code below to verify." +msgstr "Налаштуйте 2FA для додаткового захисту. Відскануйте QR-код за допомогою програми автентифікації та введіть наведений нижче код для перевірки." + +msgid "Manual Entry Secret" +msgstr "Секрет Введення вручну" + +msgid "If you're unable to scan the QR code, enter this secret manually into your authentication app. The secret is case-sensitive and should be entered exactly as shown." +msgstr "Якщо ви не можете сканувати QR-код, введіть цей секрет вручну в додатку для автентифікації. Секрет чутливий до регістру і повинен бути введений точно, як показано зараз." + +msgid "Verification Code" +msgstr "Код підтвердження" + +msgid "Enter code" +msgstr "Введіть код" + +msgid "Enter the code generated by your authentication app. This code verifies that your 2FA setup is working correctly. Once entered, click" +msgstr "Введіть код, згенерований вашим додатком для автентифікації. Цей код перевіряє, що налаштування 2FA працюють правильно. Після введення, натисніть кнопку" + +msgid "Save 2FA Settings" +msgstr "Збережіть налаштування 2FA" + +msgid "to activate two-factor authentication for your account." +msgstr "щоб активувати двофакторну автентифікацію для вашого облікового запису." + +msgid "Your account is secured with an additional layer of protection." +msgstr "Ваш обліковий запис захищений з додатковим рівнем захисту." + +msgid "2FA is currently" +msgstr "Наразі 2FA" + +msgid "enabled" +msgstr "увімкнено" + +msgid "for your account. If you encounter any issues or need to disable 2FA, please contact our support team for assistance." +msgstr "для вашого облікового запису. Якщо ви зіткнетеся з будь-якими питаннями або потребуєте вимкнення 2FA, зверніться до нашої служби підтримки за допомогою." + +msgid "WebAuthn Authentication" +msgstr "WebAuthn автентифікація" + +msgid "Secure your account with WebAuthn. Click the button below to register your device for passwordless sign-in." +msgstr "Захистіть свій обліковий запис за допомогою WebAuth. Натисніть кнопку нижче, щоб зареєструвати пристрій для безпаролю." + +msgid "Connect WebAuthn Device" +msgstr "Підключіть WebAuthn пристрій" + +msgid "Device/Browser Info" +msgstr "Інформація про пристрій/браузер" + +msgid "Registration Date" +msgstr "Дата реєстрації" + +msgid "Action" +msgstr "Дія" + +msgid "Edit" +msgstr "Змінити" + +msgid "No devices found." +msgstr "Пристроїв не знайдено." + +msgid "User Audit Log" +msgstr "Журнал аудиту користувача" + +msgid "Track and review all user activities in your account below. Monitor logins, profile changes, and other key actions to ensure security and transparency." +msgstr "Відстежуйте і переглядайте усі дії користувача у вашому обліковому записі нижче. Спостерігайте логін, зміни профілю та інші дії з метою забезпечення безпеки і прозорості." + +msgid "Event" +msgstr "Подія" + +msgid "User Agent" +msgstr "Ідентифікатор браузера" + +msgid "Location" +msgstr "Розташування" + +msgid "Timestamp" +msgstr "Відмітка часу" + +msgid "No log data for user." +msgstr "Немає записів для користувача." msgid "Documentation" msgstr "Документація" +msgid "Support Tickets" +msgstr "Тікети підтримки" + msgid "Media Kit" -msgstr "Медіакомплект" \ No newline at end of file +msgstr "Медіа-кіт" + +msgid "Create New Ticket" +msgstr "Створити нову заявку" + +msgid "Search" +msgstr "Пошук" + +msgid "Search tickets" +msgstr "Пошук заявок" + +msgid "Ticket Overview" +msgstr "Огляд тікету" + +msgid "Ticket" +msgstr "Тікет" + +msgid "Unknown Status" +msgstr "Невідомий стан" + +msgid "Ticket Details" +msgstr "Деталі тікету" + +msgid "Created On" +msgstr "Створено" + +msgid "Conversation" +msgstr "Розмова" + +msgid "Your Response" +msgstr "Ваша відповідь" + +msgid "Submit Response" +msgstr "Надіслати відповідь" + +msgid "New Support Ticket" +msgstr "Нова заява на підтримку" + +msgid "Support ticket can not be created" +msgstr "Квиток підтримки не можливо створити" + +msgid "Select a category" +msgstr "Виберіть категорію" + +msgid "Create Ticket" +msgstr "Створити тікет" + +msgid "Create Domain" +msgstr "Створіть домен" + +msgid "Your Domain Name" +msgstr "Ваше доменне ім'я" + +msgid "Select Registrar" +msgstr "Виберіть реєстратор" + +msgid "Registration Years" +msgstr "Роки реєстрації" + +msgid "Estimated Price" +msgstr "Орієнтовна ціна" + +msgid "Contacts" +msgstr "Контакти" + +msgid "Registrant Contact" +msgstr "Контактна особа реєстранта" + +msgid "Admin Contact" +msgstr "Адміністратор контакт" + +msgid "Tech Contact" +msgstr "Технічний контакт" + +msgid "Billing Contact" +msgstr "Платіжні контакти" + +msgid "Nameservers" +msgstr "Сервери імен" + +msgid "Nameserver" +msgstr "Сервер імен" + +msgid "Add DNSSEC Data" +msgstr "Додати DNSSEC дані" + +msgid "DS Record" +msgstr "DS запис" + +msgid "Key Tag" +msgstr "Key Tag" + +msgid "Select Algorithm" +msgstr "Виберіть алгоритм" + +msgid "Select Digest Type" +msgstr "Виберіть тип Digest" + +msgid "Digest" +msgstr "Digest" + +msgid "DNSKEY Record" +msgstr "DNSKEY запис" + +msgid "Flags" +msgstr "Flags" + +msgid "Protocol" +msgstr "Протокол" + +msgid "Public Key" +msgstr "Публічний ключ" + +msgid "Auth Info" +msgstr "Пароль домену" + +msgid "Statuses" +msgstr "Статуси" + +msgid "Server Statuses" +msgstr "Статуси сервера" + +msgid "Phase Type" +msgstr "Тип фази" + +msgid "Signed Mark Information" +msgstr "Підписана інформація про марку" + +msgid "Paste SMD contents" +msgstr "Вставити вміст SMD" + +msgid "Notice ID" +msgstr "ID повідомлення" + +msgid "Not After Date" +msgstr "Не після дати" + +msgid "Accepted Date" +msgstr "Дата прийняття" + +msgid "Allocation Token" +msgstr "Allocation Token" + +msgid "Allocation token" +msgstr "Allocation token" + +msgid "Domain Details" +msgstr "Деталі домену" + +msgid "Registered On" +msgstr "Зареєстровані на" + +msgid "Last Updated" +msgstr "Останнє оновлення" + +msgid "contact" +msgstr "контакт" + +msgid "Auth Type" +msgstr "Тип авторизації" + +msgid "DNSSEC Data" +msgstr "Дані DNSSEC" + +msgid "Domains" +msgstr "Домени" + +msgid "Check Domain" +msgstr "Перевірити домен" + +msgid "Search domains" +msgstr "Пошук доменів" + +msgid "Application Details" +msgstr "Деталі заявки" + +msgid "Application for" +msgstr "Заявка на" + +msgid "Application ID" +msgstr "ID заявки" + +msgid "Launch Phase" +msgstr "Launch Phase" + +msgid "Phase Name" +msgstr "Назва фази" + +msgid "Updating Application" +msgstr "Оновити заявку" + +msgid "Update Application" +msgstr "Оновити заявку" + +msgid "Create Application" +msgstr "Створити заяву" + +msgid "Renew Domain" +msgstr "Поновити домен" + +msgid "Domain Name" +msgstr "Ім'я домену" + +msgid "Your domain is currently renewed to its maximum term. At this time, no additional renewal is possible." +msgstr "Ваш домен зараз поновлено до свого максимального терміну. На даний момент неможливо додаткових оновлень." + +msgid "Applications" +msgstr "Заяви" + +msgid "Search applications" +msgstr "Пошук заяв" + +msgid "Updating Domain" +msgstr "Оновлення домену" + +msgid "Domain Security" +msgstr "Безпека домену" + +msgid "Update Domain" +msgstr "Оновити домен" + +msgid "Request Domain Transfer" +msgstr "Запит на передачу домену" + +msgid "Gaining Registrar" +msgstr "Реєстратор отримання" + +msgid "Transfer And Renew" +msgstr "Передача та Поновлення" + +msgid "Request Transfer" +msgstr "Запит на передачу" + +msgid "Transfers" +msgstr "Передачі" + +msgid "Search transfers" +msgstr "Пошук передач" + +msgid "Enter the domain name you want to check:" +msgstr "Введіть ім'я домену, яке ви хочете перевірити:" + +msgid "Check Availability" +msgstr "Перевірте наявність" + +msgid "Check claims" +msgstr "Перевірити claims" + +msgid "Create Contact" +msgstr "Створити контакт" + +msgid "General & Internationalized Info" +msgstr "Загальні та Інтернаціоналізовані дані" + +msgid "Disclose in WHOIS" +msgstr "Розкриття в WHOIS" + +msgid "Organization" +msgstr "Організація" + +msgid "Street" +msgstr "Вулиця" + +msgid "City" +msgstr "Місто" + +msgid "State/Province" +msgstr "Регіон/Область" + +msgid "Postal Code" +msgstr "Поштовий індекс" + +msgid "Country" +msgstr "Країна" + +msgid "Disclose Address in WHOIS" +msgstr "Розкрити адресу в WHOIS" + +msgid "Contact Details" +msgstr "Контактні дані" + +msgid "Contact ID" +msgstr "ID контакта" + +msgid "Voice" +msgstr "Телефон" + +msgid "Fax" +msgstr "Факс" + +msgid "Contact AuthInfo" +msgstr "Контактний пароль" + +msgid "Auto-generated authentication information for the contact" +msgstr "Автоматично створена інформація автентифікації для контакту" + +msgid "NIN - National Identification Number" +msgstr "NIN - Національний ідентифікаційний номер" + +msgid "This is a Business Contact" +msgstr "Це бізнес контакт" + +msgid "Verify by Phone" +msgstr "Підтвердити по телефону" + +msgid "Verify by Email" +msgstr "Підтвердити електронною поштою" + +msgid "Verify by Postal Mail" +msgstr "Підтвердити поштою" + +msgid "Include Localized Info" +msgstr "Включити локалізовану інформацію" + +msgid "Localized Postal Info: Personal Details" +msgstr "Локалізована поштова інформація: персональні дані" + +msgid "Localized Postal Info: Address Details" +msgstr "Локалізована поштова інформація: відомості про адресу" + +msgid "Search contacts" +msgstr "Пошук контактів" + +msgid "Update Contact" +msgstr "Оновити контакт" + +msgid "Authentication information for the contact" +msgstr "Інформація для автентифікації" + +msgid "Contact" +msgstr "Контакт" + +msgid "linked" +msgstr "пов’язаний" + +msgid "Visible in Public" +msgstr "Видимий всім" + +msgid "Hidden from Public" +msgstr "Приховано з публічного" + +msgid "International" +msgstr "Міжнародний" + +msgid "Localized" +msgstr "Локалізований" + +msgid "International Contact Details" +msgstr "Міжнародні контактні дані" + +msgid "Localized Contact Details" +msgstr "Локалізовані контактні дані" + +msgid "No Localized Contact Information Available" +msgstr "Не знайдено локалізованої контактної інформації" + +msgid "System Log" +msgstr "Системний журнал" + +msgid "Search logs" +msgstr "Пошук в журналах" + +msgid "Message Queue" +msgstr "Черга повідомлень" + +msgid "Search queue" +msgstr "Пошук черги" + +msgid "EPP History" +msgstr "Історія EPP" + +msgid "List Users" +msgstr "Список користувачів" + +msgid "Search users" +msgstr "Пошук користувачів" + +msgid "Host Details" +msgstr "Деталі хосту" + +msgid "Hosts" +msgstr "Хости" + +msgid "Create Host" +msgstr "Створити хост" + +msgid "Search hosts" +msgstr "Пошук хостів" + +msgid "Update Host" +msgstr "Оновити хост" + +msgid "The host you're trying to update" +msgstr "Хост Ви намагаєтеся оновити" + +msgid "is external to the registry. Consequently, it does not store any IP addresses within our system, and therefore, does not require any updates from your end. This means there are no associated IP addresses under our management that need your attention." +msgstr "є зовнішнім в реєстрі. Отже, він не зберігає жодних IP-адрес в нашій системі, і тому не вимагає оновлення з вашого кінця. Це означає, що не пов'язані IP-адреси під нашим управлінням, які потребують вашої уваги." + +msgid "IPv4 Address" +msgstr "Адреса IPv4" + +msgid "Please enter a valid IPv4 address." +msgstr "Будь ласка введіть правильну IPv4-адресу." + +msgid "IPv6 Address" +msgstr "Адреса IPv6" + +msgid "Please enter a valid IPv6 address." +msgstr "Будь ласка введіть правильну IPv6-адресу." + +msgid "Optional" +msgstr "Необов'язково" + +msgid "Deposit Payment Unsuccessful" +msgstr "Помилка платежу за депозит" + +msgid "Ready to try again? When you're set to proceed with your deposit, simply return to the" +msgstr "Готові спробувати ще раз? Коли ви встановили для продовження свого депозиту, просто повернутися до" + +msgid "Deposit Page" +msgstr "Сторінка депозиту" + +msgid "to initiate a new payment. We value your partnership and are committed to assisting you every step of the way." +msgstr "ініціювати новий платіж. Ми цінуємо ваше партнерство і зобов'язуємося допомагати вам на кожному кроці." + +msgid "Registrar Deposit" +msgstr "Депозит реєстратора" + +msgid "Choose Registrar..." +msgstr "Виберіть реєстратора..." + +msgid "Current Balance for" +msgstr "Поточний баланс для" + +msgid "Enter deposit amount" +msgstr "Введіть сумму депозиту" + +msgid "Optional deposit description" +msgstr "Необов'язковий опис депозиту" + +msgid "Add Deposit" +msgstr "Додати депозит" + +msgid "Account Overview" +msgstr "Огляд аккаунта" + +msgid "Search account" +msgstr "Пошук облікового запису" + +msgid "View Invoice" +msgstr "Переглянути рахунок" + +msgid "Invoice" +msgstr "Рахунок" + +msgid "Print Invoice" +msgstr "Роздрукувати рахунок" + +msgid "Provider" +msgstr "Постачальник" + +msgid "Registry" +msgstr "Реєстр" + +msgid "Client" +msgstr "Клієнт" + +msgid "Invoice Issued On" +msgstr "Рахунок виданий на" + +msgid "Due Date" +msgstr "Кінцева дата" + +msgid "Product" +msgstr "Продукт" + +msgid "No items found." +msgstr "Нічого не знайдено." + +msgid "Subtotal" +msgstr "Проміжна сума" + +msgid "Vat Rate" +msgstr "Вартість ПДВ" + +msgid "Vat Due" +msgstr "ПДВ до сплати" + +msgid "Total Due" +msgstr "Всього до Сплати" + +msgid "Notes" +msgstr "Нотатки" + +msgid "Thank you very much for doing business with us. We look forward to working with you again!" +msgstr "Дякуємо Вам за бізнес з нами. Ми з нетерпінням чекаємо на роботу з вами знову!" + +msgid "Transactions" +msgstr "Операції" + +msgid "Search transactions" +msgstr "Пошук операцій" + +msgid "Invoices" +msgstr "Рахунки" + +msgid "Search invoices" +msgstr "Пошук рахунків" + +msgid "Deposit with Stripe" +msgstr "Поповнити з Stripe" + +msgid "Registrar Details" +msgstr "Подробиці про реєстратора" + +msgid "Contact Email" +msgstr "Контактна е-пошта" + +msgid "Website" +msgstr "Сайт" + +msgid "Abuse Phone" +msgstr "Телефон для порушення" + +msgid "Abuse Email" +msgstr "Електронна пошта для скарг" + +msgid "WHOIS Server" +msgstr "Сервер WHOIS" + +msgid "RDAP Server" +msgstr "Сервер RDAP" + +msgid "Currency" +msgstr "Валюта" + +msgid "Account Balance" +msgstr "Баланс рахунку" + +msgid "Credit Limit" +msgstr "Кредитний ліміт" + +msgid "Credit Threshold" +msgstr "Поріг кредитування" + +msgid "Threshold Type" +msgstr "Тип порогу" + +msgid "IP Whitelisting" +msgstr "IP-список" + +msgid "No data available." +msgstr "Немає даних." + +msgid "Registrar User" +msgstr "Користувач Реєстратора" + +msgid "EPP Username/CLID" +msgstr "Ім'я користувача/CLID" + +msgid "Panel Login Email" +msgstr "E-mail для входу в систему" + +msgid "Operational Test and Evaluation (OTE)" +msgstr "Операційний тест і оцінювання (OTE)" + +msgid "Successfully passing the Operational Test and Evaluation (OTE) is a mandatory requirement for registrars. The OTE process evaluates the interaction and compliance of the registrar's system with registry operations through a series of EPP command tests in a controlled environment. Below you can find the results of these essential EPP command tests for your account:" +msgstr "Передача операційних випробувань і оцінювання (OTE) є обов'язковою вимогою для реєстраторів. Процес OTE оцінює взаємодію та дотримання системи реєстру за допомогою системних операцій через ряд тестів команд EPP в контрольованих умовах. Нижче ви можете знайти результати таких важливих тестів команд EPP для вашого облікового запису:" + +msgid "Pending" +msgstr "В очікуванні" + +msgid "Failed" +msgstr "Невдало" + +msgid "Update Registrar" +msgstr "Оновити реєстратор" + +msgid "The official name of the registrar." +msgstr "Офіційна назва реєстрара." + +msgid "Unique identifier assigned by IANA." +msgstr "Унікальний ідентифікатор, виданий IANA." + +msgid "Primary contact email of the registrar." +msgstr "Основна контактна пошта реєстрара." + +msgid "URL" +msgstr "URL" + +msgid "Registrar's official website URL." +msgstr "Посилання на офіційний сайт Реєстрара." + +msgid "Address of the registrar's WHOIS server." +msgstr "Адреса сервера WHOIS реєстратора." + +msgid "Address of the registrar's RDAP server." +msgstr "Адреса сервера RDAP реєстратора." + +msgid "Email address for reporting abuse." +msgstr "Email адреса для звітування про порушення." + +msgid "Phone number for reporting abuse." +msgstr "Номер телефону для звітування про порушення." + +msgid "Financial Information" +msgstr "Фінансова інформація" + +msgid "Current balance in the registrar's account." +msgstr "Поточний баланс в обліковому записі реєстрара." + +msgid "Maximum credit limit for the registrar." +msgstr "Максимальний кредитний ліміт для реєстра." + +msgid "Credit threshold triggering alerts or actions." +msgstr "Кредитний порог, викликаючи оповіщення або дії." + +msgid "Type of threshold: fixed value or percentage." +msgstr "Тип порогового порогу: фіксована вартість або відсотки." + +msgid "Registrar Contacts" +msgstr "Контакти реєстратора" + +msgid "Owner" +msgstr "Власник" + +msgid "Billing" +msgstr "Білінг" + +msgid "Abuse" +msgstr "Порушення" + +msgid "Copy data to other contacts" +msgstr "Копіювати дані в інші контакти" + +msgid "First Name" +msgstr "Ім’я" + +msgid "Last Name" +msgstr "Прізвище" + +msgid "Street Address" +msgstr "Адреса" + +msgid "Whitelist IP addresses for secure access. Up to 5 IP addresses (IPv4 or IPv6) can be added." +msgstr "Ви можете додати IP адреси для захищеного доступу. до 5 IP-адрес (IPv4 або IPv6)." + +msgid "For an existing registrar user, you can view the current EPP username (also known as CLID) and panel access email. Additionally, you have the option to reset the passwords for EPP and panel access as needed." +msgstr "Для наявного користувача-реєстратора ви можете переглянути поточне ім’я користувача EPP (також відоме як CLID) та електронну адресу для доступу до панелі. Крім того, у вас є можливість скинути паролі для EPP і доступу до панелі за потреби." + +msgid "Username/CLID" +msgstr "Ім'я користувача/CLID" + +msgid "Login Email" +msgstr "Електронна пошта для входу" + +msgid "Panel Password" +msgstr "Пароль для панелі" + +msgid "EPP Password" +msgstr "EPP пароль" + +msgid "Update Panel Password" +msgstr "Оновити пароль для панелі" + +msgid "Update EPP Password" +msgstr "Оновити EPP пароль" + +msgid "Operational Test and Evaluation (OTE) assesses the functionality of EPP commands in a simulated environment, ensuring effective communication between the registrar's system and the registry. Below are key EPP commands and their test statuses:" +msgstr "Операційний тест та оцінки (OTE) оцінює функціональність команд EPP в симулятивному середовищі, забезпечуючи ефективний зв'язок між системою реєстратора та реєстратором. Нижче наведено ключові команди EPP та їх статус тесту:" + +msgid "Create Registrar" +msgstr "Створити реєстратора" + +msgid "Fixed" +msgstr "Фіксований" + +msgid "Percent" +msgstr "Процент" + +msgid "Create a registrar user by specifying the username (also known as CLID), email, and passwords for EPP and panel access." +msgstr "Створіть зареєстрованого користувача, вказавши ім'я користувача (також відомий як CLID), електронну пошту та паролі для доступу до панелі управління." + +msgid "Registrars" +msgstr "Реєстратори" + +msgid "Search registrars" +msgstr "Пошук реєстраторів" + +msgid "Managed Reserved Names" +msgstr "Керовані зарезервовані імена" + +msgid "Manage Reserved Names" +msgstr "Керування зарезервованими іменами" + +msgid "Update Reserved Names" +msgstr "Оновити зарезервовані імена" + +msgid "Manage TLD" +msgstr "Керування TLD" + +msgid "General Details" +msgstr "Основна інформація" + +msgid "TLD Extension" +msgstr "TLD розширення" + +msgid "TLD Type" +msgstr "Тип TLD" + +msgid "Supported Script" +msgstr "Підтримуваний скрипт" + +msgid "Pricing" +msgstr "Ціни" + +msgid "Setup Fee" +msgstr "Плата за встановлення" + +msgid "Year" +msgstr "Рік" + +msgid "Years" +msgstr "роки(ів)" + +msgid "Create" +msgstr "Створити" + +msgid "Renew" +msgstr "Продовжити" + +msgid "Transfer" +msgstr "Передати" + +msgid "When you type a price for 1 Year above, it will automatically get multiplied for subsequent years." +msgstr "Коли ви вводите ціну на 1 рік вище, вона автоматично буде множуватися на наступні роки." + +msgid "Restore Price" +msgstr "Ціна за відновлення" + +msgid "Enter the price for restoring the TLD." +msgstr "Введіть ціну для відновлення TLD." + +msgid "Premium Names" +msgstr "Преміум Імена" + +msgid "Upload CSV File" +msgstr "Завантажити CSV файл" + +msgid "Please upload a CSV file containing premium names. Each row should include a name and its corresponding pricing category, separated by a comma. Note: If one or more names in the file already exist in our system, they will be overwritten with the new information provided in the upload." +msgstr "Будь ласка, завантажте CSV файл, що містить преміум імена. Кожен рядок повинен включати назву та відповідну категорію ціноутворення, розділену комою. Примітка: Якщо одне або декілька імен у файлі вже існує в нашій системі, вони будуть перезаписані в адресі новою інформацією." + +msgid "Set Premium Name Price Categories" +msgstr "Встановити преміум-назву цінових категорій" + +msgid "Category Name" +msgstr "Назва категорії" + +msgid "Price" +msgstr "Ціна" + +msgid "Update existing premium categories as needed. Please note: currently, deletion of categories is not available. All updates will modify existing information without removing any categories." +msgstr "За потреби оновіть наявні преміум-категорії. Зверніть увагу: наразі видалення категорій недоступне. Усі оновлення змінюватимуть наявну інформацію без видалення жодних категорій." + +msgid "Update TLD" +msgstr "Оновити TLD" + +msgid "Manage Promotions" +msgstr "Керування акціями" + +msgid "Promotion Name" +msgstr "Назва акції" + +msgid "Start Date" +msgstr "Дата початку" + +msgid "End Date" +msgstr "Дата завершення" + +msgid "Discount" +msgstr "Знижка" + +msgid "Discount Amount" +msgstr "Сума знижки" + +msgid "No promotions found." +msgstr "Промо-акції не знайдено." + +msgid "Create New Promotion" +msgstr "Створити нову акції" + +msgid "Promotion Start Date" +msgstr "Дата початку акції" + +msgid "Please Note:" +msgstr "Зверніть Увагу:" + +msgid "All times displayed are in" +msgstr "Усі часи відображені в" + +msgid "Promotion End Date" +msgstr "Дата закінчення акції" + +msgid "Discount Type" +msgstr "Тип знижки" + +msgid "Percentage" +msgstr "Відсоток" + +msgid "Fixed Amount" +msgstr "Фіксована Сума" + +msgid "Free Domains" +msgstr "Безкоштовні домени" + +msgid "Discount Value" +msgstr "Розмір знижки" + +msgid "Enter discount value" +msgstr "Інша вартість знижок" + +msgid "Maximum Discounted Items" +msgstr "Максимальна кількість товарів зі знижкою" + +msgid "Conditions" +msgstr "Умови" + +msgid "Enter conditions" +msgstr "Введіть умови" + +msgid "Enter description" +msgstr "Введіть опис" + +msgid "Update Promotions" +msgstr "Оновити промо-акції" + +msgid "Manage Launch Phases" +msgstr "Вимагати стартові фази" + +msgid "Phase Category" +msgstr "Фазова категорія" + +msgid "Phase Description" +msgstr "Опис фази" + +msgid "No launch phases found." +msgstr "Не знайдено фази запуску." + +msgid "Create New Phase" +msgstr "Створити нову фазу" + +msgid "Single application only." +msgstr "Тільки одна заявка." + +msgid "Multiple applications allowed." +msgstr "Дозволено кілька заяви." + +msgid "Enter phase description" +msgstr "Введіть опис" + +msgid "Phase Start Date" +msgstr "Початкова дата фази" + +msgid "Phase End Date" +msgstr "Кінцева дата фази" + +msgid "Update Phases" +msgstr "Оновлення фаз" + +msgid "Registry Configuration" +msgstr "Конфігурація реєстру" + +msgid "System Settings" +msgstr "Налаштування системи" + +msgid "Registry Operator Name" +msgstr "Ім'я оператора реєстру" + +msgid "Enter registry operator's name" +msgstr "Введіть ім'я оператора реєстру" + +msgid "The official name of the organization operating the registry." +msgstr "Офіційна назва організації." + +msgid "Registry Handle" +msgstr "Registry Handle" + +msgid "Enter registry handle" +msgstr "Введіть registry handle" + +msgid "A unique identifier for the registry which will be appended to each object handle." +msgstr "Унікальний ідентифікатор реєстру, який буде додано до кожного об'єкта." + +msgid "Enter registry whois server" +msgstr "Введіть WHOIS сервер реєстру" + +msgid "Enter the URL of the registry's WHOIS server. Example:" +msgstr "Введіть URL сервера WHOIS. Приклад:" + +msgid "Enter registry RDAP server" +msgstr "Введіть RDAP сервер реєстру" + +msgid "Enter the URL of the registry's RDAP server. Example:" +msgstr "Введіть URL сервера RDAP. Приклад:" + +msgid "Features" +msgstr "Особливості" + +msgid "Require Launch Phases" +msgstr "Вимагати стартові фази" + +msgid "Operator Details" +msgstr "Деталі оператора" + +msgid "Registry VAT/Company Number" +msgstr "Реєстраційний номер платника ПДВ / номер компанії" + +msgid "Enter registry operator's VAT number" +msgstr "Введіть номер ПДВ оператора в реєстрі" + +msgid "Enter the VAT number of the organization operating the registry, or company number if VAT is unavailable." +msgstr "Введіть номер ПДВ організації, яка керує реєстром, або номер компанії, якщо ПДВ недоступний." + +msgid "Contact Address" +msgstr "Адреса контакту" + +msgid "Enter contact address" +msgstr "Введіть адресу контакта" + +msgid "The contact address of the registry." +msgstr "Контактна адреса реєстру." + +msgid "Enter contact email" +msgstr "Введіть пошту для звʼязку" + +msgid "The email address for general inquiries to the registry." +msgstr "Адреса електронної пошти для загальних запитів до реєстру." + +msgid "Contact Phone" +msgstr "Контактний телефон" + +msgid "Enter contact phone" +msgstr "Введіть пошту для звʼязку" + +msgid "The phone number for general inquiries to the registry." +msgstr "Номер телефону для загальних запитів до реєстру." + +msgid "Update Details" +msgstr "Оновити деталі" + +msgid "TLD Management" +msgstr "Керування TLD" + +msgid "Create New TLD" +msgstr "Створити новий TLD" + +msgid "Search TLDs" +msgstr "Пошук TLDs" + +msgid "Enter the desired top-level domain (TLD) name, like" +msgstr "Введіть бажаний домен верхнього рівня (TLD) наприклад," + +msgid "or" +msgstr "або" + +msgid "DNSSEC is currently activated manually. This option is for display purposes only." +msgstr "Зараз активовано режим DNSSEC вручну. Цей параметр стосується лише вказаних цілей." + +msgid "Select the type of TLD: ccTLD (Country Code Top-Level Domain) or gTLD (Generic Top-Level Domain)." +msgstr "Виберіть тип TLD: ccTLD (Домен верхнього рівня країни) або gTLD (типовий Домен верхнього рівня)." + +msgid "Cyrillic" +msgstr "Кириличне" + +msgid "Japanese" +msgstr "Японський" + +msgid "Korean" +msgstr "Корейський" + +msgid "Choose the script type that the TLD will support. Options include ASCII, Cyrillic, Japanese, and Korean." +msgstr "Виберіть тип скрипта, який підтримуватиме TLD. Варіанти включають ASCII, кирилицю, японську та корейську." + +msgid "Upload a CSV file with premium names. Each row should contain a name and its pricing category, separated by a comma." +msgstr "Завантажити CSV файл з преміум іменами. Кожен рядок повинен містити назву та її категорію ціноутворення, розділених комою." + +msgid "Create TLD" +msgstr "Створити TLD" + +msgid "Reports" +msgstr "Звіти" + +msgid "Search reports" +msgstr "Пошук звітів" + +msgid "Dashboard" +msgstr "Панель керування" + +msgid "View Reports" +msgstr "Перегляд звітів" + +msgid "Create new domain" +msgstr "Створити новий домен" + +msgid "Enable dark mode" +msgstr "Увімкнути темний режим" + +msgid "Enable light mode" +msgstr "Увімкнути світлий режим" + +msgid "Logout" +msgstr "Вийти" + +msgid "List Domains" +msgstr "Список доменів" + +msgid "List Applications" +msgstr "Список заяви" + +msgid "List Contacts" +msgstr "Список контактів" + +msgid "List Hosts" +msgstr "Список хостів" + +msgid "List Registrars" +msgstr "Список реєстраторів" + +msgid "Financials" +msgstr "Фінанси" + +msgid "Configuration" +msgstr "Налаштування" + +msgid "TLDs" +msgstr "TLDs" + +msgid "Support" +msgstr "Підтримка" + +msgid "Registry Currency" +msgstr "Валюта реєстру" + +msgid "Choose the currency for all transactions. This impacts billing and financial reports. Inform stakeholders of changes." +msgstr "Виберіть валюту для всіх операцій. Це впливає на рахунки та фінансові звіти. Інформувати зацікавлених сторін про зміни." diff --git a/cp/resources/views/admin/contacts/createContact.twig b/cp/resources/views/admin/contacts/createContact.twig index 5287956..e148332 100644 --- a/cp/resources/views/admin/contacts/createContact.twig +++ b/cp/resources/views/admin/contacts/createContact.twig @@ -11,7 +11,7 @@
- Overview + {{ __('Overview') }}

{{ __('Create Contact') }} @@ -24,31 +24,7 @@
- {% if contactID is defined and crdate is defined %} - - {% elseif error is defined %} - - {% endif %} + {% include 'partials/flash.twig' %}
@@ -56,21 +32,21 @@
-
General & Internationalized Info
+
{{ __('General & Internationalized Info') }}
- +
{% if registrars and not registrar %}
- +
- +
- +
- +
- +
- +
- +
- + - Disclose Address in WHOIS + {{ __('Disclose Address in WHOIS') }}
-
Contact Details
+
{{ __('Contact Details') }}
- +
- +
- +
- +
- + - Auto-generated authentication information for the contact. + {{ __('Auto-generated authentication information for the contact') }}.
- +
@@ -187,7 +163,7 @@
- +
@@ -195,15 +171,15 @@
- +
- +
- +
@@ -214,7 +190,7 @@
@@ -222,64 +198,64 @@
-
-
-
-
    -
  • - Copyright © 2023 - Namingo. -
  • -
-
-
-
- + {% include 'partials/footer.twig' %}