From 4d0dfe258086a8232a1c9ddc97937e36e25f9d09 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Tue, 7 Nov 2023 19:04:23 +0100 Subject: [PATCH 01/59] New license dialog --- config/areas/system/dialogs.php | 42 ++++++------ config/areas/system/views.php | 2 +- .../src/components/Dialogs/LicenseDialog.vue | 67 +++++++++++++++++++ panel/src/components/Dialogs/index.js | 2 + src/Cms/System.php | 33 +++++---- 5 files changed, 111 insertions(+), 35 deletions(-) create mode 100644 panel/src/components/Dialogs/LicenseDialog.vue diff --git a/config/areas/system/dialogs.php b/config/areas/system/dialogs.php index de88a5f596..9a7b22dab5 100644 --- a/config/areas/system/dialogs.php +++ b/config/areas/system/dialogs.php @@ -2,6 +2,7 @@ use Kirby\Cms\App; use Kirby\Panel\Field; +use Kirby\Panel\Panel; use Kirby\Toolkit\I18n; return [ @@ -10,31 +11,28 @@ 'load' => function () { $license = App::instance()->system()->license(); - // @codeCoverageIgnoreStart - // the system is registered but the license - // key is only visible for admins - if ($license === true) { - $license = 'Kirby 3'; + if ($license === false) { + go(Panel::url('dialogs/registration')); } - // @codeCoverageIgnoreEnd return [ - 'component' => 'k-form-dialog', + 'component' => 'k-license-dialog', 'props' => [ - 'size' => 'medium', - 'fields' => [ - 'license' => [ - 'type' => 'info', - 'label' => I18n::translate('license'), - 'text' => $license ? $license : I18n::translate('license.unregistered.label'), - 'theme' => $license ? 'code' : 'negative', - 'help' => $license ? - // @codeCoverageIgnoreStart - '' . I18n::translate('license.manage') . ' →' : - // @codeCoverageIgnoreEnd - '' . I18n::translate('license.buy') . ' →' - ] - ], + 'size' => 'large', + 'license' => $license, + // 'fields' => [ + // 'license' => [ + // 'type' => 'info', + // 'label' => I18n::translate('license'), + // 'text' => $license ? $license['license'] : I18n::translate('license.unregistered.label'), + // 'theme' => $license ? 'code' : 'negative', + // 'help' => $license ? + // // @codeCoverageIgnoreStart + // '' . I18n::translate('license.manage') . ' →' : + // // @codeCoverageIgnoreEnd + // '' . I18n::translate('license.buy') . ' →' + // ] + // ], 'submitButton' => false, 'cancelButton' => false, ] @@ -62,7 +60,7 @@ 'type' => 'text', 'required' => true, 'counter' => false, - 'placeholder' => 'K3-', + 'placeholder' => 'K-', 'help' => I18n::translate('license.code.help') . ' ' . '' . I18n::translate('license.buy') . ' →' ], 'email' => Field::email(['required' => true]) diff --git a/config/areas/system/views.php b/config/areas/system/views.php index b1b99bda83..c53ad54061 100644 --- a/config/areas/system/views.php +++ b/config/areas/system/views.php @@ -15,7 +15,7 @@ $environment = [ [ 'label' => $license ? I18n::translate('license') : I18n::translate('license.activate.label'), - 'value' => $license ? 'Kirby 3' : I18n::translate('license.unregistered.label'), + 'value' => $license ? $license['type'] : I18n::translate('license.unregistered.label'), 'theme' => $license ? null : 'negative', 'icon' => $license ? 'info' : 'key', 'dialog' => $license ? 'license' : 'registration' diff --git a/panel/src/components/Dialogs/LicenseDialog.vue b/panel/src/components/Dialogs/LicenseDialog.vue new file mode 100644 index 0000000000..9df1704777 --- /dev/null +++ b/panel/src/components/Dialogs/LicenseDialog.vue @@ -0,0 +1,67 @@ + + + diff --git a/panel/src/components/Dialogs/index.js b/panel/src/components/Dialogs/index.js index 093f9da135..e37b7449c5 100644 --- a/panel/src/components/Dialogs/index.js +++ b/panel/src/components/Dialogs/index.js @@ -11,6 +11,7 @@ import FiberDialog from "./FiberDialog.vue"; import FilesDialog from "./FilesDialog.vue"; import FormDialog from "./FormDialog.vue"; import LanguageDialog from "./LanguageDialog.vue"; +import LicenseDialog from "./LicenseDialog.vue"; import LinkDialog from "./LinkDialog.vue"; import ModelsDialog from "./ModelsDialog.vue"; import PageCreateDialog from "./PageCreateDialog.vue"; @@ -35,6 +36,7 @@ export default { app.component("k-fiber-dialog", FiberDialog); app.component("k-files-dialog", FilesDialog); app.component("k-form-dialog", FormDialog); + app.component("k-license-dialog", LicenseDialog); app.component("k-link-dialog", LinkDialog); app.component("k-language-dialog", LanguageDialog); app.component("k-models-dialog", ModelsDialog); diff --git a/src/Cms/System.php b/src/Cms/System.php index e523b0e5c0..5b8d8de050 100644 --- a/src/Cms/System.php +++ b/src/Cms/System.php @@ -267,7 +267,7 @@ public function isOk(): bool * permissions for access.settings, otherwise just a * boolean that tells whether a valid license is active */ - public function license(): string|bool + public function license(): array|bool { if ($this->license !== null) { return $this->license; @@ -286,21 +286,24 @@ public function license(): string|bool $license['date'], $license['email'], $license['domain'], - $license['signature'] + $license['signature'], + $license['type'], + $license['activation'], ) !== true) { return $this->license = false; } // build the license verification data $data = [ - 'license' => $license['license'], - 'order' => $license['order'], - 'email' => hash('sha256', $license['email'] . 'kwAHMLyLPBnHEskzH9pPbJsBxQhKXZnX'), - 'domain' => $license['domain'], - 'date' => $license['date'] + 'license' => $license['license'], + 'order' => $license['order'], + 'email' => hash('sha256', $license['email'] . 'kwAHMLyLPBnHEskzH9pPbJsBxQhKXZnX'), + 'domain' => $license['domain'], + 'date' => $license['date'], + 'type' => $license['type'], + 'activation' => $license['activation'], ]; - // get the public key $pubKey = F::read($this->app->root('kirby') . '/kirby.pub'); @@ -319,10 +322,16 @@ public function license(): string|bool // only return the actual license key if the // current user has appropriate permissions if ($this->app->user()?->isAdmin() === true) { - return $this->license = $license['license']; + return $this->license = $license; } - return $this->license = true; + // redact sensitive data + $license['email'] = null; + $license['order'] = null; + $license['license'] = Str::substr($license['license'], 0, 10) . str_repeat('X', 22); + + // only keep non-sensitive license info + return $this->license = $license; } /** @@ -467,7 +476,7 @@ public function plugins(): Collection */ public function register(string $license = null, string $email = null): bool { - if (Str::startsWith($license, 'K3-PRO-') === false) { + if (Str::startsWith($license, 'K-') === false) { throw new InvalidArgumentException(['key' => 'license.format']); } @@ -476,7 +485,7 @@ public function register(string $license = null, string $email = null): bool } // @codeCoverageIgnoreStart - $response = Remote::get('https://hub.getkirby.com/register', [ + $response = Remote::get('http://hub.getkirby.test/register', [ 'data' => [ 'license' => $license, 'email' => Str::lower(trim($email)), From c3e8f8e2501616a486b6645ba5679ca59e61b757 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Tue, 7 Nov 2023 19:22:58 +0100 Subject: [PATCH 02/59] Make hub address configurable --- src/Cms/System.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Cms/System.php b/src/Cms/System.php index 5b8d8de050..14446fe512 100644 --- a/src/Cms/System.php +++ b/src/Cms/System.php @@ -484,8 +484,11 @@ public function register(string $license = null, string $email = null): bool throw new InvalidArgumentException(['key' => 'license.email']); } + // get the hub address + $hub = option('hub', 'https://hub.getkirby.com'); + // @codeCoverageIgnoreStart - $response = Remote::get('http://hub.getkirby.test/register', [ + $response = Remote::get($hub . '/register', [ 'data' => [ 'license' => $license, 'email' => Str::lower(trim($email)), From d2f900da13ada8d8d21b11a13749983a0ff377d2 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Wed, 8 Nov 2023 18:07:42 +0100 Subject: [PATCH 03/59] New license class and first steps to integrate it --- config/areas/system/dialogs.php | 30 +- config/areas/system/views.php | 10 +- i18n/translations/en.json | 16 + .../src/components/Dialogs/LicenseDialog.vue | 68 ++- panel/src/components/View/Activation.vue | 14 +- panel/src/components/View/Menu.vue | 4 +- src/Cms/License.php | 431 ++++++++++++++++++ src/Cms/System.php | 109 +---- src/Panel/View.php | 2 +- 9 files changed, 541 insertions(+), 143 deletions(-) create mode 100644 src/Cms/License.php diff --git a/config/areas/system/dialogs.php b/config/areas/system/dialogs.php index 9a7b22dab5..c1c338e252 100644 --- a/config/areas/system/dialogs.php +++ b/config/areas/system/dialogs.php @@ -9,7 +9,9 @@ // license key 'license' => [ 'load' => function () { - $license = App::instance()->system()->license(); + $kirby = App::instance(); + $license = $kirby->system()->license(); + $obfuscated = $kirby->user()->isAdmin() === false; if ($license === false) { go(Panel::url('dialogs/registration')); @@ -19,20 +21,18 @@ 'component' => 'k-license-dialog', 'props' => [ 'size' => 'large', - 'license' => $license, - // 'fields' => [ - // 'license' => [ - // 'type' => 'info', - // 'label' => I18n::translate('license'), - // 'text' => $license ? $license['license'] : I18n::translate('license.unregistered.label'), - // 'theme' => $license ? 'code' : 'negative', - // 'help' => $license ? - // // @codeCoverageIgnoreStart - // '' . I18n::translate('license.manage') . ' →' : - // // @codeCoverageIgnoreEnd - // '' . I18n::translate('license.buy') . ' →' - // ] - // ], + 'license' => [ + 'activated' => $license->activated('Y-m-d') ?? $license->purchased('Y-m-d'), + 'code' => $license->code($obfuscated), + 'domain' => $license->domain(), + 'icon' => $license->icon(), + 'info' => $license->info(), + 'purchased' => $license->purchased('Y-m-d'), + 'renewal' => $license->renewal('Y-m-d'), + 'status' => $license->status(), + 'theme' => $license->theme(), + 'type' => $license->type(), + ], 'submitButton' => false, 'cancelButton' => false, ] diff --git a/config/areas/system/views.php b/config/areas/system/views.php index c53ad54061..af64cd938f 100644 --- a/config/areas/system/views.php +++ b/config/areas/system/views.php @@ -14,11 +14,11 @@ $environment = [ [ - 'label' => $license ? I18n::translate('license') : I18n::translate('license.activate.label'), - 'value' => $license ? $license['type'] : I18n::translate('license.unregistered.label'), - 'theme' => $license ? null : 'negative', - 'icon' => $license ? 'info' : 'key', - 'dialog' => $license ? 'license' : 'registration' + 'label' => $license->label(), + 'value' => $license->type(), + 'theme' => $license->theme(), + 'icon' => $license->icon(), + 'dialog' => $license->dialog() ], [ 'label' => $updateStatus?->label() ?? I18n::translate('version'), diff --git a/i18n/translations/en.json b/i18n/translations/en.json index d6300fc2fa..29fbea448a 100644 --- a/i18n/translations/en.json +++ b/i18n/translations/en.json @@ -416,13 +416,29 @@ "license.activate.label": "Please activate your license", "license.activate.domain": "Your license will be activated for {host}.", "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Buy a license", + "license.code": "Code", "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", "license.code.label": "Please enter your license code", + "license.domain": "Domain", + "license.status": "Status", + "license.status.active.info": "Includes free updates until", + "license.status.active.label": "Valid license", + "license.status.invalid.info": "No valid license", + "license.status.invalid.label": "Please activate your license", + "license.status.expired.info": "No longer valid for this version", + "license.status.expired.label": "Expired license", + "license.status.outdated.info": "Free updates are no longer included since", + "license.status.outdated.label": "No morefree updates", "license.manage": "Manage your licenses", + "license.purchased": "Purchased", "license.ready": "Ready to launch your site?", + "license.renew": "Renew", "license.success": "Thank you for supporting Kirby", + "license.type": "Type", "license.unregistered.label": "Unregistered", + "license.updates": "Includes free updates until", "link": "Link", "link.text": "Link text", diff --git a/panel/src/components/Dialogs/LicenseDialog.vue b/panel/src/components/Dialogs/LicenseDialog.vue index 9df1704777..8a70198025 100644 --- a/panel/src/components/Dialogs/LicenseDialog.vue +++ b/panel/src/components/Dialogs/LicenseDialog.vue @@ -2,6 +2,7 @@ @@ -10,41 +11,51 @@ {{ $t("license") }} - Manage license - + />
- + - + - - + + - - + + - + + + + +
Type{{ $t("license.type") }} {{ license.type }}
Key{{ $t("license.code") }} - {{ license.license }} + {{ license.code }}
Purchased{{ license.date }}{{ $t("license.purchased") }}{{ license.purchased }}
Activated{{ license.activation }}{{ $t("license.activated") }}{{ license.activated }}
Domain{{ $t("license.domain") }} {{ license.domain }}
{{ $t("license.status") }} +

+ {{ license.info }} + + + {{ license.renewal }} + +

+
@@ -62,6 +73,37 @@ export const props = { }; export default { - mixins: [props] + mixins: [props], + computed: { + btn() { + if (this.license.status !== "active") { + return { + icon: "refresh", + theme: this.license.theme, + text: this.$t("license.renew") + }; + } + + return { + icon: "edit", + text: this.$t("license.manage") + }; + } + } }; + + diff --git a/panel/src/components/View/Activation.vue b/panel/src/components/View/Activation.vue index 107f725500..125eb86aaf 100644 --- a/panel/src/components/View/Activation.vue +++ b/panel/src/components/View/Activation.vue @@ -1,6 +1,10 @@ + + diff --git a/panel/src/components/View/Activation.vue b/panel/src/components/View/Activation.vue index 42198aa029..bdb49f06b5 100644 --- a/panel/src/components/View/Activation.vue +++ b/panel/src/components/View/Activation.vue @@ -1,18 +1,16 @@