Merge pull request #4293 from vvlladd28/feature/resources/add-new-type
[3.3] UI: Added manager resources; added new entity type TB_RESOURCE
This commit is contained in:
commit
bfc03c4c69
81
ui-ngx/src/app/core/http/resource.service.ts
Normal file
81
ui-ngx/src/app/core/http/resource.service.ts
Normal file
@ -0,0 +1,81 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { PageLink } from '@shared/models/page/page-link';
|
||||
import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PageData } from '@shared/models/page/page-data';
|
||||
import { Resource, ResourceInfo } from '@shared/models/resource.models';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ResourceService {
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
public getResources(pageLink: PageLink, config?: RequestConfig): Observable<PageData<ResourceInfo>> {
|
||||
return this.http.get<PageData<ResourceInfo>>(`/api/resource${pageLink.toQuery()}`,
|
||||
defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
public getResource(resourceId: string, config?: RequestConfig): Observable<Resource> {
|
||||
return this.http.get<Resource>(`/api/resource/${resourceId}`, defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
public downloadResource(resourceId: string): Observable<any> {
|
||||
return this.http.get(`/api/resource/${resourceId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe(
|
||||
map((response) => {
|
||||
const headers = response.headers;
|
||||
const filename = headers.get('x-filename');
|
||||
const contentType = headers.get('content-type');
|
||||
const linkElement = document.createElement('a');
|
||||
try {
|
||||
const blob = new Blob([response.body], { type: contentType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
linkElement.setAttribute('href', url);
|
||||
linkElement.setAttribute('download', filename);
|
||||
const clickEvent = new MouseEvent('click',
|
||||
{
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: false
|
||||
}
|
||||
);
|
||||
linkElement.dispatchEvent(clickEvent);
|
||||
return null;
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public saveResource(resource: Resource, config?: RequestConfig): Observable<Resource> {
|
||||
return this.http.post<Resource>('/api/resource', resource, defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
public deleteResource(resourceId: string, config?: RequestConfig) {
|
||||
return this.http.delete(`/api/resource/${resourceId}`, defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
}
|
||||
@ -103,6 +103,13 @@ export class MenuService {
|
||||
path: '/widgets-bundles',
|
||||
icon: 'now_widgets'
|
||||
},
|
||||
{
|
||||
id: guid(),
|
||||
name: 'resource.resources-library',
|
||||
type: 'link',
|
||||
path: '/resources-library',
|
||||
icon: 'folder'
|
||||
},
|
||||
{
|
||||
id: guid(),
|
||||
name: 'admin.system-settings',
|
||||
@ -181,6 +188,16 @@ export class MenuService {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'resource.management',
|
||||
places: [
|
||||
{
|
||||
name: 'resource.resources-library',
|
||||
icon: 'folder',
|
||||
path: '/resources-library'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'admin.system-settings',
|
||||
places: [
|
||||
@ -283,6 +300,13 @@ export class MenuService {
|
||||
path: '/dashboards',
|
||||
icon: 'dashboards'
|
||||
},
|
||||
{
|
||||
id: guid(),
|
||||
name: 'resource.resources-library',
|
||||
type: 'link',
|
||||
path: '/resources-library',
|
||||
icon: 'folder'
|
||||
},
|
||||
{
|
||||
id: guid(),
|
||||
name: 'admin.home-settings',
|
||||
@ -368,6 +392,16 @@ export class MenuService {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'resource.management',
|
||||
places: [
|
||||
{
|
||||
name: 'resource.resources-library',
|
||||
icon: 'folder',
|
||||
path: '/resources-library'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'dashboard.management',
|
||||
places: [
|
||||
|
||||
@ -34,6 +34,7 @@ import { MODULES_MAP } from '@shared/public-api';
|
||||
import { modulesMap } from '../../common/modules-map';
|
||||
import { DeviceProfileModule } from './device-profile/device-profile.module';
|
||||
import { ApiUsageModule } from '@home/pages/api-usage/api-usage.module';
|
||||
import { ResourceModule } from '@home/pages/resource/resource.module';
|
||||
|
||||
@NgModule({
|
||||
exports: [
|
||||
@ -52,6 +53,7 @@ import { ApiUsageModule } from '@home/pages/api-usage/api-usage.module';
|
||||
DashboardModule,
|
||||
AuditLogModule,
|
||||
ApiUsageModule,
|
||||
ResourceModule,
|
||||
UserModule
|
||||
],
|
||||
providers: [
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { EntitiesTableComponent } from '@home/components/entity/entities-table.component';
|
||||
import { Authority } from '@shared/models/authority.enum';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ResourcesLibraryTableConfigResolver } from './resources-library-table-config.resolve';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'resources-library',
|
||||
component: EntitiesTableComponent,
|
||||
data: {
|
||||
auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN],
|
||||
title: 'resource.resources-library',
|
||||
breadcrumb: {
|
||||
label: 'resource.resources-library',
|
||||
icon: 'folder'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
entitiesTableConfig: ResourcesLibraryTableConfigResolver
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
providers: [
|
||||
ResourcesLibraryTableConfigResolver
|
||||
]
|
||||
})
|
||||
export class ResourcesLibraryRoutingModule{ }
|
||||
@ -0,0 +1,33 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ResourcesLibraryRoutingModule } from '@home/pages/resource/resource-routing.module';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { HomeComponentsModule } from '@home/components/home-components.module';
|
||||
import { ResourcesLibraryComponent } from './resources-library.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ResourcesLibraryComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
HomeComponentsModule,
|
||||
ResourcesLibraryRoutingModule
|
||||
]
|
||||
})
|
||||
export class ResourceModule { }
|
||||
@ -0,0 +1,115 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
checkBoxCell,
|
||||
DateEntityTableColumn,
|
||||
EntityTableColumn,
|
||||
EntityTableConfig
|
||||
} from '@home/models/entity/entities-table-config.models';
|
||||
import { Resolve } from '@angular/router';
|
||||
import { Resource, ResourceInfo, ResourceTypeTranslationMap } from '@shared/models/resource.models';
|
||||
import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models';
|
||||
import { Direction } from '@shared/models/page/sort-order';
|
||||
import { NULL_UUID } from '@shared/models/id/has-uuid';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ResourceService } from '@core/http/resource.service';
|
||||
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { Authority } from '@shared/models/authority.enum';
|
||||
import { ResourcesLibraryComponent } from '@home/pages/resource/resources-library.component';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { PageData } from '@shared/models/page/page-data';
|
||||
|
||||
@Injectable()
|
||||
export class ResourcesLibraryTableConfigResolver implements Resolve<EntityTableConfig<Resource>> {
|
||||
|
||||
private readonly config: EntityTableConfig<Resource> = new EntityTableConfig<Resource>();
|
||||
private readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
private resourceService: ResourceService,
|
||||
private translate: TranslateService,
|
||||
private datePipe: DatePipe) {
|
||||
|
||||
this.config.entityType = EntityType.TB_RESOURCE;
|
||||
this.config.entityComponent = ResourcesLibraryComponent;
|
||||
this.config.entityTranslations = entityTypeTranslations.get(EntityType.TB_RESOURCE);
|
||||
this.config.entityResources = entityTypeResources.get(EntityType.TB_RESOURCE);
|
||||
this.config.defaultSortOrder = {property: 'title', direction: Direction.ASC};
|
||||
|
||||
this.config.entityTitle = (resource) => resource ?
|
||||
resource.title : '';
|
||||
|
||||
this.config.columns.push(
|
||||
new DateEntityTableColumn<ResourceInfo>('createdTime', 'common.created-time', this.datePipe, '150px'),
|
||||
new EntityTableColumn<ResourceInfo>('title', 'widgets-bundle.title', '60%'),
|
||||
new EntityTableColumn<ResourceInfo>('resourceType', 'resource.resource-type', '40%',
|
||||
entity => this.resourceTypesTranslationMap.get(entity.resourceType)),
|
||||
new EntityTableColumn<ResourceInfo>('tenantId', 'widgets-bundle.system', '60px',
|
||||
entity => {
|
||||
return checkBoxCell(entity.tenantId.id === NULL_UUID);
|
||||
}),
|
||||
);
|
||||
|
||||
this.config.cellActionDescriptors.push(
|
||||
{
|
||||
name: this.translate.instant('resource.export'),
|
||||
icon: 'file_download',
|
||||
isEnabled: () => true,
|
||||
onAction: ($event, entity) => this.exportResource($event, entity)
|
||||
}
|
||||
);
|
||||
|
||||
this.config.deleteEntityTitle = resource => this.translate.instant('resource.delete-resource-title',
|
||||
{ resourceTitle: resource.title });
|
||||
this.config.deleteEntityContent = () => this.translate.instant('resource.delete-resource-text');
|
||||
this.config.deleteEntitiesTitle = count => this.translate.instant('resource.delete-resources-title', {count});
|
||||
this.config.deleteEntitiesContent = () => this.translate.instant('resource.delete-resources-text');
|
||||
|
||||
this.config.entitiesFetchFunction = pageLink => this.resourceService.getResources(pageLink) as Observable<PageData<Resource>>;
|
||||
this.config.loadEntity = id => this.resourceService.getResource(id.id);
|
||||
this.config.saveEntity = resource => this.resourceService.saveResource(resource);
|
||||
this.config.deleteEntity = id => this.resourceService.deleteResource(id.id);
|
||||
}
|
||||
|
||||
resolve(): EntityTableConfig<Resource> {
|
||||
this.config.tableTitle = this.translate.instant('resource.resources-library');
|
||||
const authUser = getCurrentAuthUser(this.store);
|
||||
this.config.deleteEnabled = (resource) => this.isResourceEditable(resource, authUser.authority);
|
||||
this.config.entitySelectionEnabled = (resource) => this.isResourceEditable(resource, authUser.authority);
|
||||
this.config.detailsReadonly = (resource) => !this.isResourceEditable(resource, authUser.authority);
|
||||
return this.config;
|
||||
}
|
||||
|
||||
exportResource($event: Event, resource: ResourceInfo) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
this.resourceService.downloadResource(resource.id.id).subscribe();
|
||||
}
|
||||
|
||||
private isResourceEditable(resource: Resource, authority: Authority): boolean {
|
||||
if (authority === Authority.TENANT_ADMIN) {
|
||||
return resource && resource.tenantId && resource.tenantId.id !== NULL_UUID;
|
||||
} else {
|
||||
return authority === Authority.SYS_ADMIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2021 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<div class="tb-details-buttons" fxLayout.xs="column">
|
||||
<button mat-raised-button color="primary" fxFlex.xs
|
||||
[disabled]="(isLoading$ | async)"
|
||||
(click)="onEntityAction($event, 'delete')"
|
||||
[fxShow]="!hideDelete() && !isEdit">
|
||||
{{'resource.delete' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="mat-padding" fxLayout="column">
|
||||
<form [formGroup]="entityForm">
|
||||
<fieldset [disabled]="(isLoading$ | async) || !isEdit">
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>resource.resource-type</mat-label>
|
||||
<mat-select formControlName="resourceType" required>
|
||||
<mat-option *ngFor="let resourceType of resourceTypes" [value]="resourceType">
|
||||
{{ resourceTypesTranslationMap.get(resourceType) }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block" *ngIf="entityForm.get('resourceType').value !== resourceType.LWM2M_MODEL || !isAdd">
|
||||
<mat-label translate>resource.title</mat-label>
|
||||
<input matInput formControlName="title" required [readonly]="entityForm.get('resourceType').value === resourceType.LWM2M_MODEL">
|
||||
<mat-error *ngIf="entityForm.get('title').hasError('required')">
|
||||
{{ 'resource.title-required' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<tb-file-input
|
||||
formControlName="data"
|
||||
required
|
||||
[convertToBase64]="true"
|
||||
[allowedExtensions]="getAllowedExtensions()"
|
||||
[accept]="getAcceptType()"
|
||||
dropLabel="{{'resource.drop-file' | translate}}"
|
||||
[existingFileName]="entityForm.get('fileName')?.value"
|
||||
(fileNameChanged)="entityForm?.get('fileName').patchValue($event)">
|
||||
</tb-file-input>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
@ -0,0 +1,122 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { EntityComponent } from '@home/components/entity/entity.component';
|
||||
import {
|
||||
Resource,
|
||||
ResourceType,
|
||||
ResourceTypeExtension,
|
||||
ResourceTypeMIMETypes,
|
||||
ResourceTypeTranslationMap
|
||||
} from '@shared/models/resource.models';
|
||||
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-resources-library',
|
||||
templateUrl: './resources-library.component.html'
|
||||
})
|
||||
export class ResourcesLibraryComponent extends EntityComponent<Resource> implements OnInit, OnDestroy {
|
||||
|
||||
readonly resourceType = ResourceType;
|
||||
readonly resourceTypes = Object.values(this.resourceType);
|
||||
readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
|
||||
|
||||
private destroy$ = new Subject();
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
protected translate: TranslateService,
|
||||
@Inject('entity') protected entityValue: Resource,
|
||||
@Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Resource>,
|
||||
public fb: FormBuilder) {
|
||||
super(store, fb, entityValue, entitiesTableConfigValue);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.entityForm.get('resourceType').valueChanges.pipe(
|
||||
distinctUntilChanged((oldValue, newValue) => [oldValue, newValue].includes(this.resourceType.LWM2M_MODEL)),
|
||||
takeUntil(this.destroy$)
|
||||
).subscribe((type) => {
|
||||
if (type === this.resourceType.LWM2M_MODEL) {
|
||||
this.entityForm.get('title').clearValidators();
|
||||
} else {
|
||||
this.entityForm.get('title').setValidators(Validators.required);
|
||||
}
|
||||
this.entityForm.get('title').updateValueAndValidity({emitEvent: false});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
hideDelete() {
|
||||
if (this.entitiesTableConfig) {
|
||||
return !this.entitiesTableConfig.deleteEnabled(this.entity);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
buildForm(entity: Resource): FormGroup {
|
||||
return this.fb.group(
|
||||
{
|
||||
resourceType: [{value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL,
|
||||
disabled: this.isEdit }, [Validators.required]],
|
||||
data: [entity ? entity.data : null, [Validators.required]],
|
||||
fileName: [entity ? entity.fileName : null, [Validators.required]],
|
||||
title: [entity ? entity.title : '', []]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
updateForm(entity: Resource) {
|
||||
this.entityForm.patchValue({resourceType: entity.resourceType});
|
||||
if (this.isEdit) {
|
||||
this.entityForm.get('resourceType').disable({emitEvent: false});
|
||||
}
|
||||
this.entityForm.patchValue({
|
||||
data: entity.data,
|
||||
fileName: entity.fileName,
|
||||
title: entity.title
|
||||
});
|
||||
}
|
||||
|
||||
getAllowedExtensions() {
|
||||
try {
|
||||
return ResourceTypeExtension.get(this.entityForm.get('resourceType').value);
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
getAcceptType() {
|
||||
try {
|
||||
return ResourceTypeMIMETypes.get(this.entityForm.get('resourceType').value);
|
||||
} catch (e) {
|
||||
return '*/*';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,6 +101,9 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
|
||||
@Input()
|
||||
existingFileName: string;
|
||||
|
||||
@Input()
|
||||
convertToBase64 = false;
|
||||
|
||||
@Output()
|
||||
fileNameChanged = new EventEmitter<string>();
|
||||
|
||||
@ -128,7 +131,7 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
|
||||
const reader = new FileReader();
|
||||
reader.onload = (loadEvent) => {
|
||||
if (typeof reader.result === 'string') {
|
||||
const fileContent = reader.result;
|
||||
const fileContent = this.convertToBase64 ? window.btoa(reader.result) : reader.result;
|
||||
if (fileContent && fileContent.length > 0) {
|
||||
if (this.contentConvertFunction) {
|
||||
this.fileContent = this.contentConvertFunction(fileContent);
|
||||
@ -144,9 +147,13 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
|
||||
}
|
||||
}
|
||||
};
|
||||
if (this.convertToBase64) {
|
||||
reader.readAsBinaryString(file.file);
|
||||
} else {
|
||||
reader.readAsText(file.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -159,8 +166,10 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.autoUploadSubscription) {
|
||||
this.autoUploadSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.propagateChange = fn;
|
||||
|
||||
@ -17,22 +17,6 @@
|
||||
import { TenantId } from './id/tenant-id';
|
||||
import { BaseData, HasId } from '@shared/models/base-data';
|
||||
|
||||
///
|
||||
/// Copyright © 2016-2019 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
export enum EntityType {
|
||||
TENANT = 'TENANT',
|
||||
TENANT_PROFILE = 'TENANT_PROFILE',
|
||||
@ -48,7 +32,8 @@ export enum EntityType {
|
||||
ENTITY_VIEW = 'ENTITY_VIEW',
|
||||
WIDGETS_BUNDLE = 'WIDGETS_BUNDLE',
|
||||
WIDGET_TYPE = 'WIDGET_TYPE',
|
||||
API_USAGE_STATE = 'API_USAGE_STATE'
|
||||
API_USAGE_STATE = 'API_USAGE_STATE',
|
||||
TB_RESOURCE = 'TB_RESOURCE'
|
||||
}
|
||||
|
||||
export enum AliasEntityType {
|
||||
@ -282,7 +267,17 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
|
||||
type: 'entity.type-current-user-owner',
|
||||
list: 'entity.type-current-user-owner'
|
||||
}
|
||||
]
|
||||
],
|
||||
[
|
||||
EntityType.TB_RESOURCE,
|
||||
{
|
||||
details: 'resource.resource-library-details',
|
||||
add: 'resource.add',
|
||||
noEntities: 'resource.no-resource-text',
|
||||
search: 'resource.search',
|
||||
selectedEntities: 'resource.selected-resources'
|
||||
}
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
@ -353,6 +348,12 @@ export const entityTypeResources = new Map<EntityType, EntityTypeResource<BaseDa
|
||||
{
|
||||
helpLinkId: 'widgetsBundles'
|
||||
}
|
||||
],
|
||||
[
|
||||
EntityType.TB_RESOURCE,
|
||||
{
|
||||
helpLinkId: 'resources'
|
||||
}
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
26
ui-ngx/src/app/shared/models/id/tb-resource-id.ts
Normal file
26
ui-ngx/src/app/shared/models/id/tb-resource-id.ts
Normal file
@ -0,0 +1,26 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
|
||||
export class TbResourceId implements EntityId {
|
||||
entityType = EntityType.TB_RESOURCE;
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@ -39,6 +39,7 @@ export * from './material.models';
|
||||
export * from './oauth2.models';
|
||||
export * from './queue.models';
|
||||
export * from './relation.models';
|
||||
export * from './resource.models';
|
||||
export * from './rule-chain.models';
|
||||
export * from './rule-node.models';
|
||||
export * from './settings.models';
|
||||
|
||||
61
ui-ngx/src/app/shared/models/resource.models.ts
Normal file
61
ui-ngx/src/app/shared/models/resource.models.ts
Normal file
@ -0,0 +1,61 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { BaseData } from '@shared/models/base-data';
|
||||
import { TenantId } from '@shared/models/id/tenant-id';
|
||||
import { TbResourceId } from '@shared/models/id/tb-resource-id';
|
||||
|
||||
export enum ResourceType {
|
||||
LWM2M_MODEL = 'LWM2M_MODEL',
|
||||
PKCS_12 = 'PKCS_12',
|
||||
JKS = 'JKS'
|
||||
}
|
||||
|
||||
export const ResourceTypeMIMETypes = new Map<ResourceType, string>(
|
||||
[
|
||||
[ResourceType.LWM2M_MODEL, 'application/xml,text/xml'],
|
||||
[ResourceType.PKCS_12, 'application/x-pkcs12'],
|
||||
[ResourceType.JKS, 'application/x-java-keystore']
|
||||
]
|
||||
);
|
||||
|
||||
export const ResourceTypeExtension = new Map<ResourceType, string>(
|
||||
[
|
||||
[ResourceType.LWM2M_MODEL, 'xml'],
|
||||
[ResourceType.PKCS_12, 'p12,pfx'],
|
||||
[ResourceType.JKS, 'jks']
|
||||
]
|
||||
);
|
||||
|
||||
export const ResourceTypeTranslationMap = new Map<ResourceType, string>(
|
||||
[
|
||||
[ResourceType.LWM2M_MODEL, 'LWM2M model'],
|
||||
[ResourceType.PKCS_12, 'PKCS #12'],
|
||||
[ResourceType.JKS, 'JKS']
|
||||
]
|
||||
);
|
||||
|
||||
export interface ResourceInfo extends BaseData<TbResourceId> {
|
||||
tenantId?: TenantId;
|
||||
resourceKey?: string;
|
||||
title?: string;
|
||||
resourceType: ResourceType;
|
||||
}
|
||||
|
||||
export interface Resource extends ResourceInfo {
|
||||
data: string;
|
||||
fileName: string;
|
||||
}
|
||||
@ -1963,6 +1963,31 @@
|
||||
"invalid-additional-info": "Unable to parse additional info json.",
|
||||
"no-relations-text": "No relations found"
|
||||
},
|
||||
"resource": {
|
||||
"add": "Add Resource",
|
||||
"delete": "Delete resource",
|
||||
"delete-resource-text": "Be careful, after the confirmation the resource will become unrecoverable.",
|
||||
"delete-resource-title": "Are you sure you want to delete the resource '{{resourceTitle}}'?",
|
||||
"delete-resources-action-title": "Delete { count, plural, 1 {1 resource} other {# resources} }",
|
||||
"delete-resources-text": "Be careful, after the confirmation all selected resources will be removed.",
|
||||
"delete-resources-title": "Are you sure you want to delete { count, plural, 1 {1 resource} other {# resources} }?",
|
||||
"drop-file": "Drop a resource file or click to select a file to upload.",
|
||||
"empty": "Resource is empty",
|
||||
"export": "Export resource",
|
||||
"management": "Resource management",
|
||||
"no-resource-matching": "No resource matching '{{widgetsBundle}}' were found.",
|
||||
"no-resource-text": "No resources found",
|
||||
"open-widgets-bundle": "Open widgets bundle",
|
||||
"resource": "Resource",
|
||||
"resource-library-details": "Resource library details",
|
||||
"resource-type": "Resource type",
|
||||
"resources-library": "Resources library",
|
||||
"search": "Search resources",
|
||||
"selected-resources": "{ count, plural, 1 {1 resource} other {# resources} } selected",
|
||||
"system": "System",
|
||||
"title": "Title",
|
||||
"title-required": "Title is required."
|
||||
},
|
||||
"rulechain": {
|
||||
"rulechain": "Rule chain",
|
||||
"rulechains": "Rule chains",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user