- Accept a single pasted/uploaded combined licence key (base64+gzip of licence+activation key), auto-detected in either paste mode, alongside the legacy two-field format - Warn and block applying a key generated for the wrong protocol before it ever reaches the backend - Decode both the legacy positional features string and a new named features object, so already-issued keys keep working unchanged - Show a live "Key Details" preview (validity, users, site IDs, enabled features) as a key is typed, pasted, or uploaded - Fix cypress test setup broken by the paste-format default change, and an unreliable FileReader-based base64 conversion in the test helpers
262 lines
8.2 KiB
HTML
262 lines
8.2 KiB
HTML
<div *ngIf="action !== 'register'" class="card">
|
|
<div class="card-header">Licencing</div>
|
|
|
|
<div [ngSwitch]="action" class="card-block">
|
|
<div class="card-text">
|
|
<ng-container *ngSwitchCase="'key'">
|
|
<p class="key-error" *ngIf="!keyError">
|
|
<clr-icon
|
|
class="is-error"
|
|
shape="exclamation-circle"
|
|
size="26"
|
|
></clr-icon>
|
|
Licence key is invalid. We can't provide you more details at the
|
|
moment
|
|
</p>
|
|
|
|
<p
|
|
class="key-error"
|
|
*ngIf="keyError"
|
|
[innerHTML]="licenseErrors[keyError]"
|
|
></p>
|
|
|
|
<p *ngIf="errorDetails"><strong>Details:</strong> {{ errorDetails }}</p>
|
|
</ng-container>
|
|
|
|
<ng-container *ngSwitchCase="'limit'">
|
|
<p class="key-error">
|
|
<clr-icon
|
|
class="is-error"
|
|
shape="exclamation-circle"
|
|
size="26"
|
|
></clr-icon>
|
|
The registered number of users reached the limit specified for your
|
|
licence. Please contact
|
|
<contact-link classes="color-green" />
|
|
or your reseller to arrange additional licences for this product.
|
|
</p>
|
|
</ng-container>
|
|
|
|
<ng-container *ngSwitchCase="'update'">
|
|
<p class="key-error">
|
|
Update the license key by uploading the licence file or by pasting a
|
|
license key and activation key in the inputs below.
|
|
</p>
|
|
</ng-container>
|
|
|
|
<p class="m-0 mt-10"><strong>Protocol:</strong> {{ protocol }}</p>
|
|
|
|
<p class="m-0">
|
|
<strong>SYSSITE:</strong>
|
|
<span
|
|
*ngFor="let id of syssite.value; let i = index"
|
|
[class.misskey]="missmatchedKey && missmatchedKey === id"
|
|
>
|
|
{{ id }}{{ i === syssite.value?.length! - 1 ? '' : ',' }}
|
|
</span>
|
|
|
|
<a
|
|
class="tooltip tooltip-md tooltip-top-right"
|
|
(click)="copySyssite(copyIcon, copyTooltip, syssite.value || [])"
|
|
>
|
|
<clr-icon
|
|
#copyIcon
|
|
class="cursor-pointer"
|
|
shape="copy"
|
|
size="15"
|
|
></clr-icon>
|
|
<span #copyTooltip class="tooltip-content">Copy to clipboard</span>
|
|
</a>
|
|
</p>
|
|
|
|
<clr-tabs>
|
|
<clr-tab>
|
|
<button clrTabLink>Upload licence</button>
|
|
<clr-tab-content>
|
|
<input
|
|
#licenceFile
|
|
(change)="onFileCapture($event)"
|
|
type="file"
|
|
hidden
|
|
/>
|
|
<div
|
|
(click)="licenceFile.click()"
|
|
appFileDrop
|
|
(fileDrop)="onFileCapture($event, true)"
|
|
class="drop-area"
|
|
>
|
|
<clr-spinner
|
|
class="spinner-sm"
|
|
*ngIf="licenceFileLoading"
|
|
></clr-spinner>
|
|
<ng-container *ngIf="!licenceFileLoading">
|
|
<div *ngIf="licencefile.filename === ''">
|
|
Drop / Browse licence file
|
|
</div>
|
|
<div *ngIf="licencefile.filename !== ''">
|
|
Selected file: <strong>{{ licencefile.filename }}</strong>
|
|
</div>
|
|
<div *ngIf="licenceFileError">
|
|
<strong>{{ licenceFileError }}</strong>
|
|
</div>
|
|
</ng-container>
|
|
</div>
|
|
</clr-tab-content>
|
|
</clr-tab>
|
|
|
|
<clr-tab>
|
|
<button clrTabLink>Paste licence</button>
|
|
<clr-tab-content>
|
|
<ng-container *ngIf="keyFormat === 'combined'">
|
|
<form class="clr-form combined-key-form">
|
|
<p>Licence key:</p>
|
|
<div class="clr-control-container">
|
|
<textarea
|
|
[(ngModel)]="combinedKeyValue"
|
|
(mouseleave)="onCombinedKeyInput()"
|
|
name="combined-key-area"
|
|
placeholder="Paste licence key here"
|
|
class="clr-textarea"
|
|
></textarea>
|
|
</div>
|
|
</form>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-link p-0"
|
|
(click)="toggleKeyFormat()"
|
|
>
|
|
Paste as two separate keys instead
|
|
</button>
|
|
</ng-container>
|
|
|
|
<ng-container *ngIf="keyFormat === 'legacy'">
|
|
<form class="clr-form license-key-form">
|
|
<p>Licence key:</p>
|
|
<div class="clr-control-container">
|
|
<textarea
|
|
[(ngModel)]="licenceKeyValue"
|
|
(mouseleave)="trimKeys()"
|
|
name="license-key-area"
|
|
placeholder="Paste licence key here"
|
|
class="clr-textarea"
|
|
></textarea>
|
|
</div>
|
|
</form>
|
|
|
|
<form class="clr-form activation-key-form">
|
|
<p>Activation key:</p>
|
|
<div class="clr-control-container">
|
|
<textarea
|
|
[(ngModel)]="activationKeyValue"
|
|
(mouseleave)="trimKeys()"
|
|
name="activation-key-area"
|
|
placeholder="Paste activation key here"
|
|
class="clr-textarea"
|
|
></textarea>
|
|
</div>
|
|
</form>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-link p-0"
|
|
(click)="toggleKeyFormat()"
|
|
>
|
|
Paste as a single key instead
|
|
</button>
|
|
</ng-container>
|
|
</clr-tab-content>
|
|
</clr-tab>
|
|
</clr-tabs>
|
|
|
|
<!--
|
|
licenseKeyData starts as whatever key is currently active (set once
|
|
in ngOnInit), then live-updates to preview a pasted-but-not-yet-
|
|
applied key's own details instead - see refreshKeyPreview().
|
|
-->
|
|
<div *ngIf="licenseKeyData" class="key-details">
|
|
<h6 cds-text="subsection" class="mt-15 mb-10">Key Details</h6>
|
|
|
|
<p class="m-0">
|
|
<strong>Valid until:</strong>
|
|
{{ licenseKeyData.valid_until }}
|
|
<span *ngIf="licenseKeyData.demo">(demo/free tier key)</span>
|
|
</p>
|
|
|
|
<p class="m-0">
|
|
<strong>Allowed users:</strong>
|
|
{{ licenseKeyData.users_allowed }}
|
|
</p>
|
|
|
|
<p class="m-0">
|
|
<strong>Site ID(s) in this key:</strong>
|
|
{{
|
|
(licenseKeyData.site_id_multiple?.length
|
|
? licenseKeyData.site_id_multiple
|
|
: [licenseKeyData.site_id]
|
|
).join(', ')
|
|
}}
|
|
</p>
|
|
|
|
<p *ngIf="enabledFeatures.length" class="m-0">
|
|
<strong>Enabled features:</strong>
|
|
{{ enabledFeatures.join(', ') }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-footer d-flex clr-align-items-center">
|
|
<button
|
|
(click)="applyKeys()"
|
|
class="btn btn-primary apply-keys"
|
|
[clrLoading]="applyingKeys"
|
|
[disabled]="disableApplyButton"
|
|
>
|
|
Apply licence keys
|
|
</button>
|
|
|
|
<button
|
|
*ngIf="isAppFreeTier.value"
|
|
routerLink="/"
|
|
class="btn btn-sm btn-link"
|
|
>
|
|
Continue with free tier
|
|
</button>
|
|
</div>
|
|
|
|
<p
|
|
class="key-error protocol-mismatch-warning"
|
|
*ngIf="protocolMismatch === 'requiresHttps'"
|
|
>
|
|
<clr-icon
|
|
class="color-orange"
|
|
shape="exclamation-triangle"
|
|
size="26"
|
|
></clr-icon>
|
|
This key was generated for a secure (HTTPS) connection, but DataController
|
|
is currently running over HTTP - it will not activate here. Access
|
|
DataController via HTTPS, or contact
|
|
<contact-link classes="color-green" />
|
|
for an HTTP-compatible key.
|
|
</p>
|
|
|
|
<p
|
|
class="key-error protocol-mismatch-warning"
|
|
*ngIf="protocolMismatch === 'requiresHttp'"
|
|
>
|
|
<clr-icon
|
|
class="color-orange"
|
|
shape="exclamation-triangle"
|
|
size="26"
|
|
></clr-icon>
|
|
This key was generated for an insecure (HTTP) connection, but this page is
|
|
running in a secure browsing context (HTTPS, or localhost) - it will not
|
|
activate here. Contact
|
|
<contact-link classes="color-green" />
|
|
for an HTTPS-compatible key.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<app-terms *ngIf="action === 'register'"></app-terms>
|