diff options
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; +} |