@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
312 lines • 125 kB
JavaScript
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ApplicationService, ApplicationType, BillingMode, InventoryService, Isolation } from '@c8y/client';
import { AlertService, GainsightService, ModalService, Permissions, ViewContext, gettext } from '@c8y/ngx-components';
import { EcosystemService, PRODUCT_EXPERIENCE_ECOSYSTEM, packageProperties } from '@c8y/ngx-components/ecosystem/shared';
import { TranslateService } from '@ngx-translate/core';
import { BsModalService } from 'ngx-bootstrap/modal';
import { SubscriptionModalComponent } from './subscription-modal/subscription-modal.component';
import { UpdateApplicationModalComponent } from './update-application-modal/update-application-modal.component';
import * as i0 from "@angular/core";
import * as i1 from "@angular/router";
import * as i2 from "@c8y/ngx-components/ecosystem/shared";
import * as i3 from "@angular/forms";
import * as i4 from "@c8y/client";
import * as i5 from "@c8y/ngx-components";
import * as i6 from "@ngx-translate/core";
import * as i7 from "ngx-bootstrap/modal";
import * as i8 from "@angular/common";
import * as i9 from "ngx-bootstrap/tooltip";
import * as i10 from "@c8y/ngx-components/icon-selector";
import * as i11 from "../activity-log/activity-log.component";
const MICROSERVICES_BASE_PATH = '/ecosystem/microservice/microservices';
const APPLICATIONS_BASE_PATH = '/ecosystem/application/applications';
export class ApplicationPropertiesComponent {
constructor(activatedRoute, ecosystemService, router, formBuilder, applicationService, alertService, inventoryService, permissions, modalService, translate, bsModalService, gainsightService) {
this.activatedRoute = activatedRoute;
this.ecosystemService = ecosystemService;
this.router = router;
this.formBuilder = formBuilder;
this.applicationService = applicationService;
this.alertService = alertService;
this.inventoryService = inventoryService;
this.permissions = permissions;
this.modalService = modalService;
this.translate = translate;
this.bsModalService = bsModalService;
this.gainsightService = gainsightService;
this.CURRENT_LOCATION = location.href;
this.singleTenant = false;
this.subscription = false;
this.iconMap = {
HOSTED: 'cloud',
EXTERNAL: 'external-link-square',
MICROSERVICE: 'microchip'
};
this.isLoading = true;
this.hasAdminPermissions = false;
this.noDescriptionLabel = gettext('No description available.');
this.packageProperties = [...packageProperties];
this.isUpdateAvailable = false;
}
async ngOnInit() {
this.hasAdminPermissions = this.permissions.hasRole(Permissions.ROLE_APPLICATION_MANAGEMENT_ADMIN);
await this.refresh();
}
async refresh() {
await this.load();
this.isUnpacked = this.ecosystemService.isUnpacked(this.application);
this.isPackage = this.ecosystemService.isPackage(this.application);
this.isFeature = this.ecosystemService.isFeature(this.application);
this.isExternal = this.ecosystemService.isExternal(this.application);
this.isMicroservice = this.ecosystemService.isMicroservice(this.application);
this.appState = this.ecosystemService.getAppState(this.application);
if (this.isUnpacked) {
await this.resolveSourcePackageDetails();
}
this.setBreadcrumbConfig();
if (this.isCustomMicroservice) {
this.loadBinaryMo();
}
}
async load() {
this.isLoading = true;
this.initForm();
await this.loadApplication();
this.isLoading = false;
}
onApplication(app) {
if (app.manifest) {
this.singleTenant = app.manifest.isolation === Isolation.PER_TENANT;
this.subscription = app.manifest.billingMode === BillingMode.SUBSCRIPTION;
}
}
cancel() {
if (this.application.type === ApplicationType.MICROSERVICE) {
this.router.navigateByUrl(MICROSERVICES_BASE_PATH);
}
else {
this.router.navigateByUrl(APPLICATIONS_BASE_PATH);
}
}
openApp(app) {
this.ecosystemService.openApp(app);
}
getPackage(entityOrId) {
return this.applicationService.detail(entityOrId);
}
async delete() {
try {
await this.ecosystemService.deleteApp(this.application);
if (this.application.type === ApplicationType.MICROSERVICE) {
this.router.navigateByUrl(MICROSERVICES_BASE_PATH);
}
else {
this.router.navigateByUrl(APPLICATIONS_BASE_PATH);
}
}
catch (ex) {
if (ex) {
this.alertService.addServerFailure(ex);
}
}
}
async subscribe() {
const initialState = { application: this.application, isSubscribed: false };
await this.confirmSubscriptionChange(initialState);
this.loadApplication();
}
async unsubscribe() {
const initialState = { application: this.application, isSubscribed: true };
await this.confirmSubscriptionChange(initialState);
this.loadApplication();
}
async loadApplication() {
const { id } = this.activatedRoute.snapshot.parent.data.contextData;
this.application = await this.ecosystemService.getApplication(id);
if (this.application.type === ApplicationType.MICROSERVICE) {
this.formGroup.get('name').disable();
}
const updatedApplication = {
...this.application,
description: this.getDescription(this.application),
icon: this.application.config?.icon?.class || this.application.manifest?.icon?.class
};
this.formGroup.patchValue(updatedApplication);
this.canOpenInBrowser = this.ecosystemService.canOpenAppInBrowser(this.application);
this.disableOpenInBrowser =
this.canOpenInBrowser &&
(await this.ecosystemService.isOverwrittenByCustomApp(this.application));
this.canDelete = await this.ecosystemService.canDeleteApp(this.application);
this.isOwner = this.ecosystemService.isOwner(this.application);
this.isActivityLogSupported = this.isActivityLogSupportedByApp(this.application);
this.isCustomMicroservice = this.ecosystemService.isCustomMicroservice(this.application);
this.isSubscribed = await this.ecosystemService.checkIfSubscribed(this.application);
this.onApplication(this.application);
}
async save(app) {
const icon = this.formGroup.controls['icon'].value;
const name = this.formGroup.controls['name'].value;
if (icon) {
app = {
...app,
config: { ...this.application.config, icon: { class: icon }, appTitle: name }
};
}
else if (this.application.config) {
app = {
...app,
config: { ...this.application.config, appTitle: name }
};
}
app.id = this.application.id;
try {
const { data } = await this.ecosystemService.updateApp(app);
this.formGroup.reset();
this.loadApplication();
if (data) {
this.alertService.success(gettext('Application saved.'));
}
}
catch (ex) {
// do nothing
}
}
onNewArchive() {
this.loadBinaryMo();
}
async updateToLatestVersion() {
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.APPLICATION_PROPERTIES, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.APPLICATION_PROPERTIES,
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.UPDATE_AVAILABLE,
url: this.CURRENT_LOCATION
});
try {
const translatedBody = this.translate.instant(gettext(`You're using the version {{ currentVersion }} of the {{ packageName }} package, the latest version available is {{ latestVersion }}, do you want to update? You can always revert to a previous version in the Activity log panel.`), {
currentVersion: this.application.manifest?.version,
latestVersion: this.blueprintApplicationVersion,
packageName: this.sourcePackage.name
});
await this.modalService.confirm(gettext('Update application'), translatedBody, 'warning', {
ok: gettext('Update'),
cancel: gettext('Cancel')
});
}
catch {
// modal canceled
return;
}
try {
await this.bsModalService.show(UpdateApplicationModalComponent, {
ariaDescribedby: 'modal-body',
ariaLabelledBy: 'modal-title',
class: 'modal-sm',
ignoreBackdropClick: true,
initialState: {
sourcePackage: this.sourcePackage,
application: this.application
}
}).content.result;
}
catch {
// modal canceled
return;
}
await this.refresh();
this.alertService.success(gettext('Application updated.'));
}
getDescription(application) {
if (!application) {
return;
}
return application.description || application.manifest?.description;
}
async confirmSubscriptionChange(initialState) {
await this.bsModalService.show(SubscriptionModalComponent, {
class: 'modal-sm',
ariaDescribedby: 'modal-body',
ariaLabelledBy: 'modal-title',
initialState,
ignoreBackdropClick: true
}).content.result;
}
async resolveSourcePackageDetails() {
try {
this.sourcePackage = (await this.getPackage(this.application.manifest?.source)).data;
if (this.sourcePackage) {
this.packageProperties.push({
label: gettext('Source package'),
value: this.sourcePackage.name,
type: 'link',
action: () => this.router.navigateByUrl(ViewContext.Extension.replace(':id', this.sourcePackage.id.toString()))
});
}
}
catch {
this.alertService.warning(gettext('Unable to resolve source package.'));
return;
}
await this.extractVersionInformation(this.application);
}
initForm() {
this.formGroup = this.formBuilder.group({
id: [{ value: '' }],
name: [Validators.required, Validators.maxLength(120)],
key: [Validators.required, Validators.maxLength(120)],
contextPath: [Validators.required, Validators.maxLength(120)],
description: ['', Validators.maxLength(200)],
username: [Validators.required],
password: [Validators.required],
externalUrl: [Validators.required],
icon: [undefined, [Validators.minLength(1)]]
});
if (!this.hasAdminPermissions) {
this.formGroup.disable();
}
}
isActivityLogSupportedByApp(app) {
return (this.ecosystemService.isOwner(app) &&
app.type !== ApplicationType.MICROSERVICE &&
app.type !== ApplicationType.EXTERNAL);
}
setBreadcrumbConfig() {
this.breadcrumbConfig = {
icon: this.isMicroservice ? 'microchip' : this.isFeature ? 'tab' : 'c8y-modules',
label: this.isMicroservice
? gettext('Microservices')
: this.isFeature
? gettext('Features')
: gettext('Applications'),
path: this.isMicroservice
? '/ecosystem/microservice/microservices'
: this.isFeature
? 'ecosystem/application/features'
: 'ecosystem/application/applications'
};
}
async loadBinaryMo() {
this.binaryMo = (await this.inventoryService.detail(this.application.activeVersionId)).data;
}
async extractVersionInformation(application) {
if (!application.manifest.isPackage && !application.manifest.source) {
return;
}
const blueprintApplicationId = application.manifest?.source;
const currentVersion = application.manifest?.version;
try {
const { data: blueprintApplicationVersions } = await this.applicationService.listVersions(blueprintApplicationId);
const blueprintLatestVersion = this.ecosystemService.getApplicationVersionObjectByTag(blueprintApplicationVersions, 'latest');
this.blueprintApplicationVersion = blueprintLatestVersion.version;
this.isUpdateAvailable = this.ecosystemService.shouldUpgradePackage(currentVersion, blueprintLatestVersion);
}
catch {
this.alertService.warning(gettext('Unable to resolve versions of source package.'));
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ApplicationPropertiesComponent, deps: [{ token: i1.ActivatedRoute }, { token: i2.EcosystemService }, { token: i1.Router }, { token: i3.FormBuilder }, { token: i4.ApplicationService }, { token: i5.AlertService }, { token: i4.InventoryService }, { token: i5.Permissions }, { token: i5.ModalService }, { token: i6.TranslateService }, { token: i7.BsModalService }, { token: i5.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ApplicationPropertiesComponent, selector: "c8y-application-properties", ngImport: i0, template: "<c8y-title>{{ application | humanizeAppName | async }}</c8y-title>\n\n<c8y-breadcrumb *ngIf=\"!isMicroservice\">\n <c8y-breadcrumb-item\n [icon]=\"'c8y-atom'\"\n [label]=\"'Ecosystem' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-modules'\"\n [label]=\"'Applications' | translate\"\n [path]=\"'ecosystem/application/applications'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"breadcrumbConfig?.icon\"\n *ngIf=\"isFeature\"\n [label]=\"breadcrumbConfig?.label\"\n [path]=\"breadcrumbConfig?.path\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"application | humanizeAppName | async\"></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"'Properties' | translate\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-breadcrumb *ngIf=\"isMicroservice\">\n <c8y-breadcrumb-item\n [icon]=\"'c8y-atom'\"\n [label]=\"'Ecosystem' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"breadcrumbConfig?.icon\"\n [label]=\"breadcrumbConfig?.label\"\n [path]=\"breadcrumbConfig?.path\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"application | humanizeAppName | async\"></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"'Properties' | translate\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<div class=\"row\">\n <div [ngClass]=\"{ 'col-md-8': !isActivityLogSupported, 'col-md-12': isActivityLogSupported }\">\n <div\n class=\"card content-fullpage\"\n *ngIf=\"application\"\n [ngClass]=\"{ 'd-grid grid__col--7-5--md': isActivityLogSupported }\"\n >\n <form\n class=\"d-flex d-col content-fullpage\"\n (ngSubmit)=\"formGroup.valid && save(formGroup.value)\"\n [formGroup]=\"formGroup\"\n novalidate\n >\n <div\n class=\"d-contents\"\n *ngIf=\"!isLoading\"\n >\n <div class=\"card-block separator-bottom large-padding flex-no-shrink\">\n <div class=\"d-flex-md a-i-start text-center text-left-md\">\n <c8y-app-icon\n class=\"icon-48\"\n *ngIf=\"!isPackage && !isFeature && !isMicroservice && !isExternal\"\n [app]=\"application\"\n [contextPath]=\"application.contextPath\"\n [name]=\"application.name\"\n ></c8y-app-icon>\n <i\n class=\"icon-48\"\n c8yIcon=\"big-parcel\"\n *ngIf=\"isPackage\"\n ></i>\n <i\n class=\"icon-48\"\n c8yIcon=\"tab\"\n *ngIf=\"isFeature\"\n ></i>\n <i\n class=\"icon-48\"\n c8yIcon=\"microchip\"\n *ngIf=\"isMicroservice\"\n ></i>\n <i\n class=\"icon-48\"\n c8yIcon=\"globe1\"\n *ngIf=\"isExternal\"\n ></i>\n\n <div class=\"p-t-md-16 p-l-md-16 p-r-md-32 flex-grow\">\n <p class=\"h4 text-medium m-b-8\">{{ application | humanizeAppName | async }}</p>\n <p *ngIf=\"!isOwner\">\n <em class=\"text-muted\">\n {{\n formGroup?.controls?.description?.value || (noDescriptionLabel | translate)\n }}\n </em>\n </p>\n <div\n class=\"form-group m-b-0\"\n *ngIf=\"isOwner\"\n >\n <label\n class=\"editable\"\n [ngClass]=\"{ updated: formGroup?.controls?.description?.dirty }\"\n >\n <textarea\n class=\"form-control no-resize\"\n placeholder=\"{{ noDescriptionLabel | translate }}\"\n name=\"description\"\n c8y-textarea-autoresize\n formControlName=\"description\"\n ></textarea>\n </label>\n </div>\n </div>\n <div class=\"text-right-md m-t-4\">\n <span\n class=\"label\"\n [ngClass]=\"appState?.class\"\n >\n {{ appState?.label | translate }}\n </span>\n <div\n class=\"fit-w m-t-2\"\n *ngIf=\"application.manifest?.version\"\n data-cy=\"application-detail--version\"\n >\n <label\n class=\"text-label-small\"\n translate\n >\n Version:\n </label>\n <small class=\"p-l-4 text-bold\">{{ application.manifest?.version }}</small>\n </div>\n <div\n class=\"fit-w m-t-2\"\n *ngIf=\"!isUnpacked\"\n >\n <label\n class=\"text-label-small\"\n translate\n >\n Creation time:\n </label>\n <small class=\"p-l-4 text-bold\">\n {{ (binaryMo?.creationTime | c8yDate) || '---' }}\n </small>\n </div>\n <div class=\"m-t-8\">\n <button\n class=\"btn btn-default btn-sm\"\n [attr.aria-label]=\"\n 'There\\'s a newer version available, click to update' | translate\n \"\n tooltip=\"{{\n 'There\\'s a newer version available, click to update' | translate\n }}\"\n placement=\"top\"\n type=\"button\"\n *ngIf=\"isUpdateAvailable\"\n (click)=\"updateToLatestVersion()\"\n [delay]=\"300\"\n >\n <i [c8yIcon]=\"'installing-updates'\"></i>\n {{ 'Update available' | translate }}\n </button>\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Open' | translate }}\"\n type=\"button\"\n (click)=\"openApp(application)\"\n [disabled]=\"disableOpenInBrowser\"\n *ngIf=\"canOpenInBrowser\"\n >\n <i [c8yIcon]=\"'external-link'\"></i>\n {{ 'Open' | translate }}\n </button>\n <div *ngIf=\"canOpenInBrowser && disableOpenInBrowser\">\n <small\n class=\"text-muted\"\n translate\n >\n The application is overwritten by a custom application sharing the same path\n </small>\n </div>\n <span *ngIf=\"isCustomMicroservice\">\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Subscribe' | translate }}\"\n type=\"button\"\n (click)=\"subscribe()\"\n *ngIf=\"!isSubscribed\"\n >\n <i [c8yIcon]=\"'check-circle-o'\"></i>\n {{ 'Subscribe' | translate }}\n </button>\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Unsubscribe' | translate }}\"\n type=\"button\"\n (click)=\"unsubscribe()\"\n *ngIf=\"isSubscribed\"\n >\n <i [c8yIcon]=\"'minus-circle'\"></i>\n {{ 'Unsubscribe' | translate }}\n </button>\n </span>\n </div>\n </div>\n </div>\n </div>\n <div class=\"inner-scroll bg-level-0 flex-grow\">\n <div class=\"card-block large-padding\">\n <div\n class=\"row p-16\"\n *ngIf=\"isPackage\"\n >\n <c8y-properties-list\n icon=\"info\"\n [title]=\"'Package details' | translate\"\n [data]=\"application.manifest\"\n [properties]=\"packageProperties\"\n [emptyLabel]=\"'---'\"\n ></c8y-properties-list>\n </div>\n <div\n class=\"row p-16\"\n *ngIf=\"sourcePackage\"\n >\n <c8y-properties-list\n icon=\"info\"\n [title]=\"'Source package information' | translate\"\n [data]=\"sourcePackage.manifest\"\n [properties]=\"packageProperties\"\n [emptyLabel]=\"'---'\"\n ></c8y-properties-list>\n </div>\n <div class=\"row\">\n <div class=\"col-sm-5\">\n <c8y-form-group>\n <label for=\"appId\">ID</label>\n <input\n class=\"form-control\"\n id=\"appId\"\n name=\"id\"\n type=\"text\"\n autocomplete=\"off\"\n [readonly]=\"true\"\n formControlName=\"id\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-7\">\n <c8y-form-group>\n <label>{{ 'Name' | translate }}</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. My application' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n [readonly]=\"!isOwner\"\n formControlName=\"name\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <div class=\"row\">\n <div class=\"col-sm-5\">\n <c8y-form-group>\n <label>{{ 'Application key' | translate }}</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. my-application-key' | translate }}\"\n name=\"key\"\n type=\"text\"\n required\n [readonly]=\"application.id || !isOwner\"\n formControlName=\"key\"\n />\n </c8y-form-group>\n </div>\n\n <div class=\"col-sm-7\" data-cy=\"application-detail--type\">\n <c8y-form-group>\n <label>{{ 'Type' | translate }}</label>\n <div>\n <div *ngIf=\"application.id\">\n <p class=\"form-control-static\">\n <i [c8yIcon]=\"iconMap[application.type]\"></i>\n <span>\n {{ application.type | translate }}\n </span>\n </p>\n </div>\n </div>\n </c8y-form-group>\n </div>\n </div>\n\n <div [ngSwitch]=\"application.type\">\n <div *ngSwitchCase=\"'HOSTED'\">\n <c8y-form-group>\n <label>{{ 'Path' | translate }}</label>\n <div class=\"input-group\">\n <span class=\"input-group-addon\">/apps/</span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. my-application`used in URL`' | translate }}\"\n name=\"contextPath\"\n type=\"text\"\n required\n [readOnly]=\"application.id || !isOwner\"\n formControlName=\"contextPath\"\n />\n </div>\n </c8y-form-group>\n </div>\n\n <div *ngSwitchCase=\"'MICROSERVICE'\">\n <c8y-form-group>\n <label>{{ 'Path' | translate }}</label>\n <div class=\"input-group\">\n <span class=\"input-group-addon\">/service/</span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. my-microservice`used in URL`' | translate }}\"\n name=\"contextPath\"\n type=\"text\"\n required\n [readOnly]=\"application.id || !isOwner\"\n formControlName=\"contextPath\"\n />\n </div>\n </c8y-form-group>\n <div class=\"row\">\n <div\n class=\"col-sm-4 m-b-16 flex-auto\"\n *ngIf=\"application.manifest.version\"\n data-cy=\"application-detail--version\"\n >\n <label>{{ 'Version' | translate }}</label>\n <p class=\"form-control-static\">\n {{ application.manifest.version }}\n </p>\n </div>\n <div\n class=\"col-sm-4 m-b-16 flex-auto\"\n *ngIf=\"application.manifest.isolation\"\n data-cy=\"application-detail--isolation\"\n >\n <label>{{ 'Isolation' | translate }}</label>\n <p class=\"form-control-static\">\n <span *ngIf=\"singleTenant\">\n <i\n class=\"c8y-icon-duocolor h4\"\n [c8yIcon]=\"'c8y-enterprise'\"\n ></i>\n {{ 'Single tenant' | translate }}\n </span>\n <span *ngIf=\"!singleTenant\">\n <i\n class=\"c8y-icon-duocolor icon-32\"\n [c8yIcon]=\"'c8y-sub-tenants'\"\n ></i>\n {{ 'Multi tenant' | translate }}\n </span>\n </p>\n </div>\n <div\n class=\"col-sm-4 m-b-16 flex-auto\"\n *ngIf=\"application.manifest.isolation\"\n data-cy=\"application-detail--billing-mode\"\n >\n <label>{{ 'Billing mode' | translate }}</label>\n <p class=\"form-control-static\">\n <span\n [tooltip]=\"'Resources usage assigned to: Owner' | translate\"\n *ngIf=\"subscription\"\n >\n {{ 'Subscription' | translate }}\n </span>\n <span\n [tooltip]=\"'Resources usage assigned to: Subscriber | translate'\"\n *ngIf=\"!subscription && singleTenant\"\n >\n {{ 'Resources' | translate }}\n </span>\n <span\n [tooltip]=\"'Resources usage assigned to: Owner' | translate\"\n *ngIf=\"!subscription && !singleTenant\"\n >\n {{ 'Resources' | translate }}\n </span>\n </p>\n </div>\n </div>\n\n <div\n class=\"legend form-block m-t-40\"\n *ngIf=\"application.manifest.provider\"\n >\n {{ 'Provider' | translate }}\n </div>\n <div\n class=\"list-inline\"\n *ngIf=\"application.manifest.provider\"\n data-cy=\"application-detail--provider\"\n >\n <div *ngIf=\"application.manifest.provider.name\">\n <div class=\"col-sm-4 m-b-16\">\n <label>{{ 'Name' | translate }}</label>\n <p class=\"form-control-static\">\n {{ application.manifest.provider.name }}\n </p>\n </div>\n </div>\n <div *ngIf=\"application.manifest.provider.domain\">\n <div class=\"col-sm-4 m-b-16\">\n <label>{{ 'Domain' | translate }}</label>\n <p class=\"form-control-static\">\n {{ application.manifest.provider.domain }}\n </p>\n </div>\n </div>\n <div *ngIf=\"application.manifest.provider.support\">\n <div class=\"col-sm-4 m-b-16\">\n <label>{{ 'Support' | translate }}</label>\n <p class=\"form-control-static\">\n {{ application.manifest.provider.support }}\n </p>\n </div>\n </div>\n </div>\n </div>\n\n <div *ngSwitchCase=\"'EXTERNAL'\">\n <c8y-form-group>\n <label>{{ 'External URL' | translate }}</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://www.example.com/\"\n name=\"externalUrl\"\n type=\"url\"\n required\n [pattern]=\"'^(?!javascript:).+'\"\n [readOnly]=\"!isOwner\"\n formControlName=\"externalUrl\"\n />\n <c8y-messages>\n <c8y-message\n [name]=\"'pattern'\"\n [text]=\"'Valid URL required.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div *ngIf=\"isOwner && !isCustomMicroservice\">\n <label>{{ 'Select icon' | translate }}</label>\n <c8y-icon-selector-wrapper\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </div>\n </div>\n </div>\n </div>\n <ng-container *ngIf=\"isCustomMicroservice\">\n <div\n class=\"d-contents\"\n *ngIf=\"!isLoading\"\n >\n <c8y-upload-archive\n [(application)]=\"application\"\n (refresh)=\"onNewArchive()\"\n ></c8y-upload-archive>\n </div>\n </ng-container>\n <div\n class=\"card-footer separator\"\n *ngIf=\"application && !!isOwner && hasAdminPermissions\"\n >\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-danger\"\n title=\"{{ 'Delete' | translate }}\"\n type=\"button\"\n (click)=\"delete()\"\n *ngIf=\"canDelete\"\n >\n {{ 'Delete' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-form\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"!application.type || formGroup.invalid || formGroup.pristine\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </form>\n\n <div\n class=\"content-fullpage d-flex d-col bg-level-1\"\n *ngIf=\"isActivityLogSupported\"\n >\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Activity log\n </div>\n <div class=\"m-l-auto\">\n <button\n class=\"btn btn-link btn-sm\"\n title=\"{{ 'Reload' | translate }}\"\n type=\"button\"\n (click)=\"load()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading }\"\n ></i>\n {{ 'Reload' | translate }}\n </button>\n </div>\n </div>\n <div\n class=\"p-16 text-center\"\n *ngIf=\"isLoading\"\n >\n <c8y-loading></c8y-loading>\n </div>\n <c8y-activity-log\n class=\"d-contents\"\n *ngIf=\"!isLoading\"\n [hasAdminPermissions]=\"hasAdminPermissions\"\n [application]=\"application\"\n ></c8y-activity-log>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i5.AppIconComponent, selector: "c8y-app-icon", inputs: ["contextPath", "name", "app"] }, { kind: "component", type: i5.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i5.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "directive", type: i5.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i5.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i8.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i8.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i8.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i8.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: i5.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: i5.TextareaAutoresizeDirective, selector: "[c8y-textarea-autoresize]" }, { kind: "component", type: i5.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "component", type: i5.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i5.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i5.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i5.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i5.PropertiesListComponent, selector: "c8y-properties-list", inputs: ["properties", "title", "icon", "data", "groups", "noParse", "emptyLabel"] }, { kind: "directive", type: i9.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "component", type: i2.UploadArchiveComponent, selector: "c8y-upload-archive", inputs: ["application", "uploadNewVersion", "preUploadCallback"], outputs: ["applicationChange", "refresh"] }, { kind: "component", type: i10.IconSelectorWrapperComponent, selector: "c8y-icon-selector-wrapper", inputs: ["canRemoveIcon", "selectedIcon", "iconSize"], outputs: ["onSelect"] }, { kind: "component", type: i11.ActivityLogComponent, selector: "c8y-activity-log", inputs: ["application", "hasAdminPermissions"] }, { kind: "pipe", type: i5.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i8.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.HumanizeAppNamePipe, name: "humanizeAppName" }, { kind: "pipe", type: i5.DatePipe, name: "c8yDate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ApplicationPropertiesComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-application-properties', template: "<c8y-title>{{ application | humanizeAppName | async }}</c8y-title>\n\n<c8y-breadcrumb *ngIf=\"!isMicroservice\">\n <c8y-breadcrumb-item\n [icon]=\"'c8y-atom'\"\n [label]=\"'Ecosystem' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-modules'\"\n [label]=\"'Applications' | translate\"\n [path]=\"'ecosystem/application/applications'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"breadcrumbConfig?.icon\"\n *ngIf=\"isFeature\"\n [label]=\"breadcrumbConfig?.label\"\n [path]=\"breadcrumbConfig?.path\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"application | humanizeAppName | async\"></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"'Properties' | translate\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-breadcrumb *ngIf=\"isMicroservice\">\n <c8y-breadcrumb-item\n [icon]=\"'c8y-atom'\"\n [label]=\"'Ecosystem' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"breadcrumbConfig?.icon\"\n [label]=\"breadcrumbConfig?.label\"\n [path]=\"breadcrumbConfig?.path\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"application | humanizeAppName | async\"></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"'Properties' | translate\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<div class=\"row\">\n <div [ngClass]=\"{ 'col-md-8': !isActivityLogSupported, 'col-md-12': isActivityLogSupported }\">\n <div\n class=\"card content-fullpage\"\n *ngIf=\"application\"\n [ngClass]=\"{ 'd-grid grid__col--7-5--md': isActivityLogSupported }\"\n >\n <form\n class=\"d-flex d-col content-fullpage\"\n (ngSubmit)=\"formGroup.valid && save(formGroup.value)\"\n [formGroup]=\"formGroup\"\n novalidate\n >\n <div\n class=\"d-contents\"\n *ngIf=\"!isLoading\"\n >\n <div class=\"card-block separator-bottom large-padding flex-no-shrink\">\n <div class=\"d-flex-md a-i-start text-center text-left-md\">\n <c8y-app-icon\n class=\"icon-48\"\n *ngIf=\"!isPackage && !isFeature && !isMicroservice && !isExternal\"\n [app]=\"application\"\n [contextPath]=\"application.contextPath\"\n [name]=\"application.name\"\n ></c8y-app-icon>\n <i\n class=\"icon-48\"\n c8yIcon=\"big-parcel\"\n *ngIf=\"isPackage\"\n ></i>\n <i\n class=\"icon-48\"\n c8yIcon=\"tab\"\n *ngIf=\"isFeature\"\n ></i>\n <i\n class=\"icon-48\"\n c8yIcon=\"microchip\"\n *ngIf=\"isMicroservice\"\n ></i>\n <i\n class=\"icon-48\"\n c8yIcon=\"globe1\"\n *ngIf=\"isExternal\"\n ></i>\n\n <div class=\"p-t-md-16 p-l-md-16 p-r-md-32 flex-grow\">\n <p class=\"h4 text-medium m-b-8\">{{ application | humanizeAppName | async }}</p>\n <p *ngIf=\"!isOwner\">\n <em class=\"text-muted\">\n {{\n formGroup?.controls?.description?.value || (noDescriptionLabel | translate)\n }}\n </em>\n </p>\n <div\n class=\"form-group m-b-0\"\n *ngIf=\"isOwner\"\n >\n <label\n class=\"editable\"\n [ngClass]=\"{ updated: formGroup?.controls?.description?.dirty }\"\n >\n <textarea\n class=\"form-control no-resize\"\n placeholder=\"{{ noDescriptionLabel | translate }}\"\n name=\"description\"\n c8y-textarea-autoresize\n formControlName=\"description\"\n ></textarea>\n </label>\n </div>\n </div>\n <div class=\"text-right-md m-t-4\">\n <span\n class=\"label\"\n [ngClass]=\"appState?.class\"\n >\n {{ appState?.label | translate }}\n </span>\n <div\n class=\"fit-w m-t-2\"\n *ngIf=\"application.manifest?.version\"\n data-cy=\"application-detail--version\"\n >\n <label\n class=\"text-label-small\"\n translate\n >\n Version:\n </label>\n <small class=\"p-l-4 text-bold\">{{ application.manifest?.version }}</small>\n </div>\n <div\n class=\"fit-w m-t-2\"\n *ngIf=\"!isUnpacked\"\n >\n <label\n class=\"text-label-small\"\n translate\n >\n Creation time:\n </label>\n <small class=\"p-l-4 text-bold\">\n {{ (binaryMo?.creationTime | c8yDate) || '---' }}\n </small>\n </div>\n <div class=\"m-t-8\">\n <button\n class=\"btn btn-default btn-sm\"\n [attr.aria-label]=\"\n 'There\\'s a newer version available, click to update' | translate\n \"\n tooltip=\"{{\n 'There\\'s a newer version available, click to update' | translate\n }}\"\n placement=\"top\"\n type=\"button\"\n *ngIf=\"isUpdateAvailable\"\n (click)=\"updateToLatestVersion()\"\n [delay]=\"300\"\n >\n <i [c8yIcon]=\"'installing-updates'\"></i>\n {{ 'Update available' | translate }}\n </button>\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Open' | translate }}\"\n type=\"button\"\n (click)=\"openApp(application)\"\n [disabled]=\"disableOpenInBrowser\"\n *ngIf=\"canOpenInBrowser\"\n >\n <i [c8yIcon]=\"'external-link'\"></i>\n {{ 'Open' | translate }}\n </button>\n <div *ngIf=\"canOpenInBrowser && disableOpenInBrowser\">\n <small\n class=\"text-muted\"\n translate\n >\n The application is overwritten by a custom application sharing the same path\n </small>\n </div>\n <span *ngIf=\"isCustomMicroservice\">\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Subscribe' | translate }}\"\n type=\"button\"\n (click)=\"subscribe()\"\n *ngIf=\"!isSubscribed\"\n >\n <i [c8yIcon]=\"'check-circle-o'\"></i>\n {{ 'Subscribe' | translate }}\n </button>\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Unsubscribe' | translate }}\"\n type=\"button\"\n (click)=\"unsubscribe()\"\n *ngIf=\"isSubscribed\"\n >\n <i [c8yIcon]=\"'minus-circle'\"></i>\n {{ 'Unsubscribe' | translate }}\n </button>\n </span>\n </div>\n </div>\n </div>\n </div>\n <div class=\"inner-scroll bg-level-0 flex-grow\">\n <div class=\"card-block large-padding\">\n <div\n class=\"row p-16\"\n *ngIf=\"isPackage\"\n >\n <c8y-properties-list\n icon=\"info\"\n [title]=\"'Package details' | translate\"\n [data]=\"application.manifest\"\n [properties]=\"packageProperties\"\n [emptyLabel]=\"'---'\"\n ></c8y-properties-list>\n </div>\n <div\n class=\"row p-16\"\n *ngIf=\"sourcePackage\"\n >\n <c8y-properties-list\n icon=\"info\"\n [title]=\"'Source package information' | translate\"\n [data]=\"sourcePackage.manifest\"\n [properties]=\"packageProperties\"\n [emptyLabel]=\"'---'\"\n