diff options
author | jm.bardallo <juanmanuel.bardallo@sic.uhu.es> | 2019-05-13 09:06:55 +0200 |
---|---|---|
committer | jm.bardallo <juanmanuel.bardallo@sic.uhu.es> | 2019-05-13 09:06:55 +0200 |
commit | 1a9285621f660504fe450c07e071584887732ab6 (patch) | |
tree | 29af3c96dc0900cbb34030f441df9af6786981f2 | |
parent | d32a8b6f453fd0ce4d1c67d46912dbecf881191d (diff) |
Lista de trazas, se accede en el icono de la "campana" en la cabecera
Se muestra la información del usuario en el lateral izquierdo
Formulario de repositorio con la información minima
Creadas las páginas para los comandos:
- Borrar imagen de cache
- Formatear
- Particionar y formatear
Modificados timers de refresco de ventanas de ordenadores, dashboard y trazas
Creada version preliminar de creación de menús, aun por terminar de definir
44 files changed, 2089 insertions, 647 deletions
diff --git a/admin/WebConsole3/frontend/src/app/app-routing.module.ts b/admin/WebConsole3/frontend/src/app/app-routing.module.ts index 80d357fa..f91738a4 100644 --- a/admin/WebConsole3/frontend/src/app/app-routing.module.ts +++ b/admin/WebConsole3/frontend/src/app/app-routing.module.ts @@ -27,6 +27,9 @@ import {SoftwareComponent} from './pages/software/software.component'; import {LoginCommandComponent} from './pages/command/login-command/login-command.component'; import {ExecuteCommandComponent} from './pages/command/execute-command/execute-command.component'; import {CreateImageCommandComponent} from './pages/command/create-image-command/create-image-command.component'; +import {DeleteCacheImageCommandComponent} from './pages/command/delete-cache-image-command/delete-cache-image-command.component'; +import {FormatCommandComponent} from './pages/command/format-command/format-command.component'; +import {PartitionFormatCommandComponent} from './pages/command/partition-format-command/partition-format-command.component'; const routes: Routes = [ @@ -132,10 +135,18 @@ const routes: Routes = [ component: MenuEditComponent }, { + path: 'menus/edit/:id', + component: MenuEditComponent + }, + { path: 'commands', component: CommandComponent }, { + path: 'commands/partition_format', + component: PartitionFormatCommandComponent + }, + { path: 'commands/deploy_image', component: DeployImageCommandComponent }, @@ -152,6 +163,14 @@ const routes: Routes = [ component: CreateImageCommandComponent }, { + path: 'commands/delete_cache_image', + component: DeleteCacheImageCommandComponent + }, + { + path: 'commands/format', + component: FormatCommandComponent + }, + { path: 'commands/:id', component: EditCommandComponent }, diff --git a/admin/WebConsole3/frontend/src/app/app.component.html b/admin/WebConsole3/frontend/src/app/app.component.html index 8e5b9670..d6c88664 100644 --- a/admin/WebConsole3/frontend/src/app/app.component.html +++ b/admin/WebConsole3/frontend/src/app/app.component.html @@ -4,8 +4,8 @@ <ng-template #layoutEnabled> <mk-layout-wrapper> <mk-layout-header logoLink="/"> - <mk-layout-header-logo><b>Angular </b>AdminLTE</mk-layout-header-logo> - <mk-layout-header-logo-mini><b>A</b>LTE</mk-layout-header-logo-mini> + <mk-layout-header-logo>Open<b>GnSys</b> 3</mk-layout-header-logo> + <mk-layout-header-logo-mini>O<b>G</b>3</mk-layout-header-logo-mini> <app-header-inner></app-header-inner> </mk-layout-header> <mk-layout-sidebar-left> @@ -15,12 +15,12 @@ <app-sidebar-right-inner></app-sidebar-right-inner> </mk-layout-sidebar-right> <mk-layout-content> - <div mk-layout-content-before-header> - <div *mkLoadingPage="{checkPendingHttp: true, checkPendingRoute: true}"> - <mk-material-bar></mk-material-bar> + <router-outlet></router-outlet> + <div> + <div class="loader" *mkLoadingPage="{checkPendingHttp: true, checkPendingRoute: true}"> + <mk-circle></mk-circle> </div> </div> - <router-outlet></router-outlet> </mk-layout-content> <mk-layout-footer> <mk-layout-footer-left> diff --git a/admin/WebConsole3/frontend/src/app/app.module.ts b/admin/WebConsole3/frontend/src/app/app.module.ts index 5f861c65..34af2cb2 100644 --- a/admin/WebConsole3/frontend/src/app/app.module.ts +++ b/admin/WebConsole3/frontend/src/app/app.module.ts @@ -10,7 +10,7 @@ import {AuthModule, TokenInterceptorService} from 'globunet-angular/core'; import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from '@angular/common/http'; import {LoginComponent} from './pages/login/login.component'; import {ImageComponent} from './pages/image/image.component'; -import { LoadingPageModule, MaterialBarModule } from 'angular-loading-page'; +import { LoadingPageModule, CircleModule } from 'angular-loading-page'; import { DashboardComponent } from './pages/dashboard/dashboard.component'; import {TranslateHttpLoader} from '@ngx-translate/http-loader'; import {TranslateLoader, TranslateModule, TranslateService} from '@ngx-translate/core'; @@ -75,6 +75,10 @@ import {SoftwareComponentsGroupComponent} from './pages/software/software-compon import {LoginCommandComponent} from './pages/command/login-command/login-command.component'; import {ExecuteCommandComponent} from './pages/command/execute-command/execute-command.component'; import {CreateImageCommandComponent} from './pages/command/create-image-command/create-image-command.component'; +import {DeleteCacheImageCommandComponent} from './pages/command/delete-cache-image-command/delete-cache-image-command.component'; +import {FormatCommandComponent} from './pages/command/format-command/format-command.component'; +import {PartitionFormatCommandComponent} from './pages/command/partition-format-command/partition-format-command.component'; +import {ColResizableDirective} from './pages/common/directive/col-resizable.directive'; @@ -121,9 +125,13 @@ import {CreateImageCommandComponent} from './pages/command/create-image-command/ LoginCommandComponent, ExecuteCommandComponent, CreateImageCommandComponent, + DeleteCacheImageCommandComponent, + FormatCommandComponent, + PartitionFormatCommandComponent, EditCommandComponent, IcheckDirective, FixedToolboxBarDirective, + ColResizableDirective, OgInformationOptionsComponent, OgCommandsOptionsComponent, OgExecuteCommandOptionsComponent, @@ -166,6 +174,9 @@ import {CreateImageCommandComponent} from './pages/command/create-image-command/ LoginCommandComponent, ExecuteCommandComponent, CreateImageCommandComponent, + DeleteCacheImageCommandComponent, + FormatCommandComponent, + PartitionFormatCommandComponent, EditCommandComponent, OgOuGeneralOptionsComponent, TraceComponent, @@ -179,7 +190,7 @@ import {CreateImageCommandComponent} from './pages/command/create-image-command/ CoreModule, DropdownModule, LayoutModule.forRoot(AdminLteConf.staticConf), - LoadingPageModule, MaterialBarModule, + LoadingPageModule, CircleModule, BrowserAnimationsModule, HttpClientModule, AuthModule.forRoot(environment), diff --git a/admin/WebConsole3/frontend/src/app/core/header-inner/header-inner.component.html b/admin/WebConsole3/frontend/src/app/core/header-inner/header-inner.component.html index ad565376..31b4286d 100644 --- a/admin/WebConsole3/frontend/src/app/core/header-inner/header-inner.component.html +++ b/admin/WebConsole3/frontend/src/app/core/header-inner/header-inner.component.html @@ -2,20 +2,16 @@ <ul class="nav navbar-nav"> <li mk-dropdown type="list" [isWrapper]="false" class="notifications-menu"> <mk-dropdown-toggle> - <a [routerLink]="'/app/traces'" *ngIf="executionTasks.length == 0" #toggleElement> - <i class="fa fa-bell-o"></i> - </a> - <a *ngIf="executionTasks.length > 0" #toggleElement> - <i class="fa fa-bell-o"></i> - <span class="label label-warning" *ngIf="executionTasks.length > 0">{{executionTasks.length}}</span> - </a> + <a #toggleElement> + <i class="fa fa-bell-o"></i> + <span class="label label-warning" *ngIf="executionTasks.length > 0">{{executionTasks.length}}</span> + </a> </mk-dropdown-toggle> - <mk-dropdown-menu *ngIf="executionTasks.length > 0"> - <li class="header">You have 10 notifications</li> - <li class="header"> + <mk-dropdown-menu> + <li class="header" *ngIf="executionTasks.length > 0"> <span translate="you_have_x_exectution_taks" translate-values="{values: executionTasks.length}"></span> </li> - <li> + <li *ngIf="executionTasks.length > 0"> <!-- inner menu: contains the actual data --> <ul class="menu" *ngFor="let task of executionTasks"> <li> @@ -25,7 +21,7 @@ </a> <a href="javascript:void(0)" (click)="relaunchExecutionTask(task)" class="btn btn-sm pull-right small-box-footer"> <i class="fa fa-refresh"></i> - </a> + </a>A </div> <a href="javascript:void(0)" (click)="ogCommandsService.execute('REALTIME_LOG', {clientIp: task.client.ip})"> <i class="fa fa-warning text-yellow"></i> {{task.client.name}} ({{task.client.ip}})<br>{{task.commandType|translate}} @@ -36,7 +32,7 @@ <li class="footer"> <a href="javascript:void(0)" translate="view_all" [routerLink]="['/app/traces']"></a> </li> - </mk-dropdown-menu> + </mk-dropdown-menu> </li> <!-- Tasks: style can be found in dropdown.less --> <li mk-dropdown type="list" [isWrapper]="false" class="tasks-menu"> diff --git a/admin/WebConsole3/frontend/src/app/core/header-inner/header-inner.component.ts b/admin/WebConsole3/frontend/src/app/core/header-inner/header-inner.component.ts index d009ebe7..8aaef052 100644 --- a/admin/WebConsole3/frontend/src/app/core/header-inner/header-inner.component.ts +++ b/admin/WebConsole3/frontend/src/app/core/header-inner/header-inner.component.ts @@ -62,7 +62,7 @@ export class HeaderInnerComponent implements OnInit { self.getExectutionTasks(); }, function(error) { - this.toaster.pop({type: 'error', title: 'error', body: error}); + self.toaster.pop({type: 'error', title: 'error', body: error}); } ); diff --git a/admin/WebConsole3/frontend/src/app/core/sidebar-left-inner/sidebar-left-inner.component.html b/admin/WebConsole3/frontend/src/app/core/sidebar-left-inner/sidebar-left-inner.component.html index 7b2b79b6..68bebc52 100644 --- a/admin/WebConsole3/frontend/src/app/core/sidebar-left-inner/sidebar-left-inner.component.html +++ b/admin/WebConsole3/frontend/src/app/core/sidebar-left-inner/sidebar-left-inner.component.html @@ -3,7 +3,7 @@ <img src="assets/img/no-image.png" class="img-circle" alt="User Image"> </div> <div class="pull-left info"> - <p>Alexander Pierce</p> + <p>{{user.username}}</p> <a href="#"><i class="fa fa-circle text-success"></i> Online</a> </div> </div> diff --git a/admin/WebConsole3/frontend/src/app/core/sidebar-left-inner/sidebar-left-inner.component.ts b/admin/WebConsole3/frontend/src/app/core/sidebar-left-inner/sidebar-left-inner.component.ts index 036da6cb..991abdb2 100644 --- a/admin/WebConsole3/frontend/src/app/core/sidebar-left-inner/sidebar-left-inner.component.ts +++ b/admin/WebConsole3/frontend/src/app/core/sidebar-left-inner/sidebar-left-inner.component.ts @@ -1,7 +1,15 @@ -import { Component } from '@angular/core';
-
-@Component({
- selector: 'app-sidebar-left-inner',
- templateUrl: './sidebar-left-inner.component.html'
-})
-export class SidebarLeftInnerComponent {}
+import { Component } from '@angular/core'; +import {AuthModule} from 'globunet-angular/core'; + +@Component({ + selector: 'app-sidebar-left-inner', + templateUrl: './sidebar-left-inner.component.html' +}) +export class SidebarLeftInnerComponent { + user: any; + + constructor(private authModule: AuthModule) { + this.user = this.authModule.getLoggedUser(); + } + +} diff --git a/admin/WebConsole3/frontend/src/app/form-type/menu.form-type.ts b/admin/WebConsole3/frontend/src/app/form-type/menu.form-type.ts index 94a8d97b..b17cc7a0 100644 --- a/admin/WebConsole3/frontend/src/app/form-type/menu.form-type.ts +++ b/admin/WebConsole3/frontend/src/app/form-type/menu.form-type.ts @@ -6,7 +6,11 @@ export class MenuFormType extends GlobunetFormType { getForm() { const form: any[] = GlobunetFormType.getForm(new Menu()); this.setFieldType(form, 'description', 'textarea'); + this.setFieldType(form, 'comments', 'textarea'); this.setFieldType(form, 'resolution', 'select'); + this.getField(form, 'resolution').options = { + items: [] + }; return form; } } diff --git a/admin/WebConsole3/frontend/src/app/form-type/repository.form-type.ts b/admin/WebConsole3/frontend/src/app/form-type/repository.form-type.ts index 15b1b9f8..c0a8fffd 100644 --- a/admin/WebConsole3/frontend/src/app/form-type/repository.form-type.ts +++ b/admin/WebConsole3/frontend/src/app/form-type/repository.form-type.ts @@ -1,9 +1,11 @@ -import {Repository} from '../model/repository';
-import {GlobunetFormType} from './globunet.form-type';
-
-
-export class RepositoryFormType {
- getForm() {
- return GlobunetFormType.getForm(new Repository());
- }
-}
+import {Repository} from '../model/repository'; +import {GlobunetFormType} from './globunet.form-type'; + + +export class RepositoryFormType extends GlobunetFormType{ + getForm() { + const form = GlobunetFormType.getForm(new Repository()); + this.setFieldType(form, 'description', 'textarea'); + return form; + } +} diff --git a/admin/WebConsole3/frontend/src/app/model/image.ts b/admin/WebConsole3/frontend/src/app/model/image.ts index 2d52cf63..5df31b74 100644 --- a/admin/WebConsole3/frontend/src/app/model/image.ts +++ b/admin/WebConsole3/frontend/src/app/model/image.ts @@ -9,6 +9,7 @@ export class PartitionInfo { partitionCode: string; filesystem: string; osName: string; + type: string; } export class Image extends Resource { diff --git a/admin/WebConsole3/frontend/src/app/model/repository.ts b/admin/WebConsole3/frontend/src/app/model/repository.ts index 5b6756d2..36838128 100644 --- a/admin/WebConsole3/frontend/src/app/model/repository.ts +++ b/admin/WebConsole3/frontend/src/app/model/repository.ts @@ -1,25 +1,17 @@ -import { Resource } from 'globunet-angular/core/models/api/resource';
-
-export class Repository extends Resource {
- name: string;
- ip: string;
- port: number;
- password: string;
- configurationpath: string;
- adminpath: string;
- pxepath: string;
- description: string;
- info: any;
-
- constructor() {
- super();
- this.name = '';
- this.ip = '';
- this.port = 0;
- this.password = '';
- this.configurationpath = '';
- this.adminpath = '';
- this.pxepath = '';
- this.description = '';
- }
-}
+import { Resource } from 'globunet-angular/core/models/api/resource'; + +export class Repository extends Resource { + name: string; + ip: string; + password: string; + description: string; + info: any; + + constructor() { + super(); + this.name = ''; + this.ip = ''; + this.password = ''; + this.description = ''; + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.html b/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.html new file mode 100644 index 00000000..5d09b0aa --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.html @@ -0,0 +1,62 @@ +<section class="content-header"> + <h1 translate="delete_cache_image"> + </h1> + <ol class="breadcrumb"> + <li><a [routerLink]="'/app/dashboard'"><i class="fa fa-dashboard"></i>{{'dashboard'|translate}}</a></li> + <li><a [routerLink]="'/app/ous'"><i class="fa fa-th"></i>{{'ous'|translate}}</a></li> + <li class="active" translate="delete_cache_image"></li> + </ol> +</section> +<section fixed-toolboxbar class="toolboxbar"> + <div> + <div class="col-md-12"> + <div class="box-tools pull-right"> + <button class="btn btn-primary" translate="execute" (click)="sendCommand()"></button> + </div> + </div> + </div> +</section> +<section style="padding: 0 30px;"> + <h3 translate="selected_clients"></h3> + <app-og-selected-clients></app-og-selected-clients> +</section> +<!-- Main content --> +<section class="content"> + <div class="row"> + <div class="col-md-12"> + <div class="box box-primary"> + <div class="box-header with-border"> + </div> + <div class="box-body"> + <div> + <table class="table"> + <thead> + <tr> + <th translate="select"> + </th> + <th translate="type"> + </th> + <th translate="name"> + </th> + </tr> + </thead> + <tbody> + <tr *ngFor="let image of cacheImages" > + <td> + <input icheck type="checkbox" checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" class="selection-checkbox" [(ngModel)]="image.selected" /> + </td> + <td> + {{image.type}} + </td> + <td> + {{image.name}} + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </div> + </div> +</section> diff --git a/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.scss b/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.scss new file mode 100644 index 00000000..08b1082b --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.scss @@ -0,0 +1,3 @@ +app-execute-command { + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.ts b/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.ts new file mode 100644 index 00000000..efc14d6d --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/delete-cache-image-command/delete-cache-image-command.component.ts @@ -0,0 +1,120 @@ +import {Component, OnInit} from '@angular/core'; + +import {ToasterService} from '../../../service/toaster.service'; +import {ActivatedRoute, Router} from '@angular/router'; +import {TranslateService} from '@ngx-translate/core'; +import {OgCommonService} from '../../../service/og-common.service'; +import {OgSweetAlertService} from '../../../service/og-sweet-alert.service'; +import {AuthModule} from 'globunet-angular/core'; +import {User} from '../../../model/user'; +import {OGCommandsService} from '../../../service/og-commands.service'; +import {CommandService} from '../../../api/command.service'; +import {Image} from '../../../model/image'; +import {Command, Excecution} from '../../../model/command'; +import {Client} from '../../../model/client'; +import {Repository} from '../../../model/repository'; +import {RepositoryService} from '../../../api/repository.service'; +import {ImageService} from '../../../api/image.service'; + +@Component({ + selector: 'app-delete-cache-image-command', + templateUrl: './delete-cache-image-command.component.html', + styleUrls: [ './delete-cache-image-command.component.scss' ] +}) +export class DeleteCacheImageCommandComponent implements OnInit { + private readonly user: User; + private constants: any; + public repositories: Repository[]; + public execution = new Excecution(); + public commands: Command[] = []; + public client: Client; + public cacheImages = []; + public command = {canonicalName: '', image: new Image()}; + + + // this tells the tabs component which Pages + // should be each tab's root Page + constructor(public ogCommandsService: OGCommandsService, + private authModule: AuthModule, + private router: Router, + private activatedRoute: ActivatedRoute, + private ogCommonService: OgCommonService, + private commandService: CommandService, + private imageService: ImageService, + private repositoryService: RepositoryService, + private ogSweetAlert: OgSweetAlertService, + private toaster: ToasterService, + private translate: TranslateService) { + this.user = this.authModule.getLoggedUser(); + } + + + + + ngOnInit() { + if (this.user && this.ogCommonService.selectedClients) { + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.constants = data.constants; + } + ); + const clientIds = Object.keys(this.ogCommonService.selectedClients); + this.execution.clients = clientIds.join(','); + // Capturar para todos los clientes todas las imágenes de cache + this.cacheImages = []; + for (let index = 0; index < clientIds.length; index++) { + const client = this.ogCommonService.selectedClients[clientIds[index]]; + const diskConfigs = this.ogCommonService.getDisksConfigFromPartitions(client.partitions); + for (let dc = 0; dc < diskConfigs.length; dc++) { + const diskConfig = diskConfigs[dc]; + for (let p = 0; p < diskConfig.partitions.length; p++) { + const partition = diskConfig.partitions[p]; + if (partition.partitionCode === 'ca') { + // Solo cogemos las imagenes .img, no los .sum + for (let f = 0; f < partition.cacheContent.files.length; f++) { + const file = partition.cacheContent.files[f]; + // Si no es un .sum + if (!file.name.match('.sum')) { + this.cacheImages.push(file); + } + } + } + } + } + } + } else { + // TODO - dar error? + this.ogSweetAlert.error(this.translate.instant('opengnsys_error'), this.translate.instant('not_clients_selected')); + this.router.navigate(['app.ous']); + } + } + + + sendCommand() { + this.execution.script = ''; + for (let f = 0; f < this.cacheImages.length; f++) { + if (this.cacheImages[f].selected === true) { + if (this.cacheImages[f].type !== 'D') { + this.execution.script += 'rm -rf $OGCAC/$OGIMG/' + this.cacheImages[f].name.trim() + '*'; + } else { + this.execution.script += 'rm -rf $OGCAC/$OGIMG/' + this.cacheImages[f].name.trim(); + } + this.execution.script += '\n'; + } + } + this.execution.script += this.constants.commands.REFRESH_INFO + '\n'; + this.execution.script = this.execution.script.replace(/\"/g, '\\"').replace(/\$/g, '\\\$'); + this.execution.type = 'RUN_SCRIPT'; + + this.commandService.execute(this.execution).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_executed')}); + this.router.navigate(['/app/ous']); + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.html b/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.html new file mode 100644 index 00000000..a14c8510 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.html @@ -0,0 +1,96 @@ +<section class="content-header"> + <h1 translate="create_image"> + </h1> + <ol class="breadcrumb"> + <li><a [routerLink]="'/app/dashboard'"><i class="fa fa-dashboard"></i>{{'dashboard'|translate}}</a></li> + <li><a [routerLink]="'/app/ous'"><i class="fa fa-th"></i>{{'ous'|translate}}</a></li> + <li class="active" translate="create_image"></li> + </ol> +</section> +<section fixed-toolboxbar class="toolboxbar"> + <div> + <div class="col-md-12"> + <div class="box-tools pull-right"> + <button class="btn btn-primary" translate="execute" (click)="sendCommand()"></button> + </div> + </div> + </div> +</section> +<section style="padding: 0 30px;"> + <h3 translate="selected_clients"></h3> + <app-og-selected-clients></app-og-selected-clients> +</section> +<!-- Main content --> +<section class="content"> + <div class="row"> + <div class="col-md-12"> + <div class="box box-primary" *ngFor="let item of clientGroups | keyvalue"> + <div class="box-header with-border"> + </div> + <div class="box-body"> + <div *ngFor="let client of item.value"> + <div class="form-group"> + <span translate="clients"></span> + <label> + {{client.name}} + </label> + </div> + <div> + <table class="table"> + <thead> + <tr> + <th translate="select"> + </th> + <th translate="disk"> + </th> + <th translate="partition"> + </th> + <th translate="type"> + </th> + <th translate="filesystem"> + </th> + <th translate="size"> + </th> + <th translate="osname"> + </th> + </tr> + </thead> + <tbody> + <ng-container *ngFor="let partition of client.partitions"> + <tr *ngIf="partition.numPartition !== 0 && partition.size > 0"> + <td> + <input icheck type="checkbox" checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" [(ngModel)]="partition.selected" /> + </td> + <td> + {{partition.numDisk}} + </td> + <td> + {{partition.numPartition}} + </td> + <td> + <select [(ngModel)]="partition.partitionCode"> + <option *ngFor="let type of getPartitionTypes(client.partitions)" [ngValue]="type.id">{{type.type}}</option> + </select> + </td> + <td> + <select [(ngModel)]="partition.filesystem"> + <option [ngValue]="filesystem" *ngFor="let filesystem of constants.filesystems">{{filesystem}}</option> + </select> + </td> + <td> + {{ogCommonService.getUnits(partition.size*1024)}} + </td> + <td> + {{partition.osName}} + </td> + </tr> + </ng-container> + </tbody> + </table> + </div> + </div> + </div> + </div> + </div> + </div> +</section> diff --git a/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.scss b/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.scss new file mode 100644 index 00000000..ccb34440 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.scss @@ -0,0 +1,3 @@ +command { + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.ts b/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.ts new file mode 100644 index 00000000..62b733b8 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/format-command/format-command.component.ts @@ -0,0 +1,150 @@ +import {Component, OnInit} from '@angular/core'; + +import {ToasterService} from '../../../service/toaster.service'; +import {ActivatedRoute, Router} from '@angular/router'; +import {TranslateService} from '@ngx-translate/core'; +import {OgCommonService} from '../../../service/og-common.service'; +import {OgSweetAlertService} from '../../../service/og-sweet-alert.service'; +import {AuthModule} from 'globunet-angular/core'; +import {User} from '../../../model/user'; +import {OGCommandsService} from '../../../service/og-commands.service'; +import {Client, Partition} from '../../../model/client'; +import {CommandService} from '../../../api/command.service'; +import {forkJoin} from 'rxjs'; + +@Component({ + selector: 'app-format-command', + templateUrl: './format-command.component.html', + styleUrls: [ './format-command.component.scss' ] +}) +export class FormatCommandComponent implements OnInit { + execution = {clients: '', script: '', type: ''}; + command = {}; + user: User; + clientGroups = {}; + constants: any; + + // this tells the tabs component which Pages + // should be each tab's root Page + constructor(public ogCommandsService: OGCommandsService, + private authModule: AuthModule, + private router: Router, + private activatedRoute: ActivatedRoute, + public ogCommonService: OgCommonService, + private commandService: CommandService, + private ogSweetAlert: OgSweetAlertService, + private toaster: ToasterService, + private translate: TranslateService) { + this.user = this.authModule.getLoggedUser(); + } + + + ngOnInit() { + if (this.user && this.ogCommonService.selectedClients) { + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.constants = data.constants; + } + ); + const clientIds = Object.keys(this.ogCommonService.selectedClients); + // Recorrer todos los clientes y formar los grupos según el partitionCode de sus particiones, deben coincidir todos + for (let index = 0; index < clientIds.length; index++) { + // Generamos una clave usando disco-particion-code para comparar + const client = this.ogCommonService.selectedClients[clientIds[index]]; + const key = this.getPartitionsCode(client.partitions); + + if (!this.clientGroups[key]) { + this.clientGroups[key] = []; + } + this.clientGroups[key].push(client); + } + } else { + // TODO - dar error? + this.ogSweetAlert.error(this.translate.instant('opengnsys_error'), this.translate.instant('not_clients_selected')); + this.router.navigate(['/app/ous']); + } + } + + getPartitionsCode(partitions) { + let key = ''; + for (let p = 0; p < partitions.length; p++) { + // Además de calcular la clave, alteramos el partitionCode pasandolo a mayusculas y aplicando padding de "0" a la izquierda si es necesario + partitions[p].partitionCode = partitions[p].partitionCode.toUpperCase(); + if (partitions[p].partitionCode.length === 1) { + partitions[p].partitionCode = '0' + partitions[p].partitionCode; + } + key += partitions[p].numDisk + partitions[p].numPartition + partitions[p].partitionCode; + } + return key; + } + + + sendCommand() { + // Comrobar qué particiones se han seleccionado de qué grupos + const executions = {}; + const groups = Object.keys(this.clientGroups); + for (let g = 0; g < groups.length; g++) { + if (!executions[g]) { + executions[g] = { + clients: '', + script: '' + }; + } + // Recorrer las particiones del primer cliente de la lista y ver si hay alguna seleccionada + const found = false; + // La partición 0 no se usa, solo indica las propiedades del disco + let index = 1; + const client = this.clientGroups[groups[g]][0]; + for (let c = 0; c < this.clientGroups[groups[g]].length; c++) { + executions[g].clients += client.id + ','; + } + while (!found && index < client.partitions.length) { + const partition = client.partitions[index]; + if (partition.selected === true) { + if(executions[g].script === '') { + executions[g].script = 'ogUnmountAll ' + partition.numDisk + '\n'; + } + // Si la particion es cache + if (partition.partitionCode.toUpperCase() === 'CA') { + executions[g].script += 'ogFormatCache' + '\n'; + } else { + executions[g].script += 'ogFormat ' + partition.numDisk + ' ' + partition.numPartition + ' ' + partition.filesystem + '\n'; + } + } + index++; + } + } + + // Creamos tantas promises como diferentes grupos de ejecución haya + const promises = []; + const len = Object.keys(executions).length; + for (let index = 0; index < len; index++) { + const execution = { + type: 'RUN_SCRIPT', + script: executions[index].script, + clients: executions[index].clients.substring(0, executions[index].clients.length - 1) // Quitar la ultima "," + }; + promises.push(this.commandService.execute(execution)); + } + forkJoin(promises).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_executed')}); + this.router.navigate(['/app/ous']); + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + + getPartitionTypes(partitions) { + let types = []; + const infoPart = partitions.filter((partition: Partition) => partition.numPartition === 0); + if (infoPart.length === 1) { + const partitionTable = this.ogCommonService.getPartitionTable(infoPart[0]); + types = partitionTable.partitions; + } + return types; + } + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.html b/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.html new file mode 100644 index 00000000..fab8b8d7 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.html @@ -0,0 +1,164 @@ +<section class="content-header"> + <h1 translate="partition_format"></h1> + <ol class="breadcrumb"> + <li><a [routerLink]="'/app/dashboard'"><i class="fa fa-dashboard"></i> {{'dashboard'|translate}}</a></li> + <li><a [routerLink]="'/app/ous'" ><i class="fa fa-th"></i> {{'ous'|translate}}</a></li> + <li class="active" translate="partition_format"></li> + </ol> +</section> +<section style="padding: 0 30px;"> + <h3 translate="selected_clients"></h3> + <app-og-selected-clients></app-og-selected-clients> +</section> +<section class="content padding-top-30" style="padding-top: 30px"> + <div class="box box-default box-solid"> + <div class="box-header with-border"> + <div class="row"> + <div class="col-md-4"> + <span translate="num_disk"></span>: <input name="number" type="number" step="1" min="1" [(ngModel)]="diskConfig.disk" > + </div> + <div class="col-md-4"> + <span translate="part_table"></span>: <b> + <select name="partTableType" [(ngModel)]="diskConfig.parttable.type" (change)="checkPartitionTableType()"> + <option *ngFor="let partitionTableTypes of partitionTableTypes">{{partitionTableTypes.type}}</option> + </select></b> + </div> + <div class="col-md-4"> + <span translate="size"></span>: <b>{{getSizeInGB(diskConfig.size)}} GB ({{getSizeInGB(diskConfig.size*diskConfig.remaining/100)}} GB <span translate="free"></span>)</b> + </div> + </div> + <div class="box-tools pull-right"> + <button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i> + </button> + </div> + <!-- /.box-tools --> + </div> + <!-- /.box-header --> + <div class="box-body"> + <div class="row"> + <div class="col-md-12"> + <table col-resizable class="disk-partitions" [elements]="diskConfig.partitions" cr-update-property="usage" (onResize)="updatePartitionUsage($event)"> + <tr> + <td *ngFor="let partition of diskConfig.partitions" [style]="sanitizer.bypassSecurityTrustStyle('width: '+ partition.usage + '%; background-color:' + getPartitionColor(partition))"> + <span><span translate="{{partition.type}}"></span> ({{partition.usage}}%)</span> + <table *ngIf="partition.partitions && partition.partitions.length > 0" [elements]="partition.partitions" col-resizable class="disk-partitions" cr-update-property="usage" (onResize)="updateExtendedPartitions($event)" cr-force-refresh="refresh-extended-partitions"> + <tr> + <td *ngFor="let extendedPartition of partition.partitions" [style]="sanitizer.bypassSecurityTrustStyle('width: '+extendedPartition.usage+'%; background-color: '+ getPartitionColor(extendedPartition))"> + <span>{{extendedPartition.type}} ({{extendedPartition.usage}}%)</span> + </td> + </tr> + </table> + </td> + </tr> + </table> + </div> + </div> + <div class="row"> + <div class="col-md-8"> + <button translate="add" class="btn btn-primary" (click)="addPartition()"></button> + <table class="table table-condensed table-striped partitions"> + <tbody> + <tr> + <th translate="partition"></th> + <th translate="part_type"></th> + <th translate="filesystem"></th> + <th><span translate="size"></span> (bytes)</th> + <th translate="usage"></th> + <th translate="format"></th> + <th translate="remove"></th> + </tr> + </tbody> + <tbody *ngFor="let partition of diskConfig.partitions; let index = index;" ng-class="{'extended-partition': partition.partitions.length > 0}"> + <tr [ngClass]="index%2 == 0 ? 'odd' : 'even'" *ngIf="partition.partition != 0"> + <td>{{partition.partition}} ({{getSizeInGB(partition.size)}}GB)</td> + <td> + <select name="partitionType" [disabled]="partTableTypeIsGPT() && index == 0 && isEFI(partition)" [(ngModel)]="partition.type" (change)="checkPartitionType(partition)"> + <option *ngFor="let partitionType of diskConfig.parttable.partitions">{{partitionType.type}}</option> + </select> + </td> + <td>{{partition.filesystem}}</td> + <td> + <input step=".01" name="partitionSize" type="number" [(ngModel)]="partition.size" (change)="setPartitionUsage(diskConfig, partition)"/> + </td> + <td> + <input step=".01" name="partitionUsage" type="number" [(ngModel)]="partition.usage" (change)="updatePartitionUsage(partition)"> + <span>%</span> + </td> + <td> + <div *ngIf="isEXTENDED(partition) != true" class="checkbox clip-check check-primary checkbox-inline" style="margin-top: 0"> + <input name="format" icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" value="true" [(ngModel)]="partition.format" /> + </div> + </td> + <td> + <button *ngIf="!(partTableTypeIsGPT() && $index == 0 && isEFI(partition))" class="btn btn-danger btn-xs"><i class="fa fa-times" (click)="removePartition(partition)"></i></button> + </td> + <td *ngIf="isEXTENDED(partition) == true"> + <button class="btn btn-primary btn-xs"><i class="fa fa-plus" (click)="addExtendedPartition(partition)"></i></button> + </td> + </tr> + <tr *ngFor="let extendedPartition of partition.partitions" class="extended" ng-class="{'odd': $index%2 == 0, 'even': $index%2 != 0}"> + <td>{{extendedPartition.partition}}</td> + <td> + <select name="extendedPartitionType" [(ngModel)]="extendedPartition.type" (change)="checkPartitionType(extendedPartition)"> + <option *ngFor="let partitionType of diskConfig.parttable.partitions">{{partitionType.type}}</option> + </select> + </td> + <td>{{extendedPartition.filesystem}}</td> + <td><input step=".01" name="extendedPartitonSize" type="number"[(ngModel)]="extendedPartition.size" (change)="updateExtendedPartitions(extendedPartition)"/></td> + <td> + <input step=".01" name="extendedPartitionUsage" type="number"[(ngModel)]="extendedPartition.usage" (change)="updateExtendedPartitionsUsage(extendedPartition)"> + <span>%</span> + </td> + <td> + <div class="checkbox clip-check check-primary checkbox-inline" style="margin-top: 0"> + <input name="format" icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" value="true"[(ngModel)]="extendedPartition.format" /> + </div> + </td> + <td> + <button class="btn btn-danger btn-xs"><i class="fa fa-times" (click)="removeExtendedPartition(extendedPartition)"></i></button> + </td> + </tr> + </tbody> + </table> + </div> + <div class="col-md-4"> + <!--flot dataset="diskConfig.diskChartData" options="diskConfig.diskChartOptions" height="200px"></flot--> + <div class="chart" > + <canvas baseChart + [data]="diskConfig.diskChartData" + [labels]="diskConfig.diskChartLabels" + [colors]="diskConfig.diskPieChartColors" + [chartType]="'doughnut'" + [options]="diskConfig.diskChartOptions"> + </canvas> + </div> + </div> + </div> + </div> + <!-- /.box-body --> + <div class="box-footer"> + <div class="row"> + <div class="col-md-12"> + <div class="btn-group pull-left"> + <button class="btn btn-primary"(click)="generateOgInstruction()"><span translate="generate_og_instruction"></span></button> + <button [class]="editInstructions == false ? 'btn btn-default':'btn btn-success'" *ngIf="ogCommandsService.ogInstructions != ''" (click)="editInstructions = !editInstructions"><span translate="{{editInstructions == false?'edit':'done'}}"></span></button> + </div> + <div class="btn-group pull-right"> + <app-og-execute-command-options></app-og-execute-command-options> + </div> + </div> + </div> + <div class="row"> + <div class="col-md-12"> + <div class="box"> + <div class="box-header"></div> + <div class="box-body" > + <div *ngIf="editInstructions == false" [innerHTML]="ogCommandsService.ogInstructions|ogCommands"></div> + <textarea *ngIf="editInstructions == true" class="og-instructions" [(ngModel)]="ogCommandsService.ogInstructions"></textarea> + </div> + </div> + </div> + </div> + </div> + </div> +</section> diff --git a/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.scss b/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.scss new file mode 100644 index 00000000..97d2231d --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.scss @@ -0,0 +1,22 @@ +::ng-deep app-partition-format-command { + ::ng-deep table.disk-partitions { + min-height: 40px; + } + ::ng-deep table.disk-partitions td { + position: relative; + } + ::ng-deep span.resizer { + display: block; + position: absolute; + top: 0; + right: 0; + margin: 0; + background-color: black; + width: 2px; + height: 100%; + padding: 0 !important; + cursor: col-resize; + border: 1px solid transparent; + } + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.ts b/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.ts new file mode 100644 index 00000000..8c75fa2e --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/partition-format-command/partition-format-command.component.ts @@ -0,0 +1,618 @@ +import {Component, OnInit} from '@angular/core'; + +import {ToasterService} from '../../../service/toaster.service'; +import {ActivatedRoute, Router} from '@angular/router'; +import {TranslateService} from '@ngx-translate/core'; +import {OgCommonService} from '../../../service/og-common.service'; +import {OgSweetAlertService} from '../../../service/og-sweet-alert.service'; +import {AuthModule} from 'globunet-angular/core'; +import {User} from '../../../model/user'; +import {OGCommandsService} from '../../../service/og-commands.service'; +import {CommandService} from '../../../api/command.service'; +import {DomSanitizer} from '@angular/platform-browser'; +import {ChartOptions} from 'chart.js'; +import {Client} from '../../../model/client'; + +@Component({ + selector: 'app-partition-format-command', + templateUrl: './partition-format-command.component.html', + styleUrls: [ './partition-format-command.component.scss' ] +}) +export class PartitionFormatCommandComponent implements OnInit { + execution = {clients: '', script: '', type: ''}; + command = {}; + user: User; + constants: any; + diskConfig: any; + partitionTableTypes = ['MSDOS', 'GPT']; + partitionTypes = []; + editInstructions = false; + + // this tells the tabs component which Pages + // should be each tab's root Page + constructor(public ogCommandsService: OGCommandsService, + public sanitizer: DomSanitizer, + private authModule: AuthModule, + private router: Router, + private activatedRoute: ActivatedRoute, + public ogCommonService: OgCommonService, + private commandService: CommandService, + private ogSweetAlert: OgSweetAlertService, + private toaster: ToasterService, + private translate: TranslateService) { + this.user = this.authModule.getLoggedUser(); + this.diskConfig = {}; + this.ogCommonService.saveSelection(); + } + + ngOnInit() { + if (this.user) { + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.constants = data.constants; + // Comprobar la selección de clientes + if (this.ogCommonService.selectedClients) { + + // Recorrer todos los clientes seleccionados y usar el tamaño del disco de menor tamaño + const clientsId = Object.keys(this.ogCommonService.selectedClients); + let minSize = 0; + // por defecto la tabla de particiones msdos + let parttable = this.ogCommonService.getPartitionTable({partitionCode: 1}); + if (this.ogCommonService.selectedClients[clientsId[0]].partitions[0]) { + minSize = this.ogCommonService.selectedClients[clientsId[0]].partitions[0].size; + parttable = this.ogCommonService.getPartitionTable(this.ogCommonService.selectedClients[clientsId[0]].partitions[0]); + } + + for (let c = 1; c < clientsId.length; c++) { + if (this.ogCommonService.selectedClients[clientsId[0]].partitions[0].size < minSize) { + minSize = this.ogCommonService.selectedClients[clientsId[0]].partitions[0].size; + + } + } + + this.diskConfig = { + disk: 1, + parttable: parttable, + size: minSize, + partitions: [ + { + partition: 0, + type: 'free_space', + filesystem: '', + size: minSize, + usage: 100, + } + ] + }; + // TODO - Revisar Usamos la tabla de particiones del cliente seleccionado + const client: Client = this.ogCommonService.selectedClients[clientsId[0]]; + const clientPartitions = []; + client.partitions.forEach((partition) => { + if (partition.numPartition !== 0 && partition.filesystem !== 'EMPTY') { + + // Obtener el tipo de partición según su code + if (partition.partitionCode.length === 1) { + partition.partitionCode = '0' + partition.partitionCode; + } + const partTypes = this.diskConfig.parttable.partitions.filter(p => p.id === partition.partitionCode.toUpperCase()); + if (partTypes.length > 0) { + partition.type = partTypes[0].type; + } + clientPartitions.push(partition); + } + }); + this.diskConfig.partitions = clientPartitions.concat((this.diskConfig.partitions)); + this.reorderPartitions(); + + this.setChartData(this.diskConfig); + this.partitionTableTypes = this.constants.partitiontable; + } else { + // TODO - dar error? + this.ogSweetAlert.error(this.translate.instant('opengnsys_error'), this.translate.instant('not_clients_selected')); + this.router.navigate(['app/ous']); + } + + } + ); + } + } + + + partTableTypeIsGPT() { + return this.diskConfig.parttable.type === 'GPT'; + } + + partTableTypeIsMSDOS() { + return this.diskConfig.parttable.type === 'MSDOS'; + } + + partTableTypeIsLVM() { + return this.diskConfig.parttable.type === 'LVM'; + } + partTableTypeIsZPOOL() { + return this.diskConfig.parttable.type === 'ZPOOL'; + } + + isEFI(partition) { + return partition.type === 'EFI'; + } + + isCACHE(partition) { + return partition.type === 'CACHE'; + } + + isEXTENDED(partition) { + return partition.type === 'EXTENDED'; + } + + isWINDOWS(partition) { + return partition.type === 'NTFS' || partition.type === 'WINDOWS'; + } + + isLINUX(partition) { + return typeof partition.type === 'string' && partition.type.includes('LINUX'); + } + + isLINUXSWAP(partition) { + return partition.type === 'LINUX-SWAP'; + } + + isDATA(partition) { + return partition.type === 'DATA'; + } + + isUNKNOWN(partition) { + return partition.type === 'UNKNOWN'; + } + + isFreeSpace(partition) { + return partition.type === 'free_space'; + } + + + convertPartitionType(partition) { + if (this.partTableTypeIsMSDOS()) { + if (this.isWINDOWS(partition)) { + partition.type = 'NTFS'; + } else if (this.isUNKNOWN(partition)) { + partition.type = 'NTFS'; + } + } else if (this.partTableTypeIsGPT()) { + if (this.isWINDOWS(partition)) { + partition.type = 'WINDOWS'; + } else if (this.isDATA(partition)) { + partition.type = 'UNKNOWN'; + } + } else if (this.partTableTypeIsLVM()) { + partition.type = 'LVM-LV'; + } else if (this.partTableTypeIsZPOOL()) { + partition.type = 'ZFS-VOL'; + } + } + + checkPartitionTableType() { + const self = this; + if (this.partTableTypeIsMSDOS()) { + if (this.diskConfig.partitions.length > 5) { + this.ogSweetAlert.info('opengnsys_info', 'En MS-DOS sólo puede haber 4 particiones primarias, se creará una extendida con el resto de particiones'); + const tmpPartitions = []; + const extendedPartition = { + type: 'EXTENDED', + partitions: [], + size: 0, + usage: 0 + }; + const hasCache = (this.diskConfig.partitions.filter((partition) => partition.type === 'CACHE').length > 0); + // Si tiene cache se añaden 2 particiones, más la cache y el espacio libre + const numParts = hasCache ? 2 : 3; + this.diskConfig.partitions.forEach(function(partition, index) { + self.convertPartitionType(partition); + if (index < numParts || self.isFreeSpace(partition) || self.isCACHE(partition)) { + tmpPartitions.push(partition); + } else { + extendedPartition.partitions.push(partition); + extendedPartition.size += partition.size; + } + }); + // Actualizar porcentajes de las particiones extendidas + for (let p = 0; p < extendedPartition.partitions.length; p++) { + self.setPartitionUsage(extendedPartition, extendedPartition.partitions[p]); + } + tmpPartitions.push(extendedPartition); + self.diskConfig.partitions = tmpPartitions; + self.updatePartitionUsage(this.diskConfig.partitions[0]); + } else { + self.diskConfig.partitions.forEach(function(partition, index) { + self.convertPartitionType(partition); + }); + } + + } else { + const tmpPartitions = []; + // Para particiones GPT se crea una particion EFI en primer lugar de 512M + if (this.partTableTypeIsGPT()) { + // Comprobar si existe ya una partición EFI al principio del disco, sino, crearla + if (!this.isEFI(this.diskConfig.partitions[0])) { + tmpPartitions.push({ + type: 'EFI', + size: 512000, + usage: (512000 / this.diskConfig.size) * 100 + }); + } + } + + this.diskConfig.partitions.forEach(function(partition) { + self.convertPartitionType(partition); + if (!self.isEXTENDED(partition)) { + tmpPartitions.push(partition); + } else { + partition.partitions.forEach(function(extPart) { + self.convertPartitionType(extPart); + tmpPartitions.push(extPart); + self.setPartitionUsage(this.diskConfig, extPart); + }); + } + }); + this.diskConfig.partitions = tmpPartitions; + this.updatePartitionUsage(this.diskConfig.partitions[0]); + } + } + + addPartition() { + // Si el tipo de tabla de particiones es MSDOS, sólo se admiten 4 particiones + if (this.partTableTypeIsGPT() || (this.partTableTypeIsMSDOS() && this.diskConfig.partitions.length < 5)) { + this.diskConfig.partitions.push({ + partition: (this.diskConfig.partitions.length), + type: this.partTableTypeIsGPT() ? 'WINDOWS' : 'NTFS', + filesystem: '', + size: 0, + usage: 5 + + }); + this.updatePartitionUsage(this.diskConfig.partitions[this.diskConfig.partitions.length - 1]); + } else if (this.partTableTypeIsMSDOS()) { + this.ogSweetAlert.warning('opengnsys_warning', 'En MS-DOS sólo puede haber 4 particiones primarias, utilice alguna como extendida si necesita más particiones'); + } + // Actualizar información + // setChartData(this.diskConfig); + } + + addExtendedPartition(partition) { + partition.partitions.push({ + partition: (partition.partitions.length + 1), + type: 'NTFS', + filesystem: '', + size: 0, + usage: 0 + + }); + const extendedPartUsage = Math.round(100 / partition.partitions.length); + partition.partitions.forEach(function(extPart) { + extPart.usage = extendedPartUsage; + }); + // Actualiza tamaños en funcion del porcentaje de uso + this.updateExtendedPartitions(partition); + } + + updateExtendedPartitions(extPartition) { + const parentPartition = this.diskConfig.partitions.filter((partition) => partition.type === 'EXTENDED')[0]; + const totalSize = parentPartition.size; + parentPartition.partitions.forEach( function(extPart, index) { + extPart.partition = (index + 1); + extPart.size = Math.round((extPart.usage || 0) * totalSize / 100); + }); + } + + updateExtendedPartitionsUsage(extPartition) { + const parentPartition = this.diskConfig.partitions.filter((partition) => partition.type === 'EXTENDED')[0]; + const index = parentPartition.partitions.indexOf(extPartition); + let nextPart = null; + // si solo hay una partición el uso es siempre el 100% + if (parentPartition.partitions.length === 1) { + extPartition.usage = 100; + } else { + nextPart = null; + // el porcentaje que crezca la particion, se le resta a la siguiente o a la anterior si es la ultima + if (index === parentPartition.partitions.length - 1) { + nextPart = parentPartition.partitions[index - 1]; + } else { + nextPart = parentPartition.partitions[index + 1]; + } + let restPercent = 100; + parentPartition.partitions.forEach(function(extPart) { + restPercent -= (extPart.usage || 0); // Hay casos en los que se obtiene NaN + }); + // Le quitamos el porcentaje a la particion contigua hasta que quede con un mínimo de 1 + if (nextPart.usage > (restPercent * -1)) { + nextPart.usage += restPercent; + } else { + // restamos 1 al resto del porcentaje que será lo que ocupe la particion contigua + restPercent = Math.abs(restPercent) - (nextPart.usage - 1); + nextPart.usage = 1; + + extPartition.usage -= restPercent; + } + } + this.updateExtendedPartitions(extPartition); + } + + removeExtendedPartition(extPartition) { + const parentPartition = this.diskConfig.partitions.filter((partition) => partition.type === 'EXTENDED')[0]; + const index = parentPartition.partitions.indexOf(extPartition); + if (index !== -1) { + parentPartition.partitions.splice(index, 1); + } + // Comprobamos el % que queda libre ahora + const freePercent = Math.round(extPartition.usage / parentPartition.partitions.length); + parentPartition.partitions.forEach(function(extPart) { + extPart.usage += freePercent; + extPart.size = Math.round(extPart.usage * parentPartition.size / 100); + }); + } + + + reorderPartitions() { + const self = this; + const tmpPartitions = []; + let indexFreeSpace = -1; + let indexCache = -1; + this.diskConfig.partitions.forEach(function(partition, index) { + if (partition.type !== 'free_space' && !self.isCACHE(partition)) { + partition.partition = (tmpPartitions.length + 1); + tmpPartitions.push(partition); + } else if (self.isCACHE(partition)) { + indexCache = index; + } else if (partition.type === 'free_space') { + indexFreeSpace = index; + } + }); + // Añadir el espacio libre y la cache + if (indexFreeSpace !== -1) { + this.diskConfig.partitions[indexFreeSpace].usage = this.calculateFreeSpace(this.diskConfig.partitions[indexFreeSpace]); + tmpPartitions.push(this.diskConfig.partitions[indexFreeSpace]); + } + if (indexCache !== -1) { + tmpPartitions.push(this.diskConfig.partitions[indexCache]); + } + this.diskConfig.partitions = tmpPartitions; + } + + setChartData(diskConfig) { + const self = this; + const diskChartData = []; + const diskChartLabels = []; + const diskPieChartColors = [{ + backgroundColor: [] + }]; + let usedSpace = 0; + + diskConfig.partitions.forEach( function(partition) { + if (partition.size > 0) { + self.setPartitionUsage(diskConfig, partition); + if (partition.type === 'free_space') { + partition.usage = self.calculateFreeSpace(partition); + } + // El espacio libre solo se añade si es 0 + if (partition.type !== 'free_space' || (partition.type === 'free_space' && partition.usage > 0)) { + diskChartData.push(partition.usage); + diskChartLabels.push([ + self.translate.instant(partition.os || partition.filesystem || partition.type), + (partition.usage + '%') + ]); + diskPieChartColors[0].backgroundColor.push(self.getPartitionColor(partition)); + } + if (partition.type !== 'free_space') { + usedSpace += partition.usage; + } + } + }); + + this.diskConfig.remaining = Math.round(100 * (100 - usedSpace)) / 100; + + const diskChartOptions: ChartOptions = { + responsive: true, + legend: { + position: 'bottom' + }, + plugins: { + datalabels: { + formatter: (value, ctx) => { + const label = ctx.chart.data.labels[ctx.dataIndex]; + return label; + }, + }, + } + }; + + diskConfig.diskChartData = diskChartData; + diskConfig.diskChartOptions = diskChartOptions; + diskConfig.diskChartLabels = diskChartLabels; + diskConfig.diskPieChartColors = diskPieChartColors; + } + + getPartitionColor(partition) { + let color = '#c5e72b'; + // Para la partición de datos se usa un color específico + if (this.isDATA(partition)) { + color = 'rgb(237,194,64)'; + } else if (this.isEFI(partition)) { + color = '#bfe4e5'; + } else if (this.isWINDOWS(partition)) { + color = '#00c0ef'; + } else if (this.isLINUXSWAP(partition)) { + color = '#545454'; + } else if (this.isLINUX(partition)) { + color = '#605ca8'; + } else if (this.isCACHE(partition)) { + color = '#FC5A5A'; + } else if (this.isFreeSpace(partition)) { + color = '#bcbcbc'; + } + return color; + } + + /* + * Custom Label formatter + * ---------------------- + */ + labelFormatter(label, series) { + return '<div style="font-size:13px; text-align:center; padding:2px; color: #000; font-weight: 600;">' + + '<br>' + + series.usage + '%</div>'; + } + + getSizeInGB(size) { + size = size / (1024 * 1024); + return Math.round(size * 100) / 100; + } + + setPartitionUsage(diskConfig, partition) { + partition.usage = Math.round(((partition.size * 100) / diskConfig.size) * 100) / 100; + } + + checkPartitionType(partition) { + let ok = true; + if (this.isCACHE(partition)) { + // Comprobar si ya hay alguna partición como CACHE + if (this.diskConfig.partitions.filter((p) => p.type === 'CACHE').length > 1) { + this.ogSweetAlert.error('opengnsys_error', 'Solo debe haber una CACHE'); + partition.type = 'NTFS'; + ok = false; + } + } else if (this.isEXTENDED(partition)) { + // Comprobar si ya hay alguna partición como EXTENDIDA + if (this.diskConfig.partitions.filter((p) => p.type === 'EXTENDED').length > 1) { + this.ogSweetAlert.error('opengnsys_error', 'Solo debe haber una EXTENDIDA'); + partition.type = 'NTFS'; + ok = false; + } else { + partition.partitions = [ + { + partition: 1, + type: 'NTFS', + filesystem: '', + size: partition.size, + usage: 100 + + } + ]; + } + } else if (typeof partition.partitions !== 'undefined' && partition.partitions.length > 0) { + ok = false; + const self = this; + this.ogSweetAlert.question('opengnsys_question', 'Esta particion contiene otras partitiones!, si continua, dichas particiones serán eliminadas....', + function(yes) { + partition.partitions = []; + self.updatePartitionUsage(partition); + }, + function(cancel) { + // Si contesta no se deja el tipo extendido + partition.type = 'EXTENDED'; + } + ); + } + + if (ok) { + this.updatePartitionUsage(partition); + } + } + + updatePartitionUsage(partition) { + const remaining = this.calculateFreeSpace(partition); + if (partition.usage > remaining) { + partition.usage = remaining; + } + partition.size = Math.round(this.diskConfig.size * partition.usage / 100); + this.setChartData(this.diskConfig); + this.reorderPartitions(); + // Si es una partición extendida + if (typeof partition.partitions !== 'undefined' && partition.partitions.length > 0) { + this.updateExtendedPartitions(partition.partitions[0]); + } + } + + calculateFreeSpace(asignedPartition) { + let usedSpace = 0; + this.diskConfig.partitions.forEach(function(partition, index) { + if (partition !== asignedPartition && partition.type !== 'free_space') { + usedSpace += partition.usage || 0; + } + }); + return Math.round(100 * (100 - usedSpace)) / 100; + } + + removePartition(partition) { + const index = this.diskConfig.partitions.indexOf(partition); + if (index !== -1) { + this.diskConfig.partitions.splice(index, 1); + } + + this.setChartData(this.diskConfig); + + } + + +// var RC='@'; +// document.fdatosejecucion.atributos.value="scp="+escape(document.fdatos.codigo.value)+RC; + + + /**/ + generateOgInstruction() { + const self = this; + let initPartitionTable = 'ogCreatePartitionTable ' + this.diskConfig.disk + ' ' + this.diskConfig.parttable.type + '\n'; + initPartitionTable += 'ogEcho log session "[0] $MSG_HELP_ogCreatePartitions"\n'; + initPartitionTable += 'ogEcho session "[10] $MSG_HELP_ogUnmountAll ' + this.diskConfig.disk + '"\n'; + initPartitionTable += 'ogUnmountAll ' + this.diskConfig.disk + ' 2>/dev/null\n'; + initPartitionTable += 'ogUnmountCache\n'; + initPartitionTable += 'ogEcho session "[30] $MSG_HELP_ogUpdatePartitionTable ' + this.diskConfig.disk + '"\n'; + initPartitionTable += 'ogDeletePartitionTable ' + this.diskConfig.disk + '\n'; + initPartitionTable += 'ogUpdatePartitionTable ' + this.diskConfig.disk + '\n'; + + let createPartitions = 'ogEcho session "[60] $MSG_HELP_ogListPartitions ' + this.diskConfig.disk + '"\n'; + createPartitions += 'ogExecAndLog command session ogListPartitions ' + this.diskConfig.disk + '\n'; + + let cacheInstruction = ''; + let partitionList = ''; + let formatInstructions = ''; + this.diskConfig.partitions.forEach(function(partition, index) { + if (partition.type !== 'free_space') { + // La unica particion especial es la 4 que es cache, para el resto + if (!self.isCACHE(partition)) { + partitionList += ' ' + partition.type + ':' + partition.size; + if (self.isEXTENDED(partition)) { + for (let p = 0; p < partition.partitions.length; p++) { + partitionList += ' ' + partition.partitions[p].type + ':' + partition.partitions[p].size; + if (partition.partitions[p].format === true) { + formatInstructions += 'ogUnmount ' + self.diskConfig.disk + ' ' + (partition.partition + (partition.partitions[p].partition - 1)) + '\n'; + formatInstructions += 'ogFormat ' + self.diskConfig.disk + ' ' + (partition.partition + (partition.partitions[p].partition - 1)) + '\n'; + } + } + } + if (partition.format === true) { + formatInstructions += 'ogUnmount ' + self.diskConfig.disk + ' ' + partition.partition + '\n'; + formatInstructions += 'ogFormat ' + self.diskConfig.disk + ' ' + partition.partition + '\n'; + } + } else { + cacheInstruction = 'ogEcho session "[50] $MSG_HELP_ogCreateCache"\n'; + cacheInstruction += 'initCache ' + self.diskConfig.disk + ' ' + partition.size + ' NOMOUNT &>/dev/null\n'; + + if (partition.format === true) { + formatInstructions += 'ogUnmountCache\n'; + formatInstructions += 'ogFormatCache\n'; + } + } + } + }); + + createPartitions += 'ogEcho session "[70] $MSG_HELP_ogCreatePartitions ' + partitionList + '"\n'; + createPartitions += 'ogExecAndLog command ogCreatePartitions ' + this.diskConfig.disk + partitionList + '\n'; + createPartitions += 'ogEcho session "[80] $MSG_HELP_ogSetPartitionActive ' + this.diskConfig.disk + ' 1"\n'; + createPartitions += 'ogSetPartitionActive ' + this.diskConfig.disk + ' 1\n'; + createPartitions += 'ogEcho log session "[100] $MSG_HELP_ogListPartitions ' + this.diskConfig.disk + '"\n'; + createPartitions += 'ogUpdatePartitionTable ' + this.diskConfig.disk + '\n'; + createPartitions += 'ms-sys /dev/sda | grep unknow && ms-sys /dev/sda\n'; + createPartitions += 'ogExecAndLog command session log ogListPartitions ' + this.diskConfig.disk + '\n'; + + this.ogCommandsService.ogInstructions = initPartitionTable + cacheInstruction + createPartitions + formatInstructions; + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/common/directive/col-resizable.directive.ts b/admin/WebConsole3/frontend/src/app/pages/common/directive/col-resizable.directive.ts new file mode 100644 index 00000000..7c32f406 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/common/directive/col-resizable.directive.ts @@ -0,0 +1,81 @@ +import {Directive, DoCheck, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, Renderer2} from '@angular/core'; + + +class ResizeInfo { + width: number; + percent: number; +} + +@Directive({ + selector: '[col-resizable]' +}) +export class ColResizableDirective implements OnInit, DoCheck { + private el: ElementRef; + private start: any; + private pressed: boolean; + private startX: number; + private startWidth: any; + + @Input() + elements: any[]; + + @Output() + onResize = new EventEmitter<ResizeInfo>(); + private elementProperty: string; + + + constructor(el: ElementRef, private renderer: Renderer2) { + this.el = el; + + } + + ngOnInit() { + this.elementProperty = this.el.nativeElement.getAttribute('cr-update-property') || ''; + } + ngDoCheck(): void { + const table = this.el.nativeElement; + const trs = table.getElementsByTagName('tr'); + let tds = null; + + for (let i = 0; i < trs.length; i++) { + tds = trs[i].getElementsByTagName('td'); + if (tds.length > 0) { + for (let n = 0; n < tds.length; n++) { + + if (tds[n].getElementsByClassName('resizer').length === 0) { + const span = document.createElement('span'); + span.classList.add('resizer'); + tds[n].appendChild(span); + span.addEventListener('mousedown', (event) => { + this.start = event.target; + this.pressed = true; + this.startX = event.x; + this.startWidth = this.start.parentElement.offsetWidth; + this.initResizableColumns(); + }); + } + } + } + } + + } + + private initResizableColumns() { + this.renderer.listen('body', 'mousemove', (event) => { + if (this.pressed) { + const width = this.startWidth + (event.x - this.startX); + this.start.parentElement.style.width = width + 'px'; + const element = this.elements[this.start.parentElement.cellIndex]; + if (typeof element !== 'undefined' && this.elementProperty !== ''){ + element[this.elementProperty] = ((width / this.el.nativeElement.offsetWidth) * 100); + } + this.onResize.emit(element); + } + }) + this.renderer.listen('body', 'mouseup', (event) => { + if (this.pressed) { + this.pressed = false; + } + }); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-selected-clients/og-selected-clients.component.html b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-selected-clients/og-selected-clients.component.html index ebb52d60..91d187ff 100644 --- a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-selected-clients/og-selected-clients.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-selected-clients/og-selected-clients.component.html @@ -1,19 +1,19 @@ <div class="row"> - <div *ngFor="let client of selectedClients" class="col-md-2 col-xs-6 padding-5"> - <div class="info-box client" *ngIf="client && client.id"> + <div *ngFor="let client of selectedClients | keyvalue" class="col-md-2 col-xs-6 padding-5"> + <div class="info-box client" *ngIf="client.value && client.value.id"> <span style="position: absolute;"> <input icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" - class="selection-checkbox" [(ngModel)]="client.selected" - (change)="ogCommonService.selectClient(client, client.parent)"/> + class="selection-checkbox" [(ngModel)]="client.value.selected" + (change)="ogCommonService.selectClient(client.value, client.value.parent)"/> </span> <span class="info-box-icon"> <i class="fa fa-desktop"> </i> </span> <div class="info-box-content"> - <span class="info-box-text">{{client.name}}</span> - <span class="info-box-text">{{client.ip}}</span> - <span class="info-box-text">{{client.mac}}</span> + <span class="info-box-text">{{client.value.name}}</span> + <span class="info-box-text">{{client.value.ip}}</span> + <span class="info-box-text">{{client.value.mac}}</span> </div> <!-- /.info-box-content --> </div> diff --git a/admin/WebConsole3/frontend/src/app/pages/common/table-action/ng2-table-action.component.css b/admin/WebConsole3/frontend/src/app/pages/common/table-action/ng2-table-action.component.css index 4e8eee70..975006ac 100644 --- a/admin/WebConsole3/frontend/src/app/pages/common/table-action/ng2-table-action.component.css +++ b/admin/WebConsole3/frontend/src/app/pages/common/table-action/ng2-table-action.component.css @@ -1,3 +1,3 @@ -ul.dropdown-menu {
- background-color: transparent;
-}
+::ng-deep ng2-smart-table ul.dropdown-menu { + background-color: transparent; +} diff --git a/admin/WebConsole3/frontend/src/app/pages/dashboard/dashboard.component.ts b/admin/WebConsole3/frontend/src/app/pages/dashboard/dashboard.component.ts index 97d26ae1..ef11a47f 100644 --- a/admin/WebConsole3/frontend/src/app/pages/dashboard/dashboard.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/dashboard/dashboard.component.ts @@ -87,11 +87,13 @@ export class DashboardComponent implements OnInit, OnDestroy { this.ogCommonService.loadEngineConfig().subscribe( (config) => { this.timers = config.timers; - if (this.timers.serverStatusInterval.object == null) { + if (this.timers.serverStatusInterval.object == null && this.timers.serverStatusInterval.tick > 0) { this.updateStatus(); this.timers.serverStatusInterval.object = setInterval(() => { this.updateStatus(); }, this.timers.serverStatusInterval.tick); + } else { + this.updateStatus(); } }, (error) => { diff --git a/admin/WebConsole3/frontend/src/app/pages/hardware-component/hardware-component.component.html b/admin/WebConsole3/frontend/src/app/pages/hardware-component/hardware-component.component.html index 0ddd6b5c..60c60cd1 100644 --- a/admin/WebConsole3/frontend/src/app/pages/hardware-component/hardware-component.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/hardware-component/hardware-component.component.html @@ -11,7 +11,7 @@ <div> <div class="col-md-12"> <div class="box-tools pull-right"> - <button class="btn btn-primary" translate="save" ng-click="vm.save(Form)"></button> + <button class="btn btn-primary" translate="save" (click)="save(Form)"></button> </div> </div> </div> @@ -21,13 +21,12 @@ <div class="col-md-12"> <div class="box box-primary"> <div class="box-header with-border"> - <h3 ng-if="!vm.hardwareComponent.id" class="box-title" translate="new_hardware_component"></h3> - <h3 ng-if="vm.hardwareComponent.id" class="box-title" translate="hardware_component"></h3> + <h3 *ngIf="!hardwareComponent.id" class="box-title" translate="new_hardware_component"></h3> + <h3 *ngIf="hardwareComponent.id" class="box-title" translate="hardware_component"></h3> </div> <div class="box-body"> - <form role="form" gbn-auto-form name="Form" form-options="vm.formOptions" form-model="vm.hardwareComponent"> + <app-form-input [model]="hardwareComponent" [formType]="formType"></app-form-input> - </form> </div> </div> </div> diff --git a/admin/WebConsole3/frontend/src/app/pages/hardware-component/hardware-component.component.ts b/admin/WebConsole3/frontend/src/app/pages/hardware-component/hardware-component.component.ts index a7ef127f..4b4306af 100644 --- a/admin/WebConsole3/frontend/src/app/pages/hardware-component/hardware-component.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/hardware-component/hardware-component.component.ts @@ -1,17 +1,48 @@ -import { Component } from '@angular/core';
-
-import { HardwareComponentService } from 'src/app/api/hardware-component.service';
-import { HardwareComponent } from 'src/app/model/hardware-component';
-
-@Component({
- selector: 'app-hardware-component',
- templateUrl: './hardware-component.component.html',
- styleUrls: [ './hardware-component.component.scss' ]
-})
-export class HardwareComponentComponent {
- // this tells the tabs component which Pages
- // should be each tab's root Page
- constructor(public hardwareComponentService: HardwareComponentService) {
- }
-
-}
+import { Component } from '@angular/core'; + +import { HardwareComponentService } from 'src/app/api/hardware-component.service'; +import { HardwareComponent } from 'src/app/model/hardware-component'; +import {Observable} from 'rxjs'; +import {ToasterService} from '../../service/toaster.service'; +import {TranslateService} from '@ngx-translate/core'; +import {Router} from '@angular/router'; + +@Component({ + selector: 'app-hardware-component', + templateUrl: './hardware-component.component.html', + styleUrls: [ './hardware-component.component.scss' ] +}) +export class HardwareComponentComponent { + // this tells the tabs component which Pages + public hardwareComponent: HardwareComponent; + formType: any; + // should be each tab's root Page + constructor(public hardwareComponentService: HardwareComponentService, private toaster: ToasterService, private translate: TranslateService, private router: Router) { + this.hardwareComponent = new HardwareComponent(); + this.formType = [{ + field: 'description', + name: 'description', + label: 'description', + type: 'textarea' + }]; + } + + save() { + let request: Observable<HardwareComponent>; + if (this.hardwareComponent.id !== 0) { + request = this.hardwareComponentService.update(this.hardwareComponent); + } else { + request = this.hardwareComponentService.create(this.hardwareComponent); + } + + request.subscribe( + (response) => { + this.toaster.pop({type: this.translate.instant('success'), title: this.translate.instant('success'), body: this.translate.instant('successfully_saved')}); + this.router.navigate(['/app/hardware']); + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/image/image.component.ts b/admin/WebConsole3/frontend/src/app/pages/image/image.component.ts index 821f63a4..76629163 100644 --- a/admin/WebConsole3/frontend/src/app/pages/image/image.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/image/image.component.ts @@ -1,146 +1,146 @@ -import {Component, OnInit} from '@angular/core';
-
-import { ImageService } from 'src/app/api/image.service';
-import {Image, PartitionInfo} from 'src/app/model/image';
-import {OgCommonService} from '../../service/og-common.service';
-import {TranslateService} from '@ngx-translate/core';
-import {ToasterService} from '../../service/toaster.service';
-import {OgSweetAlertService} from '../../service/og-sweet-alert.service';
-import {Ng2TableActionComponent} from '../common/table-action/ng2-table-action.component';
-import {Router} from '@angular/router';
-
-@Component({
- selector: 'app-image',
- templateUrl: './image.component.html',
- styleUrls: [ './image.component.scss' ]
-})
-export class ImageComponent implements OnInit {
- images: Image[];
- constants: any;
- removeFile = false;
- tableSettings: any;
-
- // this tells the tabs component which Pages
- // should be each tab's root Page
- constructor(private router: Router, public imageService: ImageService, private ogCommonService: OgCommonService, private translate: TranslateService, private toaster: ToasterService, private ogSweetAlert: OgSweetAlertService) {
- this.ogCommonService.loadEngineConfig().subscribe(
- data => {
- this.constants = data.constants;
- }
- );
- }
-
- ngOnInit(): void {
- this.imageService.list().subscribe(
- data => {
- this.images = data;
- }
- );
- const self = this;
- this.tableSettings = {
- columns: {
- canonicalName: {
- title: this.translate.instant('canonical_name')
- },
- description: {
- title: this.translate.instant('description')
- },
- partitionInfo: {
- title: this.translate.instant('filesystem'),
- valuePrepareFunction: (cell, image) => {
- return this.getImageFileSystem(image);
- },
- filterFunction: (value: PartitionInfo, search: string) => {
- return (value.filesystem) ? value.filesystem.includes(search) : false;
- }
-
- },
- createdAt: {
- title: this.translate.instant('createdAt')
- },
- options: {
- title: 'Options',
- filter: false,
- sort: false,
- type: 'custom',
- renderComponent: Ng2TableActionComponent,
- onComponentInitFunction(instance) {
- instance.edit.subscribe(row => {
- self.router.navigate(['/app/images/edit/', row.id]);
- });
- instance.delete.subscribe(row => {
- self.deleteImage(row);
- });
- }
- },
- },
- actions: {
- position: 'right',
- add: false,
- edit: false,
- delete: false
- }
- };
- }
-
- getImageFileSystem(image) {
- const result = '';
- if (typeof image.partitionInfo === 'string') {
- image.partitionInfo = JSON.parse(image.partitionInfo);
- } else if (!image.partitionInfo) {
- image.partitionInfo = {};
- }
- return image.partitionInfo.filesystem;
- }
-
-
- getPartitionType(partition) {
- // buscar la particion en el array global
- let result = this.constants.partitionTypes.filter(function(obj) { return obj.id === partition.id; });
- result = result[0];
- return result.type;
- }
-
- deleteImage(image) {
- const self = this;
- this.removeFile = false;
- this.ogSweetAlert.swal({
- title: this.translate.instant('sure_to_delete') + '?',
- html: '<form style="text-align: center; padding-left: 10px">\
- <div class="form-group" translate="action_cannot_be_undone"></div>\
- <div class="form-group">\
- <div class="checkbox clip-check check-primary checkbox-inline">\
- <input id="removeFile" icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" [(ngModel)]="removeFile" />\
- </div>\
- <label for="removeFile" translate="remove_file">\
- </label>?\
- </div>\
- </form>',
- type: 'warning',
- showCancelButton: true,
- confirmButtonColor: '#3c8dbc',
- confirmButtonText: this.translate.instant('yes_delete'),
- closeOnConfirm: true
- }).then(
- function(result) {
- if (result === true) {
- if (self.removeFile === true) {
- // TODO Borrar fichero físico...
- }
- this.imageService.delete(image.id).then(
- function(response) {
- this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_deleted')});
- // Buscar el elemento en el array y borrarlo
- const index = this.images.indexOf(image);
- if (index !== -1) {
- this.images.splice(index, 1);
- }
- },
- function(error) {
- this.toaster.pop({type: 'error', title: 'error', body: error});
- }
- );
- }
- });
- }
-}
+import {Component, OnInit} from '@angular/core'; + +import { ImageService } from 'src/app/api/image.service'; +import {Image, PartitionInfo} from 'src/app/model/image'; +import {OgCommonService} from '../../service/og-common.service'; +import {TranslateService} from '@ngx-translate/core'; +import {ToasterService} from '../../service/toaster.service'; +import {OgSweetAlertService} from '../../service/og-sweet-alert.service'; +import {Ng2TableActionComponent} from '../common/table-action/ng2-table-action.component'; +import {Router} from '@angular/router'; + +@Component({ + selector: 'app-image', + templateUrl: './image.component.html', + styleUrls: [ './image.component.scss' ] +}) +export class ImageComponent implements OnInit { + images: Image[]; + constants: any; + removeFile = false; + tableSettings: any; + + // this tells the tabs component which Pages + // should be each tab's root Page + constructor(private router: Router, public imageService: ImageService, private ogCommonService: OgCommonService, private translate: TranslateService, private toaster: ToasterService, private ogSweetAlert: OgSweetAlertService) { + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.constants = data.constants; + } + ); + } + + ngOnInit(): void { + this.imageService.list().subscribe( + data => { + this.images = data; + } + ); + const self = this; + this.tableSettings = { + columns: { + canonicalName: { + title: this.translate.instant('canonical_name') + }, + description: { + title: this.translate.instant('description') + }, + partitionInfo: { + title: this.translate.instant('filesystem'), + valuePrepareFunction: (cell, image) => { + return this.getImageFileSystem(image); + }, + filterFunction: (value: PartitionInfo, search: string) => { + return (value.filesystem) ? value.filesystem.includes(search) : false; + } + + }, + createdAt: { + title: this.translate.instant('createdAt') + }, + options: { + title: 'Options', + filter: false, + sort: false, + type: 'custom', + renderComponent: Ng2TableActionComponent, + onComponentInitFunction(instance) { + instance.edit.subscribe(row => { + self.router.navigate(['/app/images/edit/', row.id]); + }); + instance.delete.subscribe(row => { + self.deleteImage(row); + }); + } + }, + }, + actions: { + position: 'right', + add: false, + edit: false, + delete: false + } + }; + } + + getImageFileSystem(image) { + const result = ''; + if (typeof image.partitionInfo === 'string') { + image.partitionInfo = JSON.parse(image.partitionInfo); + } else if (!image.partitionInfo) { + image.partitionInfo = {}; + } + return image.partitionInfo.filesystem; + } + + + getPartitionType(partition) { + // buscar la particion en el array global + let result = this.constants.partitionTypes.filter(function(obj) { return obj.id === partition.id; }); + result = result[0]; + return result.type; + } + + deleteImage(image) { + const self = this; + this.removeFile = false; + this.ogSweetAlert.swal({ + title: this.translate.instant('sure_to_delete') + '?', + html: '<form style="text-align: center; padding-left: 10px">\ + <div class="form-group" translate="action_cannot_be_undone"></div>\ + <div class="form-group">\ + <div class="checkbox clip-check check-primary checkbox-inline">\ + <input id="removeFile" icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" [(ngModel)]="removeFile" />\ + </div>\ + <label for="removeFile" translate="remove_file">\ + </label>?\ + </div>\ + </form>', + type: 'warning', + showCancelButton: true, + confirmButtonColor: '#3c8dbc', + confirmButtonText: this.translate.instant('yes_delete'), + closeOnConfirm: true + }).then( + function(result) { + if (result.value === true) { + if (self.removeFile === true) { + // TODO Borrar fichero físico... + } + self.imageService.delete(image.id).subscribe( + (response) => { + self.toaster.pop({type: 'success', title: 'success', body: self.translate.instant('successfully_deleted')}); + // Buscar el elemento en el array y borrarlo + const index = self.images.indexOf(image); + if (index !== -1) { + self.images.splice(index, 1); + } + }, + (error) => { + self.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + }); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.html b/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.html index 9d96cde9..dd147bf3 100644 --- a/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.html @@ -19,16 +19,19 @@ <!-- Main content --> <section class="content"> <div class="row"> - <div class="col-md-12"> + <div class="col-md-4"> <div class="box box-primary"> <div class="box-header with-border"> </div> <div class="box-body"> - <form role="form" gbn-auto-form name="Form" ng-submit="saveImage(Form)" form-options="formOptions" form-model="image"> + <form role="form" gbn-auto-form name="Form" form-model="image"> <app-form-input [model]="menu" [cols]="1" [formType]="form"></app-form-input> </form> </div> </div> </div> + <div class="col-md-8"> + <iframe class="e2e-trusted-url" [src]="sanitizer.bypassSecurityTrustResourceUrl(menu.publicUrl)" width="100%" height="500px"></iframe> + </div> </div> </section> diff --git a/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.ts b/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.ts index d52f53ad..c16bf372 100644 --- a/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.ts @@ -10,6 +10,7 @@ import {Observable} from 'rxjs'; import {MenuService} from '../../../api/menu.service'; import {Menu} from '../../../model/menu'; import {OgCommonService} from '../../../service/og-common.service'; +import {DomSanitizer} from '@angular/platform-browser'; @Component({ selector: 'app-menu', @@ -24,7 +25,7 @@ export class MenuEditComponent implements OnInit { // this tells the tabs component which Pages // should be each tab's root Page - constructor(private router: Router, private activatedRouter: ActivatedRoute, private ogCommonService: OgCommonService, private menuService: MenuService, private translate: TranslateService, private toaster: ToasterService) { + constructor(public sanitizer: DomSanitizer, private router: Router, private activatedRouter: ActivatedRoute, private ogCommonService: OgCommonService, private menuService: MenuService, private translate: TranslateService, private toaster: ToasterService) { this.form = this.formType.getForm(); } diff --git a/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.ts b/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.ts index 4c47eb73..2d803c86 100644 --- a/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.ts @@ -7,6 +7,7 @@ import {Ng2TableActionComponent} from '../common/table-action/ng2-table-action.c import {TranslateService} from '@ngx-translate/core'; import {Router} from '@angular/router'; import {OgSweetAlertService} from '../../service/og-sweet-alert.service'; +import {ToasterService} from '../../service/toaster.service'; @Component({ selector: 'app-menu', @@ -18,7 +19,7 @@ export class MenuComponent implements OnInit { private tableSettings: any; // this tells the tabs component which Pages // should be each tab's root Page - constructor(public menuService: MenuService, private router: Router, private ogSweetAlert: OgSweetAlertService, private translate: TranslateService) { + constructor(public menuService: MenuService, private router: Router, private ogSweetAlert: OgSweetAlertService, private toaster: ToasterService, private translate: TranslateService) { } ngOnInit(): void { @@ -82,19 +83,19 @@ export class MenuComponent implements OnInit { closeOnConfirm: true }).then( function(result) { - if (result === true) { + if (result.value === true) { - this.menuService.delete(menu.id).then( - function(response) { - this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_deleted')}); + self.menuService.delete(menu.id).subscribe( + (response) => { + self.toaster.pop({type: 'success', title: 'success', body: self.translate.instant('successfully_deleted')}); // Buscar el elemento en el array y borrarlo - const index = this.images.indexOf(menu); + const index = self.menus.indexOf(menu); if (index !== -1) { - this.images.splice(menu, 1); + self.menus.splice(menu, 1); } }, - function(error) { - this.toaster.pop({type: 'error', title: 'error', body: error}); + (error) => { + self.toaster.pop({type: 'error', title: 'error', body: error}); } ); } diff --git a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/organizational-unit.component.ts b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/organizational-unit.component.ts index 8a59d77b..c020b7fc 100644 --- a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/organizational-unit.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/organizational-unit.component.ts @@ -55,7 +55,7 @@ export class OrganizationalUnitComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.ogCommonService.loadEngineConfig().subscribe( + this.ogCommonService.loadEngineConfig().subscribe( data => { this.config = data; @@ -74,7 +74,7 @@ export class OrganizationalUnitComponent implements OnInit, OnDestroy { (response) => { this.ous = Array.isArray(response) ? response : [response]; // La primera vez que entra - if (this.config.timers.clientsStatusInterval.object == null) { + if (this.config.timers.clientsStatusInterval.object == null && this.config.timers.clientsStatusInterval.tick > 0) { this.getClientStatus(); const self = this; this.config.timers.clientsStatusInterval.object = window.setInterval(function() { @@ -95,7 +95,6 @@ export class OrganizationalUnitComponent implements OnInit, OnDestroy { ); - } showGrid(show) { diff --git a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.html b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.html index 32b0a4db..02d60caa 100644 --- a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.html @@ -49,7 +49,7 @@ <!-- *ngIf="selectedStatus[client.status] == true" --> <div class="row"> <ng-container *ngFor="let client of ou.clients"> - <div class="col-md-2 col-xs-6 padding-5"> + <div class="col-md-2 col-xs-6 og-client-box"> <div class="info-box client " *ngIf="mustShow(client)"> <span style="position: absolute;"> <input icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" [(ngModel)]="client.selected" (change)="selectClient(client)" /> diff --git a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.scss b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.scss new file mode 100644 index 00000000..118db555 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.scss @@ -0,0 +1,11 @@ +.og-client-box { + padding: 0; + border: 1px solid lightgray; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + margin: 0 5px; + background: white; +} + +.og-client-box .info-box { + box-shadow: none; +} diff --git a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.ts b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.ts index 743ba965..0c5e8a14 100644 --- a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-clients/ou-client.component.ts @@ -9,7 +9,8 @@ import {OgCommonService} from '../../../service/og-common.service'; @Component({ selector: 'app-ou-client-component', - templateUrl: 'ou-client.component.html' + templateUrl: 'ou-client.component.html', + styleUrls: ['ou-client.component.scss'] }) export class OuClientComponent { private _ou: OrganizationalUnit; diff --git a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-group/ou-group.component.ts b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-group/ou-group.component.ts index 9d04c84e..e2f3d005 100644 --- a/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-group/ou-group.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/organizational-unit/ou-group/ou-group.component.ts @@ -17,8 +17,20 @@ import {Router} from '@angular/router'; styleUrls: ['ou-group.component.css'] }) export class OuGroupComponent { + private _ou: OrganizationalUnit; @Input() ous; - @Input() content; + @Input() + set content(ou) { + this._ou = ou; + this._ou.clients.forEach((client) => { + if (this.ogCommonService.selectedClients[client.id]) { + client.selected = true; + } + }); + } + get content() { + return this._ou; + } @Input() clientStatus; @Input() showGrid; @Input() selectedStatus; diff --git a/admin/WebConsole3/frontend/src/app/pages/profile/profile.component.html b/admin/WebConsole3/frontend/src/app/pages/profile/profile.component.html index 97761b02..2f1b7bf5 100644 --- a/admin/WebConsole3/frontend/src/app/pages/profile/profile.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/profile/profile.component.html @@ -16,6 +16,7 @@ </div> </section> <section class="content"> + {{user.preferences|json}} <div class="row"> <div class="col-md-12"> <div class="box box-primary"> @@ -26,7 +27,7 @@ <form role="form"> <div class="form-group col-md-2"> <label translate="username"></label> - <input class="form-control" readonly="readonly" type="text" [(ngModel)]="user.username" name="username"> + <input class="form-control" readonly="readonly" type="text" [value]="user.username" name="username"> </div> <div class="form-group col-md-4"> <label translate="password"></label> @@ -52,11 +53,11 @@ <label class="help-block" translate="display_as"></label> <div class="row"> <div class="form-group col-md-1"> - <input icheck type="radio" name="displayGrid" checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" class="selection-checkbox" [(ngModel)]="user.preferences.ous.showGrid" value="true" /> + <input icheck type="radio" name="displayGrid" checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" class="selection-checkbox" [(ngModel)]="user.preferences.ous.showGrid" [value]="true" /> <label translate="grid"></label> </div> <div class="form-group col-md-1"> - <input icheck type="radio" name="displayTable" checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" class="selection-checkbox" [(ngModel)]="user.preferences.ous.showGrid" value="false" /> + <input icheck type="radio" name="displayTable" checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" class="selection-checkbox" [(ngModel)]="user.preferences.ous.showGrid" [value]="false" /> <label translate="table"></label> </div> </div> @@ -66,7 +67,7 @@ <div class="row"> <div class="form-group col-md-2"> <label translate="language"></label> - <select class="form-control" name="language" [(ngModel)]="user.preferences.language" (change)="ogCommonService.changeLanguage($event)"> + <select class="form-control" name="language" [(ngModel)]="user.preferences.language" (ngModelChange)="ogCommonService.changeLanguage(user.preferences.language)"> <option *ngFor="let lang of constants.languages" [ngValue]="lang.id" >{{lang.name}}</option> </select> </div> diff --git a/admin/WebConsole3/frontend/src/app/pages/profile/profile.component.ts b/admin/WebConsole3/frontend/src/app/pages/profile/profile.component.ts index 149ad6f3..da6859c2 100644 --- a/admin/WebConsole3/frontend/src/app/pages/profile/profile.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/profile/profile.component.ts @@ -42,7 +42,6 @@ export class ProfileComponent implements OnInit { } changeTheme() { - this.app.theme = this.user.preferences.theme; this.layoutStore.setSkin(this.user.preferences.theme); } diff --git a/admin/WebConsole3/frontend/src/app/pages/repository/repository.component.html b/admin/WebConsole3/frontend/src/app/pages/repository/repository.component.html index 38f08ade..a2d0cf28 100644 --- a/admin/WebConsole3/frontend/src/app/pages/repository/repository.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/repository/repository.component.html @@ -1,100 +1,100 @@ -<section class="content-header">
- <h1 translate="repositories">
- </h1>
- <ol class="breadcrumb">
- <li><a [routerLink]="'/app/dashboard'"><i class="fa fa-dashboard"></i> {{'dashboard'|translate}}</a></li>
- <li class="active" translate="repositories"></li>
- </ol>
-</section>
-<section fixed-toolboxbar class="toolboxbar">
- <div >
- <div class="col-md-12">
- <div class="input-group">
- <input type="text" name="q" class="form-control" placeholder="Search..." ng-model="searchText">
- <span class="input-group-btn">
- <button type="button" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i>
- </button>
- </span>
- </div>
- </div>
- <div class="col-md-12" style="margin-top: 5px;margin-bottom: 5px;">
- <div class="box-tools pull-right">
- <button class="btn btn-default" (click)="newRepository()" translate="new_repo"></button>
- </div>
- </div>
- </div>
-</section>
-<section class="content">
- <div class="row">
- <mk-box [header]="repository.name" [isLoading]="isRepositoryLoading()" [isCollapsable]="true" [isRemovable]="false" boxColor="primary" *ngFor="let repository of repositories" class="col-md-6">
- <div class="box-body">
- <form #form>
- <app-form-input [formType]="formType" [model]="repository" [cols]="2"></app-form-input>
- </form>
- <div class="box-footer">
-
- <button type="button" class="btn btn-primary" translate="save" (click)="saveRepository(Form, repository)"></button>
- <button type="button" class="btn btn-danger pull-right" (click)="deleteRepository(repository)" translate="delete">
- </button>
- </div>
- </div>
- <div class="box-footer">
- <mk-box header="{{'filesystem_info'|translate}}" [isCollapsable]="true" [isRemovable]="false" contentClasses="table-responsive">
- <button type="button" [disabled]="repository.password.length == 0" class="btn btn-primary pull-right" (click)="refreshRepoInfo(repository)">
- <i class="fa fa-refresh"></i> <span translate="refresh_info"></span>
- </button>
- <div class="box-body no-padding" >
-
- <div translate="connection_not_available" *ngIf="!repository.info"></div>
- <table class="table table-condensed table-striped" *ngIf="repository.info">
- <tbody>
- <tr>
- <th colspan="5" translate="disk_info"></th>
- </tr>
- <tr>
- <th translate="partition"></th>
- <th translate="total_disk"></th>
- <th translate="used_disk"></th>
- <th translate="free_disk"></th>
- <th translate="busy_percent_disk"></th>
- </tr>
- <tr>
- <td>{{repository.info.disk[0].partition}}</td>
- <td>{{repository.info.disk[0].total}}</td>
- <td>{{repository.info.disk[0].used}}</td>
- <td>
- {{repository.info.disk[0].free}}
- </td>
- <td>
- <span class="badge" ng-class="{'bg-green': repository.info.disk[0].percent.split('%')[0] < 60, 'bg-red': repository.info.disk[0].percent.split('%')[0] > 85, 'bg-yellow': repository.info.disk[0].percent.split('%')[0] >= 60 && repository.info.disk[0].percent.split('%')[0] <= 85}">{{repository.info.disk[0].percent}}
- </span>
- </td>
- </tr>
- </tbody>
- <tbody>
- <tr>
- <th colspan="2" translate="images_info"></th>
- </tr>
- <tr>
- <th translate="name"></th>
- <th translate="size"></th>
- <th translate="delete"></th>
- </tr>
- </tbody>
- <tbody ng-repeat="fileGroup in repository.info.files">
- <tr ng-repeat="file in fileGroup | orderBy: 'name'">
- <td>{{file.name}}</td>
- <td>{{OGCommonService.getUnits(file.size)}}</td>
- <td>
- <a *ngIf="isImageFile(file)" class="btn btn-danger" href="javascript:void(0)" translate="delete" (click)="deleteImageFile(file)"></a>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </mk-box>
- </div>
- <!-- /.box-body -->
- </mk-box>
- </div>
-</section>
+<section class="content-header"> + <h1 translate="repositories"> + </h1> + <ol class="breadcrumb"> + <li><a [routerLink]="'/app/dashboard'"><i class="fa fa-dashboard"></i> {{'dashboard'|translate}}</a></li> + <li class="active" translate="repositories"></li> + </ol> +</section> +<section fixed-toolboxbar class="toolboxbar"> + <div > + <div class="col-md-12"> + <div class="input-group"> + <input type="text" name="q" class="form-control" placeholder="Search..." ng-model="searchText"> + <span class="input-group-btn"> + <button type="button" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i> + </button> + </span> + </div> + </div> + <div class="col-md-12" style="margin-top: 5px;margin-bottom: 5px;"> + <div class="box-tools pull-right"> + <button class="btn btn-default" (click)="newRepository()" translate="new_repo"></button> + </div> + </div> + </div> +</section> +<section class="content"> + <div class="row"> + <mk-box [header]="repository.name" [isLoading]="isRepositoryLoading()" [isCollapsable]="true" [isRemovable]="false" boxColor="primary" *ngFor="let repository of repositories" class="col-md-6"> + <div class="box-body"> + <form #form> + <app-form-input [formType]="formType" [model]="repository" [cols]="1"></app-form-input> + </form> + <div class="box-footer"> + + <button type="button" class="btn btn-primary" translate="save" (click)="saveRepository(Form, repository)"></button> + <button type="button" class="btn btn-danger pull-right" (click)="deleteRepository(repository)" translate="delete"> + </button> + </div> + </div> + <div class="box-footer"> + <mk-box header="{{'filesystem_info'|translate}}" [isCollapsable]="true" [isRemovable]="false" contentClasses="table-responsive"> + <button type="button" [disabled]="repository.password.length == 0" class="btn btn-primary pull-right" (click)="refreshRepoInfo(repository)"> + <i class="fa fa-refresh"></i> <span translate="refresh_info"></span> + </button> + <div class="box-body no-padding" > + + <div translate="connection_not_available" *ngIf="!repository.info"></div> + <table class="table table-condensed table-striped" *ngIf="repository.info"> + <tbody> + <tr> + <th colspan="5" translate="disk_info"></th> + </tr> + <tr> + <th translate="partition"></th> + <th translate="total_disk"></th> + <th translate="used_disk"></th> + <th translate="free_disk"></th> + <th translate="busy_percent_disk"></th> + </tr> + <tr> + <td>{{repository.info.disk[0].partition}}</td> + <td>{{repository.info.disk[0].total}}</td> + <td>{{repository.info.disk[0].used}}</td> + <td> + {{repository.info.disk[0].free}} + </td> + <td> + <span class="badge" ng-class="{'bg-green': repository.info.disk[0].percent.split('%')[0] < 60, 'bg-red': repository.info.disk[0].percent.split('%')[0] > 85, 'bg-yellow': repository.info.disk[0].percent.split('%')[0] >= 60 && repository.info.disk[0].percent.split('%')[0] <= 85}">{{repository.info.disk[0].percent}} + </span> + </td> + </tr> + </tbody> + <tbody> + <tr> + <th colspan="2" translate="images_info"></th> + </tr> + <tr> + <th translate="name"></th> + <th translate="size"></th> + <th translate="delete"></th> + </tr> + </tbody> + <tbody ng-repeat="fileGroup in repository.info.files"> + <tr ng-repeat="file in fileGroup | orderBy: 'name'"> + <td>{{file.name}}</td> + <td>{{OGCommonService.getUnits(file.size)}}</td> + <td> + <a *ngIf="isImageFile(file)" class="btn btn-danger" href="javascript:void(0)" translate="delete" (click)="deleteImageFile(file)"></a> + </td> + </tr> + </tbody> + </table> + </div> + </mk-box> + </div> + <!-- /.box-body --> + </mk-box> + </div> +</section> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software.component.css b/admin/WebConsole3/frontend/src/app/pages/software/software.component.scss index e69de29b..e69de29b 100644 --- a/admin/WebConsole3/frontend/src/app/pages/software/software.component.css +++ b/admin/WebConsole3/frontend/src/app/pages/software/software.component.scss diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software.component.ts b/admin/WebConsole3/frontend/src/app/pages/software/software.component.ts index a51d9560..59b81298 100644 --- a/admin/WebConsole3/frontend/src/app/pages/software/software.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/software/software.component.ts @@ -11,7 +11,7 @@ import {SoftwareTypeService} from '../../api/software-type.service'; @Component({ selector: 'app-software', templateUrl: './software.component.html', - styleUrls: ['./software.component.css'] + styleUrls: ['./software.component.scss'] }) export class SoftwareComponent implements OnInit { public softwareProfileGroups: any[] = []; diff --git a/admin/WebConsole3/frontend/src/app/pages/trace/trace.component.ts b/admin/WebConsole3/frontend/src/app/pages/trace/trace.component.ts index af1b2cf7..750e5096 100644 --- a/admin/WebConsole3/frontend/src/app/pages/trace/trace.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/trace/trace.component.ts @@ -1,261 +1,263 @@ -import {Component, OnDestroy, OnInit} from '@angular/core';
-
-import { TraceService } from 'src/app/api/trace.service';
-import { Trace } from 'src/app/model/trace';
-import {ToasterService} from '../../service/toaster.service';
-import {OgSweetAlertService} from '../../service/og-sweet-alert.service';
-import {Router} from '@angular/router';
-import {OgCommonService} from '../../service/og-common.service';
-import {environment} from '../../../environments/environment';
-import {TranslateService} from '@ngx-translate/core';
-import {QueryOptions} from 'globunet-angular/core/providers/api/query-options';
-import {forkJoin} from 'rxjs';
-
-import * as moment from 'moment';
-
-@Component({
- selector: 'app-trace',
- templateUrl: './trace.component.html',
- styleUrls: [ './trace.component.scss' ]
-})
-export class TraceComponent implements OnInit, OnDestroy {
- public traces = [];
- public selection = [];
- public filters = {
- searchText: '',
- status: {
- 'finished': {
- name: 'finished',
- selected: true
- },
- 'execution': {
- name: 'execution',
- selected: true
- }
- },
- finishedStatus: {
- 'noErrors': {
- name: 'no-errors',
- selected: true
- },
- 'withErrors': {
- name: 'with-errors',
- selected: true
- },
- },
- dateRange: {
- startDate: null,
- endDate: null
- }
- };
- config: { constants: any; timers: any; };
-
- private datePickerOptions: { timePickerIncrement: number; timePicker: boolean; format: string; timePicker24Hour: boolean; locale: { fromLabel: any; toLabel: any; cancelLabel: any; firstDay: number; applyLabel: any; format: string; daysOfWeek: any[]; separator: string; customRangeLabel: any; weekLabel: string; monthNames: any[] } };
- private selectAll: any;
- private executionTasks: Trace[];
- public showInfo: string;
- // this tells the tabs component which Pages
- // should be each tab's root Page
- constructor(public traceService: TraceService,
- private ogCommonService: OgCommonService,
- private router: Router,
- private ogSweetAlert: OgSweetAlertService,
- private toaster: ToasterService,
- private translate: TranslateService) {
- }
-
- ngOnDestroy() {
- if (this.config.timers && this.config.timers.executionsInterval) {
- clearInterval(this.config.timers.executionsInterval.object);
- }
- }
-
-ngOnInit(): void {
- const self = this;
- this.ogCommonService.loadEngineConfig().subscribe(
- data => {
- this.config = data;
-
- if (this.config.timers.executionsInterval.object === null) {
- this.config.timers.executionsInterval.object = setInterval(function() {
- self.getExecutionTasks();
- }, this.config.timers.executionsInterval.tick);
- }
-
- this.datePickerOptions = {
- 'locale': {
- 'format': 'DD/MM/YYYY HH:mm',
- 'separator': ' - ',
- 'applyLabel': this.translate.instant('apply'),
- 'cancelLabel': this.translate.instant('cancel'),
- 'fromLabel': this.translate.instant('from'),
- 'toLabel': this.translate.instant('to'),
- 'customRangeLabel': this.translate.instant('custom_range'),
- 'weekLabel': 'W',
- 'daysOfWeek': [
- this.translate.instant('sun'),
- this.translate.instant('mon'),
- this.translate.instant('tue'),
- this.translate.instant('wed'),
- this.translate.instant('thu'),
- this.translate.instant('fri'),
- this.translate.instant('sat')
- ],
- 'monthNames': [
- this.translate.instant('january'),
- this.translate.instant('february'),
- this.translate.instant('march'),
- this.translate.instant('april'),
- this.translate.instant('may'),
- this.translate.instant('june'),
- this.translate.instant('july'),
- this.translate.instant('august'),
- this.translate.instant('september'),
- this.translate.instant('october'),
- this.translate.instant('november'),
- this.translate.instant('december')
- ],
- 'firstDay': 1
- },
- timePicker: true,
- timePickerIncrement: 30,
- timePicker24Hour: true,
- format: 'DD/MM/YYYY HH:mm'
- };
- this.traceService.list().subscribe(
- (response) => {
- this.traces = response;
- },
- (error) => {
- this.toaster.pop({type: 'error', title: 'error', body: error});
- }
- );
- }
- );
-}
-
-
- selectTrace(trace) {
- const index = this.selection.indexOf(trace);
- if (trace.selected === true && index === -1) {
- this.selection.push(trace);
- } else if (trace.selected === false && index !== -1) {
- this.selection.splice(index, 1);
- }
- }
-
- selectAllTraces() {
- const filter = this.traces.filter(function(trace: Trace) {
- return true;
- });
- for (let index = 0; index < filter.length; index++) {
- filter[index].selected = this.selectAll;
- this.selectTrace(filter[index]);
- }
- }
-
- relaunchTraces() {
-
- }
-
- deleteTraces() {
- this.ogSweetAlert.question( this.translate.instant('sure_to_delete') + '?', this.translate.instant('action_cannot_be_undone'), function(response) {
- const promises = [];
- for (let index = 0; index < this.selection.length; index++) {
- promises.push(this.traceService.delete(this.selection[index].id));
- }
- forkJoin(promises).subscribe(
- (success) => {
- this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_deleted')});
- this.selectAll = false;
- this.selection = [];
- this.searchText = '';
- },
- (error) => {
- this.toaster.pop({type: 'error', title: 'error', body: error});
- }
- );
- });
- }
-
- getExecutionTasks() {
- this.traceService.list(new QueryOptions({finished: 0})).subscribe(
- (result) => {
- this.executionTasks = result;
- },
- (error) => {
-
- }
- );
- }
-
- deleteExecutionTace(task) {
- this.ogSweetAlert.question(
- this.translate.instant('delete_task'),
- this.translate.instant('sure_to_delete_task') + '?',
- function(result) {
- if (result) {
- this.traceService.delete(task.id).subscribe(
- (response) => {
- this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_deleted')});
- this.getExecutionTasks();
- },
- (error) => {
- this.toaster.pop({type: 'error', title: 'error', body: error});
- }
- );
-
- }
- }
- );
-
- }
-
- relaunchExecutionTask(task) {
- this.ogSweetAlert.question(
- this.translate.instant('relaunch_task'),
- this.translate.instant('sure_to_relaunch_task') + '?',
- function(result) {
- if (result) {
-
- }
- }
- );
- }
-
- filterTraceStatus(trace, index, array) {
-
- // Comprobar si para el filtro de estado actual de la traza
- let result = (trace.finishedAt != null && this.filters.status['finished'].selected === true) || (trace.finishedAt === null && this.filters.status['execution'].selected === true);
- result = result && (trace.finishedAt != null && (trace.status === 0 && this.filters.finishedStatus['noErrors'].selected === true) || (trace.status !== 0 && this.filters.finishedStatus['withErrors'].selected === true));
- if (this.filters.dateRange.startDate != null) {
- result = result && moment(trace.executedAt).isAfter(this.filters.dateRange.startDate);
- }
- if (this.filters.dateRange.endDate != null) {
- result = result && moment(trace.executedAt).isBefore(this.filters.dateRange.endDate);
- }
-
- return result;
- }
-
-
- filteredTraces() {
- const self = this;
- return this.traces.filter(function(trace, index, array) {
- return self.filterTraceStatus(trace, index, array);
- });
- }
-
- getTraceCssClass(trace: any) {
- let result = '';
- if (!trace.finishedAt) {
- result = 'fa-warning text-yellow';
- }
- if (trace.status === 0) {
- result += ' fa-check-circle text-green';
- } else {
- result += ' fa-times-circle text-red';
- }
- return result;
- }
-}
+import {Component, OnDestroy, OnInit} from '@angular/core'; + +import { TraceService } from 'src/app/api/trace.service'; +import { Trace } from 'src/app/model/trace'; +import {ToasterService} from '../../service/toaster.service'; +import {OgSweetAlertService} from '../../service/og-sweet-alert.service'; +import {Router} from '@angular/router'; +import {OgCommonService} from '../../service/og-common.service'; +import {environment} from '../../../environments/environment'; +import {TranslateService} from '@ngx-translate/core'; +import {QueryOptions} from 'globunet-angular/core/providers/api/query-options'; +import {forkJoin} from 'rxjs'; + +import * as moment from 'moment'; + +@Component({ + selector: 'app-trace', + templateUrl: './trace.component.html', + styleUrls: [ './trace.component.scss' ] +}) +export class TraceComponent implements OnInit, OnDestroy { + public traces = []; + public selection = []; + public searchText = ''; + public filters = { + searchText: '', + status: { + 'finished': { + name: 'finished', + selected: true + }, + 'execution': { + name: 'execution', + selected: true + } + }, + finishedStatus: { + 'noErrors': { + name: 'no-errors', + selected: true + }, + 'withErrors': { + name: 'with-errors', + selected: true + }, + }, + dateRange: { + startDate: null, + endDate: null + } + }; + config: { constants: any; timers: any; }; + + private datePickerOptions: { timePickerIncrement: number; timePicker: boolean; format: string; timePicker24Hour: boolean; locale: { fromLabel: any; toLabel: any; cancelLabel: any; firstDay: number; applyLabel: any; format: string; daysOfWeek: any[]; separator: string; customRangeLabel: any; weekLabel: string; monthNames: any[] } }; + private selectAll: any; + private executionTasks: Trace[]; + public showInfo: string; + // this tells the tabs component which Pages + // should be each tab's root Page + constructor(public traceService: TraceService, + private ogCommonService: OgCommonService, + private router: Router, + private ogSweetAlert: OgSweetAlertService, + private toaster: ToasterService, + private translate: TranslateService) { + } + + ngOnDestroy() { + if (this.config.timers && this.config.timers.executionsInterval) { + clearInterval(this.config.timers.executionsInterval.object); + } + } + +ngOnInit(): void { + const self = this; + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.config = data; + + if (this.config.timers.executionsInterval.object === null && this.config.timers.executionsInterval.tick > 0) { + this.config.timers.executionsInterval.object = setInterval(function() { + self.getExecutionTasks(); + }, this.config.timers.executionsInterval.tick); + } + + this.datePickerOptions = { + 'locale': { + 'format': 'DD/MM/YYYY HH:mm', + 'separator': ' - ', + 'applyLabel': this.translate.instant('apply'), + 'cancelLabel': this.translate.instant('cancel'), + 'fromLabel': this.translate.instant('from'), + 'toLabel': this.translate.instant('to'), + 'customRangeLabel': this.translate.instant('custom_range'), + 'weekLabel': 'W', + 'daysOfWeek': [ + this.translate.instant('sun'), + this.translate.instant('mon'), + this.translate.instant('tue'), + this.translate.instant('wed'), + this.translate.instant('thu'), + this.translate.instant('fri'), + this.translate.instant('sat') + ], + 'monthNames': [ + this.translate.instant('january'), + this.translate.instant('february'), + this.translate.instant('march'), + this.translate.instant('april'), + this.translate.instant('may'), + this.translate.instant('june'), + this.translate.instant('july'), + this.translate.instant('august'), + this.translate.instant('september'), + this.translate.instant('october'), + this.translate.instant('november'), + this.translate.instant('december') + ], + 'firstDay': 1 + }, + timePicker: true, + timePickerIncrement: 30, + timePicker24Hour: true, + format: 'DD/MM/YYYY HH:mm' + }; + this.traceService.list().subscribe( + (response) => { + this.traces = response; + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + ); +} + + + selectTrace(trace) { + const index = this.selection.indexOf(trace); + if (trace.selected === true && index === -1) { + this.selection.push(trace); + } else if (trace.selected === false && index !== -1) { + this.selection.splice(index, 1); + } + } + + selectAllTraces() { + const filter = this.traces.filter(function(trace: Trace) { + return true; + }); + for (let index = 0; index < filter.length; index++) { + filter[index].selected = this.selectAll; + this.selectTrace(filter[index]); + } + } + + relaunchTraces() { + + } + + deleteTraces() { + const self = this; + this.ogSweetAlert.question( this.translate.instant('sure_to_delete') + '?', this.translate.instant('action_cannot_be_undone'), function(response) { + const promises = []; + for (let index = 0; index < self.selection.length; index++) { + promises.push(self.traceService.delete(self.selection[index].id)); + } + forkJoin(promises).subscribe( + (success) => { + self.toaster.pop({type: 'success', title: 'success', body: self.translate.instant('successfully_deleted')}); + self.selectAll = false; + self.selection = []; + self.searchText = ''; + }, + (error) => { + self.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + }); + } + + getExecutionTasks() { + this.traceService.list(new QueryOptions({finished: 0})).subscribe( + (result) => { + this.executionTasks = result; + }, + (error) => { + + } + ); + } + + deleteExecutionTace(task) { + this.ogSweetAlert.question( + this.translate.instant('delete_task'), + this.translate.instant('sure_to_delete_task') + '?', + function(result) { + if (result) { + this.traceService.delete(task.id).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_deleted')}); + this.getExecutionTasks(); + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + + } + } + ); + + } + + relaunchExecutionTask(task) { + this.ogSweetAlert.question( + this.translate.instant('relaunch_task'), + this.translate.instant('sure_to_relaunch_task') + '?', + function(result) { + if (result) { + + } + } + ); + } + + filterTraceStatus(trace, index, array) { + + // Comprobar si para el filtro de estado actual de la traza + let result = (trace.finishedAt != null && this.filters.status['finished'].selected === true) || (trace.finishedAt === null && this.filters.status['execution'].selected === true); + result = result && (trace.finishedAt != null && (trace.status === 0 && this.filters.finishedStatus['noErrors'].selected === true) || (trace.status !== 0 && this.filters.finishedStatus['withErrors'].selected === true)); + if (this.filters.dateRange.startDate != null) { + result = result && moment(trace.executedAt).isAfter(this.filters.dateRange.startDate); + } + if (this.filters.dateRange.endDate != null) { + result = result && moment(trace.executedAt).isBefore(this.filters.dateRange.endDate); + } + + return result; + } + + + filteredTraces() { + const self = this; + return this.traces.filter(function(trace, index, array) { + return self.filterTraceStatus(trace, index, array); + }); + } + + getTraceCssClass(trace: any) { + let result = ''; + if (!trace.finishedAt) { + result = 'fa-warning text-yellow'; + } + if (trace.status === 0) { + result += ' fa-check-circle text-green'; + } else { + result += ' fa-times-circle text-red'; + } + return result; + } +} diff --git a/admin/WebConsole3/frontend/src/app/service/og-commands.service.ts b/admin/WebConsole3/frontend/src/app/service/og-commands.service.ts index 14e80e84..1ea33faa 100644 --- a/admin/WebConsole3/frontend/src/app/service/og-commands.service.ts +++ b/admin/WebConsole3/frontend/src/app/service/og-commands.service.ts @@ -14,15 +14,11 @@ import {environment} from '../../environments/environment'; export class OGCommandsService { public ogInstructions = ''; public execution: any; - private commands = []; + private commands: any; constructor(private router: Router, private ogCommonService: OgCommonService, private toaster: ToasterService, private ogSweetAlert: OgSweetAlertService, private commandService: CommandService, private translate: TranslateService) { this.execution = {}; - this.ogCommonService.loadEngineConfig().subscribe( - (response) => { - this.commands = response.constants.commandtypes; - } - ); + this.commands = environment.commands; } sendCommand() { @@ -114,15 +110,19 @@ export class OGCommandsService { // Comprobar tipo de cada particion para ver si es clonable // var parttable = $rootScope.constants.partitiontable[client.partitions[0].partitionCode-1]; // buscar las particiones que sean clonables + const clonablePartitions = []; for (let index = 1; index < client.partitions.length; index++) { if (client.partitions[index].osName !== 'DATA' && client.partitions[index].osName !== '') { // Crear como nombre para mostrar, el disco y partición del sistema const obj = Object.assign({}, client.partitions[index]); - obj.name = 'disco: ' + obj.numDisk + ', part: ' + obj.numPartition + ', SO: ' + client.partitions[index].osName; - options.scope.partitions.push(obj); + const str = 'disco: ' + obj.numDisk + ', part: ' + obj.numPartition + ', SO: ' + client.partitions[index].osName; + clonablePartitions.push(obj.numDisk + ' ' + obj.numPartition) + options.scope.partitions.push(str); } } + const self = this; + this.ogSweetAlert.swal({ title: this.translate.instant('select_partition_to_inventary'), // text: $filter("translate")("action_cannot_be_undone"), @@ -137,9 +137,9 @@ export class OGCommandsService { function(result) { if (result.value) { // Montar el script con el disco y partición elegida - this.execution.script = this.commands.SOFTWARE_INVENTORY + ' ' + result.value; - this.loadClients(); - this.sendCommand(); + self.execution.script = self.commands.SOFTWARE_INVENTORY + ' ' + clonablePartitions[result.value]; + self.loadClients(); + self.sendCommand(); } }, null); diff --git a/admin/WebConsole3/frontend/src/app/service/og-common.service.ts b/admin/WebConsole3/frontend/src/app/service/og-common.service.ts index 8a2bbe56..b529199d 100644 --- a/admin/WebConsole3/frontend/src/app/service/og-common.service.ts +++ b/admin/WebConsole3/frontend/src/app/service/og-common.service.ts @@ -26,10 +26,13 @@ export class OgCommonService { constructor(private layoutStore: LayoutStore, private adminLteConfig: AdminLteConf, private engineService: EngineService, private translate: TranslateService, private authModule: AuthModule) { this.app = {}; - this.selectedClients = []; + this.selectedClients = {}; this.selectedOu = null; this.movingOu = null; this.movingClients = false; + if (localStorage.getItem('selectedClients')) { + this.selectedClients = JSON.parse(localStorage.getItem('selectedClients')); + } } loadEngineConfig(): Observable<{constants: any, timers: any}> { @@ -43,21 +46,22 @@ export class OgCommonService { themes: environment.themes, menus: environment.menus, languages: environment.languages, - deployMethods: environment.deployMethods + deployMethods: environment.deployMethods, + commands: environment.commands }; this.constants = Object.assign(this.constants, data[0]); // inicializar timers generales para refresco de información this.timers = { serverStatusInterval: { - tick: 5000, + tick: 0, object: null }, clientsStatusInterval: { - tick: 5000, + tick: 0, object: null }, executionsInterval: { - tick: 5000, + tick: 0, object: null }, @@ -190,6 +194,19 @@ export class OgCommonService { } else { delete this.selectedClients[client.id]; } + this.saveSelection(); + } + + saveSelection() { + if (Object.keys(this.selectedClients).length > 0) { + localStorage.setItem('selectedClients', JSON.stringify(this.selectedClients, function (key, value) { + let result = value; + if (key === 'parent' && typeof value === 'object') { + result = value.id; + } + return result; + })); + } } getSelectionSize() { diff --git a/admin/WebConsole3/frontend/src/styles.scss b/admin/WebConsole3/frontend/src/styles.scss index 65c2fd6d..b0a5eaec 100644 --- a/admin/WebConsole3/frontend/src/styles.scss +++ b/admin/WebConsole3/frontend/src/styles.scss @@ -19,6 +19,14 @@ table.table-no-border { border: none; } +.loader { + position: absolute; + top: 0; + width: 100%; + height: 100%; + z-index: 999999; +} + #loading-bar .bar { background: white; } @@ -100,8 +108,7 @@ i.client { } .bg-lab{ - background-color: #d7dbef; - padding: 10px 10px 0 10px; + background-color: #d7dbef !important; } tr.odd { @@ -182,7 +189,7 @@ table.disk-partitions td { } table.disk-partitions td span { - padding: 5px 10px; + padding: 5px 5px; font-weight: bold; } @@ -306,3 +313,7 @@ ul.dropdown-menu.fit { ng2-smart-table { font-size: initial !important; } + +.swal2-popup { + font-size: 1em !important; +} |