diff options
Diffstat (limited to 'admin/WebConsole3/frontend')
102 files changed, 5409 insertions, 1409 deletions
diff --git a/admin/WebConsole3/frontend/package.json b/admin/WebConsole3/frontend/package.json index fd197606..a2f353f5 100644 --- a/admin/WebConsole3/frontend/package.json +++ b/admin/WebConsole3/frontend/package.json @@ -34,6 +34,7 @@ "angular-loading-page": "^0.6.0", "bootstrap-css-only": "^3.3.7", "chart.js": "^2.7.3", + "chartjs-plugin-datalabels": "^0.6.0", "core-js": "^2.5.4", "font-awesome": "^4.7.0", "ionicons": "^4.5.5", @@ -58,8 +59,6 @@ "@types/jasminewd2": "~2.0.3", "@types/node": "~8.9.4", "codelyzer": "~4.3.0", - "globunet-angular": "git+https://gitlab.globunet.com/globunet/angular2-shared.git", - "globunet-schematics": "git+https://gitlab.globunet.com/globunet/globunet-angular-schematics.git", "jasmine-core": "~2.99.1", "jasmine-spec-reporter": "~4.2.1", "karma": "~3.0.0", diff --git a/admin/WebConsole3/frontend/src/app/api/command.service.ts b/admin/WebConsole3/frontend/src/app/api/command.service.ts index fe77bda3..28b18520 100644 --- a/admin/WebConsole3/frontend/src/app/api/command.service.ts +++ b/admin/WebConsole3/frontend/src/app/api/command.service.ts @@ -1,26 +1,26 @@ -import { Injectable } from '@angular/core';
-import { HttpClient} from '@angular/common/http';
-
-import { environment } from '../../environments/environment';
-import { Command } from '../model/command';
-import { CommandSerializer } from '../serializer/command.serializer';
-
-import {ResourceService} from 'globunet-angular/core/providers/api/resource.service';
-import {Observable} from 'rxjs';
-
-
-@Injectable({
- providedIn: 'root'
-})
-export class CommandService extends ResourceService<Command> {
-
- constructor(http: HttpClient) {
- super(http, environment.API_URL, 'commands', new CommandSerializer());
- }
-
- execute(params): Observable<any> {
- const url = this.url + 'executes.json';
- return this.httpClient.post(url, params);
- }
-
-}
+import {Injectable} from '@angular/core'; +import {HttpClient} from '@angular/common/http'; + +import {environment} from '../../environments/environment'; +import {Command} from '../model/command'; +import {CommandSerializer} from '../serializer/command.serializer'; + +import {ResourceService} from 'globunet-angular/core/providers/api/resource.service'; +import {Observable} from 'rxjs'; + + +@Injectable({ + providedIn: 'root' +}) +export class CommandService extends ResourceService<Command> { + + constructor(http: HttpClient) { + super(http, environment.API_URL, 'commands', new CommandSerializer()); + } + + execute(params): Observable<any> { + const url = this.url + '/commands/executes.json'; + return this.httpClient.post(url, params); + } + +} diff --git a/admin/WebConsole3/frontend/src/app/api/software-component.service.ts b/admin/WebConsole3/frontend/src/app/api/software-component.service.ts index 6b794109..b154d41a 100644 --- a/admin/WebConsole3/frontend/src/app/api/software-component.service.ts +++ b/admin/WebConsole3/frontend/src/app/api/software-component.service.ts @@ -1,20 +1,20 @@ -import { Injectable } from '@angular/core';
-import { HttpClient} from '@angular/common/http';
-
-import { environment } from '../../environments/environment';
-import { SoftwareComponent } from "../model/software-component";
-import { SoftwareComponentSerializer } from "../serializer/software-component.serializer";
-
-import {ResourceService} from "globunet-angular/core/providers/api/resource.service";
-
-
-@Injectable({
- providedIn: 'root'
-})
-export class SoftwareComponentService extends ResourceService<SoftwareComponent> {
-
- constructor(http: HttpClient){
- super(http, environment.API_URL,"softwareComponents", new SoftwareComponentSerializer());
- }
-
-}
+import { Injectable } from '@angular/core'; +import { HttpClient} from '@angular/common/http'; + +import { environment } from '../../environments/environment'; +import { SoftwareComponent } from "../model/software-component"; +import { SoftwareComponentSerializer } from "../serializer/software-component.serializer"; + +import {ResourceService} from "globunet-angular/core/providers/api/resource.service"; + + +@Injectable({ + providedIn: 'root' +}) +export class SoftwareComponentService extends ResourceService<SoftwareComponent> { + + constructor(http: HttpClient){ + super(http, environment.API_URL,"softwarecomponents", new SoftwareComponentSerializer()); + } + +} diff --git a/admin/WebConsole3/frontend/src/app/api/software-profile.service.ts b/admin/WebConsole3/frontend/src/app/api/software-profile.service.ts index 442d97a7..92b09e23 100644 --- a/admin/WebConsole3/frontend/src/app/api/software-profile.service.ts +++ b/admin/WebConsole3/frontend/src/app/api/software-profile.service.ts @@ -1,20 +1,20 @@ -import { Injectable } from '@angular/core';
-import { HttpClient} from '@angular/common/http';
-
-import { environment } from '../../environments/environment';
-import { SoftwareProfile } from "../model/software-profile";
-import { SoftwareProfileSerializer } from "../serializer/software-profile.serializer";
-
-import {ResourceService} from "globunet-angular/core/providers/api/resource.service";
-
-
-@Injectable({
- providedIn: 'root'
-})
-export class SoftwareProfileService extends ResourceService<SoftwareProfile> {
-
- constructor(http: HttpClient){
- super(http, environment.API_URL,"softwareProfiles", new SoftwareProfileSerializer());
- }
-
-}
+import { Injectable } from '@angular/core'; +import { HttpClient} from '@angular/common/http'; + +import { environment } from '../../environments/environment'; +import { SoftwareProfile } from "../model/software-profile"; +import { SoftwareProfileSerializer } from "../serializer/software-profile.serializer"; + +import {ResourceService} from "globunet-angular/core/providers/api/resource.service"; + + +@Injectable({ + providedIn: 'root' +}) +export class SoftwareProfileService extends ResourceService<SoftwareProfile> { + + constructor(http: HttpClient){ + super(http, environment.API_URL,"softwareprofiles", new SoftwareProfileSerializer()); + } + +} diff --git a/admin/WebConsole3/frontend/src/app/api/software-type.service.ts b/admin/WebConsole3/frontend/src/app/api/software-type.service.ts index aa2e457e..ccc6eb8f 100644 --- a/admin/WebConsole3/frontend/src/app/api/software-type.service.ts +++ b/admin/WebConsole3/frontend/src/app/api/software-type.service.ts @@ -1,20 +1,20 @@ -import { Injectable } from '@angular/core';
-import { HttpClient} from '@angular/common/http';
-
-import { environment } from '../../environments/environment';
-import { SoftwareType } from "../model/software-type";
-import { SoftwareTypeSerializer } from "../serializer/software-type.serializer";
-
-import {ResourceService} from "globunet-angular/core/providers/api/resource.service";
-
-
-@Injectable({
- providedIn: 'root'
-})
-export class SoftwareTypeService extends ResourceService<SoftwareType> {
-
- constructor(http: HttpClient){
- super(http, environment.API_URL,"softwareTypes", new SoftwareTypeSerializer());
- }
-
-}
+import { Injectable } from '@angular/core'; +import { HttpClient} from '@angular/common/http'; + +import { environment } from '../../environments/environment'; +import { SoftwareType } from "../model/software-type"; +import { SoftwareTypeSerializer } from "../serializer/software-type.serializer"; + +import {ResourceService} from "globunet-angular/core/providers/api/resource.service"; + + +@Injectable({ + providedIn: 'root' +}) +export class SoftwareTypeService extends ResourceService<SoftwareType> { + + constructor(http: HttpClient){ + super(http, environment.API_URL,"softwaretypes", new SoftwareTypeSerializer()); + } + +} diff --git a/admin/WebConsole3/frontend/src/app/app-routing.module.ts b/admin/WebConsole3/frontend/src/app/app-routing.module.ts index 7e5c4c63..f91738a4 100644 --- a/admin/WebConsole3/frontend/src/app/app-routing.module.ts +++ b/admin/WebConsole3/frontend/src/app/app-routing.module.ts @@ -17,6 +17,19 @@ import {ImageEditComponent} from './pages/image/edit/image-edit.component'; import {ProfileComponent} from './pages/profile/profile.component'; import {OrganizationalUnitEditComponent} from './pages/organizational-unit/edit/organizational-unit-edit.component'; import {ClientComponent} from './pages/client/client.component'; +import {ClientDhcpComponent} from './pages/client/dhcp/client-dhcp.component'; +import {DeployImageCommandComponent} from './pages/command/deploy-image-command/deploy-image-command.component'; +import {MenuComponent} from './pages/menu/menu.component'; +import {MenuEditComponent} from './pages/menu/edit/menu-edit.component'; +import {SoftwareProfileComponent} from './pages/software-profile/software-profile.component'; +import {SoftwareComponentComponent} from './pages/software-component/software-component.component'; +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 = [ @@ -59,14 +72,18 @@ const routes: Routes = [ }, { path: 'clients/dhcp', - component: ClientComponent + component: ClientDhcpComponent }, { path: 'images', component: ImageComponent }, { - path: 'images/create', + path: 'images/create/monolithic', + component: ImageEditComponent + }, + { + path: 'images/create/basic', component: ImageEditComponent }, { @@ -94,10 +111,66 @@ const routes: Routes = [ component: HardwareProfileComponent }, { + path: 'software', + component: SoftwareComponent, + }, + { + path: 'software/profile/create', + component: SoftwareProfileComponent + }, + { + path: 'software/component/create', + component: SoftwareComponentComponent + }, + { + path: 'software/profile/:id', + component: SoftwareProfileComponent + }, + { + path: 'menus', + component: MenuComponent + }, + { + path: 'menus/create', + component: MenuEditComponent + }, + { + path: 'menus/edit/:id', + component: MenuEditComponent + }, + { path: 'commands', component: CommandComponent }, { + path: 'commands/partition_format', + component: PartitionFormatCommandComponent + }, + { + path: 'commands/deploy_image', + component: DeployImageCommandComponent + }, + { + path: 'commands/login', + component: LoginCommandComponent + }, + { + path: 'commands/execute', + component: ExecuteCommandComponent + }, + { + path: 'commands/create_image', + 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 3dd55fe6..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'; @@ -26,8 +26,8 @@ import { HardwareComponent } from './pages/hardware/hardware.component'; import { HardwareComponentsComponent } from './pages/hardware/hardware-components/hardware-components.component'; import { HardwareProfilesComponent } from './pages/hardware/hardware-profiles/hardware-profiles.component'; import { HardwareTypesComponent } from './pages/hardware/hardware-types/hardware-types.component'; -import {ProfilesTableComponent} from './pages/hardware/hardware-profiles/profiles-table/profiles-table.component'; -import {ProfilesGroupComponent} from './pages/hardware/hardware-profiles/profiles-group/profiles-group.component'; +import {HardwareProfilesTableComponent} from './pages/hardware/hardware-profiles/profiles-table/hardware-profiles-table.component'; +import {HardwareProfilesGroupComponent} from './pages/hardware/hardware-profiles/profiles-group/hardware-profiles-group.component'; import {HardwareComponentsTableComponent} from './pages/hardware/hardware-components/hardware-components-table/hardware-components-table.component'; import {HardwareComponentsGroupComponent} from './pages/hardware/hardware-components/hardware-components-group/hardware-components-group.component'; import {HardwareProfileComponent} from './pages/hardware-profile/hardware-profile.component'; @@ -58,6 +58,27 @@ import {OrganizationalUnitEditComponent} from './pages/organizational-unit/edit/ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {ClientComponent} from './pages/client/client.component'; import {ChartsModule} from 'ng2-charts'; +import {ClientDhcpComponent} from './pages/client/dhcp/client-dhcp.component'; +import {DeployImageCommandComponent} from './pages/command/deploy-image-command/deploy-image-command.component'; +import {MenuComponent} from './pages/menu/menu.component'; +import {MenuEditComponent} from './pages/menu/edit/menu-edit.component'; +import {SoftwareComponentComponent} from './pages/software-component/software-component.component'; +import {SoftwareComponent} from './pages/software/software.component'; +import {SoftwareComponentsComponent} from './pages/software/software-components/software-components.component'; +import {SoftwareProfilesComponent} from './pages/software/software-profiles/software-profiles.component'; +import {SoftwareProfileComponent} from './pages/software-profile/software-profile.component'; +import {SoftwareTypesComponent} from './pages/software/software-types/software-types.component'; +import {SoftwareProfilesTableComponent} from './pages/software/software-profiles/software-profiles-table/software-profiles-table.component'; +import {SoftwareProfilesGroupComponent} from './pages/software/software-profiles/software-profiles-group/software-profiles-group.component'; +import {SoftwareComponentsTableComponent} from './pages/software/software-components/software-components-table/software-components-table.component'; +import {SoftwareComponentsGroupComponent} from './pages/software/software-components/software-components-group/software-components-group.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'; +import {ColResizableDirective} from './pages/common/directive/col-resizable.directive'; @@ -67,6 +88,8 @@ import {ChartsModule} from 'ng2-charts'; LoginComponent, ImageComponent, ImageEditComponent, + MenuComponent, + MenuEditComponent, DashboardComponent, RepositoryComponent, OrganizationalUnitComponent, @@ -79,17 +102,36 @@ import {ChartsModule} from 'ng2-charts'; HardwareProfilesComponent, HardwareProfileComponent, HardwareTypesComponent, - ProfilesTableComponent, - ProfilesGroupComponent, + HardwareProfilesTableComponent, + HardwareProfilesGroupComponent, HardwareComponentsTableComponent, HardwareComponentsGroupComponent, + SoftwareComponentComponent, + SoftwareComponent, + SoftwareComponentsComponent, + SoftwareProfilesComponent, + SoftwareProfileComponent, + SoftwareTypesComponent, + SoftwareProfilesTableComponent, + SoftwareProfilesGroupComponent, + SoftwareComponentsTableComponent, + SoftwareComponentsGroupComponent, OuGroupComponent, OuClientComponent, ClientComponent, + ClientDhcpComponent, CommandComponent, + DeployImageCommandComponent, + LoginCommandComponent, + ExecuteCommandComponent, + CreateImageCommandComponent, + DeleteCacheImageCommandComponent, + FormatCommandComponent, + PartitionFormatCommandComponent, EditCommandComponent, IcheckDirective, FixedToolboxBarDirective, + ColResizableDirective, OgInformationOptionsComponent, OgCommandsOptionsComponent, OgExecuteCommandOptionsComponent, @@ -110,17 +152,31 @@ import {ChartsModule} from 'ng2-charts'; LoginComponent, ImageComponent, ImageEditComponent, + MenuComponent, + MenuEditComponent, OrganizationalUnitComponent, OrganizationalUnitEditComponent, Ng2TableActionComponent, - ProfilesTableComponent, - ProfilesGroupComponent, + HardwareProfilesTableComponent, + HardwareProfilesGroupComponent, HardwareComponentsTableComponent, HardwareComponentsGroupComponent, + SoftwareProfilesTableComponent, + SoftwareProfilesGroupComponent, + SoftwareComponentsTableComponent, + SoftwareComponentsGroupComponent, OuGroupComponent, OuClientComponent, ClientComponent, + ClientDhcpComponent, CommandComponent, + DeployImageCommandComponent, + LoginCommandComponent, + ExecuteCommandComponent, + CreateImageCommandComponent, + DeleteCacheImageCommandComponent, + FormatCommandComponent, + PartitionFormatCommandComponent, EditCommandComponent, OgOuGeneralOptionsComponent, TraceComponent, @@ -134,7 +190,7 @@ import {ChartsModule} from 'ng2-charts'; 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 new file mode 100644 index 00000000..b17cc7a0 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/form-type/menu.form-type.ts @@ -0,0 +1,16 @@ +import {GlobunetFormType} from './globunet.form-type'; +import {Menu} from '../model/menu'; + + +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/client.ts b/admin/WebConsole3/frontend/src/app/model/client.ts index dbc9976e..8977e168 100644 --- a/admin/WebConsole3/frontend/src/app/model/client.ts +++ b/admin/WebConsole3/frontend/src/app/model/client.ts @@ -2,6 +2,20 @@ import { Resource } from 'globunet-angular/core/models/api/resource'; import {Repository} from './repository'; import {HardwareProfile} from './hardware-profile'; import {Netboot} from './netboot'; +import {PartitionInfo} from './image'; + +export class Partition { + cacheContent: string; + filesystem: string; + id: number; + image: any; + numDisk: number; + numPartition: number; + osName: string; + partitionCode: string; + size: number; + usage: number; +} export class Client extends Resource { public name = ''; @@ -15,8 +29,10 @@ export class Client extends Resource { public oglive = null; public netboot: Netboot = null; public organizationalUnit: number; + public partitions?: PartitionInfo[] // Variables temporales para la vista, no vienen del servidor public status?: string; public selected?: boolean; + public disksConfig?: any[]; } diff --git a/admin/WebConsole3/frontend/src/app/model/command.ts b/admin/WebConsole3/frontend/src/app/model/command.ts index 2f3639ba..2c5697aa 100644 --- a/admin/WebConsole3/frontend/src/app/model/command.ts +++ b/admin/WebConsole3/frontend/src/app/model/command.ts @@ -1,8 +1,14 @@ -import { Resource } from 'globunet-angular/core/models/api/resource';
-
-export class Command extends Resource {
- public title = '';
- public script = '';
- public parameters = false;
-
-}
+import { Resource } from 'globunet-angular/core/models/api/resource'; + +export class Excecution { + script = ''; + clients = ''; + type = ''; +} + +export class Command extends Resource { + public title = ''; + public script = ''; + public parameters = false; + +} diff --git a/admin/WebConsole3/frontend/src/app/model/image.ts b/admin/WebConsole3/frontend/src/app/model/image.ts index 2a85299f..5df31b74 100644 --- a/admin/WebConsole3/frontend/src/app/model/image.ts +++ b/admin/WebConsole3/frontend/src/app/model/image.ts @@ -1,20 +1,25 @@ -import { Resource } from 'globunet-angular/core/models/api/resource';
-import {Repository} from './repository';
-
-export class PartitionInfo {
- numDisk: number;
- numPartition: number;
- partitionCode: string;
- filesystem: string;
- osName: string;
-}
-
-export class Image extends Resource {
- public canonicalName: string = '';
- public repository: Repository = new Repository();
- public description: string = '';
- public comments: string = '';
- public revision: string;
- public createdAt: Date;
- public partitionInfo: PartitionInfo;
-}
+import { Resource } from 'globunet-angular/core/models/api/resource'; +import {Repository} from './repository'; +import {Client} from './client'; +import {SoftwareProfile} from './software-profile'; + +export class PartitionInfo { + numDisk: number; + numPartition: number; + partitionCode: string; + filesystem: string; + osName: string; + type: string; +} + +export class Image extends Resource { + public canonicalName = ''; + public repository: Repository = new Repository(); + public description = ''; + public comments = ''; + public revision: string; + public createdAt: Date; + public softwareProfile: SoftwareProfile; + public partitionInfo: PartitionInfo; + public client?: Client; +} diff --git a/admin/WebConsole3/frontend/src/app/model/menu.ts b/admin/WebConsole3/frontend/src/app/model/menu.ts index e8928976..1d19bd44 100644 --- a/admin/WebConsole3/frontend/src/app/model/menu.ts +++ b/admin/WebConsole3/frontend/src/app/model/menu.ts @@ -1,12 +1,12 @@ -import { Resource } from 'globunet-angular/core/models/api/resource';
-
-export class Menu extends Resource {
- public title: string;
- public resolution: string;
- public description: string;
- public comments: string;
- public publicColumns: number;
- public publicUrl: string;
- public privateColumns: number;
- public privateUrl: string;
-}
+import { Resource } from 'globunet-angular/core/models/api/resource'; + +export class Menu extends Resource { + public title = ''; + public resolution = ''; + public description = ''; + public comments = ''; + public publicUrl = '' + public privateUrl = ''; + public publicColumns: number; + public privateColumns: number; +} 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/client/client.component.html b/admin/WebConsole3/frontend/src/app/pages/client/client.component.html index 0ba00d7d..81c0a3ac 100644 --- a/admin/WebConsole3/frontend/src/app/pages/client/client.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/client/client.component.html @@ -3,7 +3,7 @@ </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><a [routerLink]="'/app/ous'"><i class="fa fa-th"></i> {{'ous'|translate}}</a></li> <li class="active" translate="client"></li> </ol> </section> @@ -24,18 +24,18 @@ <h3 class="box-title" translate="client_data"></h3> <div class="box-tools pull-right"> <div class="btn-group" *ngIf="client.id !== 0"> - <app-og-information-options ></app-og-information-options> + <app-og-information-options></app-og-information-options> <app-og-commands-options></app-og-commands-options> </div> </div> </mk-box-header> <mk-box-content> <form role="form" name="Form"> - <app-form-input [formType]="form" [cols]="2" [model]="client" ></app-form-input> + <app-form-input [formType]="form" [cols]="2" [model]="client"></app-form-input> </form> </mk-box-content> - <mk-box-footer > - <mk-box class="box box-default box-solid" *ngFor="let diskConfig of client.diskConfig"> + <mk-box-footer> + <mk-box class="box box-default box-solid" [isRemovable]="false" *ngFor="let diskConfig of client.disksConfig"> <mk-box-header class="box-header with-border"> <div class="row"> <div class="col-md-4"> @@ -48,10 +48,6 @@ <span translate="size"></span>: <b>{{getSizeInGB(diskConfig.size)}} GB</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 --> </mk-box-header> <!-- /.box-header --> @@ -69,47 +65,62 @@ <th translate="os"></th> </tr> </tbody> - <ng-template *ngIf="partition.size > 0"> - <tbody *ngFor="let partition of diskConfig.partitions" > - <tr ng-class="{'odd': $index%2 == 0, 'even': $index%2 != 0}"> - <td>{{partition.numPartition}}</td> - <td>{{partition.parttype}}</td> - <td>{{partition.filesystem}}</td> - <td>{{getSizeInGB(partition.size)}} GB</td> - <td><span class="badge" ng-class="{'bg-green': partition.usage < 60, 'bg-yellow': partition.usage >= 60 && partition.usage < 80, 'bg-red': partition.usage >= 80}">{{partition.usage}}%</span></td> - <td>{{partition.osName}}</td> - </tr> - <tr *ngIf="partition.partitionCode == 'ca' && partition.cacheContent.files.length > 0" class="client-cache-info" ng-class="{'odd': $index%2 == 0, 'even': $index%2 != 0}"> - <td colspan="6"> - <span style="font-weight: bold" translate="cache_content"></span> - <table class="table"> - <thead> - <tr> - <th translate="type"></th> - <th translate="name"></th> - <th translate="size"></th> - </tr> - </thead> - <tbody> - <tr *ngFor="let file of partition.cacheContent.files"> - <td>{{file.type}}</td> - <td>{{file.name}}</td> - <td>{{file.size}}</td> - </tr> - <tr> - <td style="font-weight: bold" translate="free_space" colspan="2"></td> - <td>{{partition.cacheContent.freeSpace}}</td> - </tr> - </tbody> - </table> - </td> - </tr> - </tbody> - </ng-template> + <ng-container *ngFor="let partition of diskConfig.partitions"> + <tbody *ngIf="partition.size > 0"> + <tr ng-class="{'odd': $index%2 == 0, 'even': $index%2 != 0}"> + <td>{{partition.numPartition}}</td> + <td>{{partition.parttype}}</td> + <td>{{partition.filesystem}}</td> + <td>{{getSizeInGB(partition.size)}} GB</td> + <td><span class="badge" + [ngClass]="getPartitionUsageClass(partition.usage)">{{partition.usage}} + %</span></td> + <td>{{partition.osName}}</td> + </tr> + <tr *ngIf="partition.partitionCode == 'ca' && partition.cacheContent.files.length > 0" + class="client-cache-info" + ng-class="{'odd': $index%2 == 0, 'even': $index%2 != 0}"> + <td colspan="6"> + <span style="font-weight: bold" translate="cache_content"></span> + <table class="table"> + <thead> + <tr> + <th translate="type"></th> + <th translate="name"></th> + <th translate="size"></th> + </tr> + </thead> + <tbody> + <tr *ngFor="let file of partition.cacheContent.files"> + <td>{{file.type}}</td> + <td>{{file.name}}</td> + <td>{{file.size}}</td> + </tr> + <tr> + <td style="font-weight: bold" translate="free_space" + colspan="2"></td> + <td>{{partition.cacheContent.freeSpace}}</td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </ng-container> </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]="'pie'" + [plugins]="pluginDataLabels" + [options]="diskConfig.diskChartOptions"> + </canvas> + </div> </div> </div> </mk-box-content> diff --git a/admin/WebConsole3/frontend/src/app/pages/client/client.component.ts b/admin/WebConsole3/frontend/src/app/pages/client/client.component.ts index 50346ffd..0611d6dc 100644 --- a/admin/WebConsole3/frontend/src/app/pages/client/client.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/client/client.component.ts @@ -12,7 +12,8 @@ import {Repository} from '../../model/repository'; import {HardwareProfile} from '../../model/hardware-profile'; import {OgCommonService} from '../../service/og-common.service'; import {ClientFormType} from '../../form-type/client.form-type'; -import {GlobunetFormType} from '../../form-type/globunet.form-type'; +import * as pluginDataLabels from 'chartjs-plugin-datalabels'; +import {ChartOptions} from 'chart.js'; @Component({ selector: 'app-client', @@ -27,6 +28,7 @@ export class ClientComponent implements OnInit { public oglives: any[] = []; private formType: ClientFormType; public form; + public pluginDataLabels; // this tells the tabs component which Pages // should be each tab's root Page @@ -39,6 +41,7 @@ export class ClientComponent implements OnInit { private hardwareProfileService: HardwareProfileService, private ogCommonService: OgCommonService) { this.client = new Client(); + this.client.disksConfig = []; this.formType = new ClientFormType(); this.form = this.formType.getForm(); } @@ -57,6 +60,17 @@ export class ClientComponent implements OnInit { this.clientService.read(id).subscribe( client => { this.client = client; + this.client.disksConfig = this.ogCommonService.getDisksConfigFromPartitions(this.client.partitions); + + const self = this; + this.client.disksConfig.forEach(function(diskConfig) { + const chartData = self.getChartData(diskConfig); + diskConfig.diskChartData = chartData.diskChartData; + diskConfig.diskChartLabels = chartData.diskChartLabels; + diskConfig.diskPieChartColors = chartData.diskPieChartColors; + diskConfig.diskChartOptions = chartData.diskChartOptions; + }); + } ); } @@ -122,6 +136,75 @@ export class ClientComponent implements OnInit { ); } + getChartData(diskConfig) { + const diskChartData = []; + const diskChartLabels = []; + const diskPieChartColors = [{ + backgroundColor: [] + }]; + const self = this; + diskConfig.partitions.forEach(function(partition) { + if (partition.size > 0) { + self.setPartitionPercentOfDisk(diskConfig, partition); + diskChartData.push(partition.percentOfDisk); + diskChartLabels.push([ + (partition.os || partition.filesystem), + partition.percentOfDisk + '%' + ]); + diskPieChartColors[0].backgroundColor.push(self.getPartitionColor(partition)); + } + }); + const diskChartOptions: ChartOptions = { + responsive: true, + legend: { + position: 'bottom' + }, + plugins: { + datalabels: { + formatter: (value, ctx) => { + const label = ctx.chart.data.labels[ctx.dataIndex]; + return label; + }, + }, + } + }; + + return { + diskChartData: diskChartData, + diskChartLabels: diskChartLabels, + diskChartOptions: diskChartOptions, + diskPieChartColors: diskPieChartColors + }; + } + + setPartitionPercentOfDisk(diskConfig, partition) { + partition.percentOfDisk = Math.round(((partition.size * 100) / diskConfig.size) * 100) / 100; + } + + labelFormatter(label, series) { + return '<div style="font-size:13px; text-align:center; padding:2px; color: #000; font-weight: 600;">' + + '<br>' + + series.percentOfDisk + '%</div>'; + } + + getPartitionColor(partition) { + let color = 'rgb(252, 90, 90)'; + + // Para la partición de datos se usa un color específico + if (partition.osName === 'DATA') { + color = 'rgb(237,194,64)'; + } else if (partition.filesystem === 'NTFS') { + color = 'rgb(0,192, 239)'; + } else if (partition.filesystem.match('EXT')) { + color = 'rgb(96, 92, 168)'; + } else if (partition.filesystem.match('LINUX-SWAP')) { + color = 'rgb(84, 84, 84)'; + } else if (partition.filesystem.match('CACHE')) { + color = 'rgb(252, 90, 90)'; + } + return color; + } + save() { let request: Observable<Client>; if (this.client.id !== 0) { @@ -146,5 +229,16 @@ export class ClientComponent implements OnInit { } + getPartitionUsageClass(usage: number | string | number) { + let result = ''; + if (usage < 60) { + result = 'bg-green'; + } else if (usage >= 60 && usage < 80) { + result = 'bg-yellow'; + } else if (usage >= 80) { + result = 'bg-red'; + } + return result; + } } diff --git a/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.html b/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.html new file mode 100644 index 00000000..c21f5c8d --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.html @@ -0,0 +1,139 @@ +<section class="content-header"> + <h1 translate="dhcp_clients"> + </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="dhcp_clients"></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="save" (click)="save()"></button> + </div> + </div> + </div> +</section> +<section class="content"> + <div class="row"> + <div class="col-md-12"> + <div class="box box-primary"> + <div class="box-header with-border"> + <h3 class="box-title" translate="load_clients_from_dhcp"></h3> + <div *ngIf="clients.length == 0" class="btn-group pull-right"> + <button type="button" name="search" id="search-btn" class="btn btn-flat" (click)="downloadFromServer()"> + <i class="fa fa-download"></i> + <span translate="download_from_server"></span> + </button> + <button class="btn btn-primary" (click)="proccessDhcp()" translate="proccess_dhcp"></button> + </div> + <div *ngIf="clients.length > 0" class="pull-right"> + <button class="btn btn-danger" (click)="clients = []" translate="back"></button> + </div> + </div> + <div *ngIf="clients.length == 0" class="box-body"> + <div class="row"> + <div class="col-md-12"> + <textarea [(ngModel)]="dhcpText" class="og-instructions"> + + </textarea> + </div> + </div> + </div> + <div class="box-body" *ngIf="clients.length > 0"> + <div class="row"> + <div class="col-md-12"> + <div class="box box-default"> + <div class="box-header with-border"> + <h3 class="box-title" translate="common_options"></h3> + </div> + <div class="box-body"> + <div class="row"> + <div class="col-xs-2"> + <div class="form-group"> + <label class="control-label" for="repository" translate="repository"></label> + <select name="repository" class="form-control" [(ngModel)]="commonProperties.repository"> + <option *ngFor="let repository of repositories" [value]="repository.id">{{repository.name}}</option> + </select> + </div> + </div> + <div class="col-xs-2"> + <div class="form-group"> + <label class="control-label" for="hardwareProfile" translate="hardware_profile"></label> + <select name="hardwareProfile" class="form-control" [(ngModel)]="commonProperties.hardwareProfile"> + <option *ngFor="let profile of hardwareProfiles" [value]="profile.id"> + {{profile.description}} + </option> + </select> + </div> + </div> + <div class="col-xs-1"> + <div class="form-group"> + <label class="control-label" for="netiface" translate="netiface"></label> + <input type="text" name="netiface" class="form-control" [(ngModel)]="commonProperties.netiface" /> + </div> + </div> + <div class="col-xs-2"> + <div class="form-group"> + <label class="control-label" for="netdriver" translate="netdriver"></label> + <input type="text" name="netdriver" class="form-control" [(ngModel)]="commonProperties.netdriver" /> + </div> + </div> + <div class="form-group col-md-3"> + <label for="oglive" translate="oglive"> + <span class="symbol required ng-scope"></span> + </label> + <select class="form-control" required="true" [(ngModel)]="commonProperties.oglive" name="oglive"> + <option *ngFor="let item of constants.ogliveinfo" [value]="item.directory"> + {{item.directory}} + </option> + </select> + </div> + <div class="form-group col-md-2"> + <label for="netboot" translate="netboot"> + <span class="symbol required ng-scope"></span> + </label> + <select class="form-control" required="true" [(ngModel)]="commonProperties.netboot" name="netboot"> + <option *ngFor="let item of netboots" [value]="item.id">{{item.name}}</option> + </select> + </div> + </div> + </div> + <!-- /.box-body --> + </div> + </div> + <div class="col-md-12"> + <table class="table table-striped"> + <thead> + <tr> + <th> + <input icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" (change)="selectedUnselectAll()" [(ngModel)]="selectAll" /> + <span translate="select"></span> + </th> + <th translate="name"></th> + <th translate="ip"></th> + <th translate="mac"></th> + </tr> + </thead> + <tbody> + <tr *ngFor="let client of clients"> + <td> + <input icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" [(ngModel)]="client.$$selected" /> + </td> + <td>{{client.name}}</td> + <td>{{client.ip}}</td> + <td>{{client.mac}}</td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <div class="box-footer"> + </div> + </div> + </div> + </div> +</section> diff --git a/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.scss b/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.scss new file mode 100644 index 00000000..0bbea7e4 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.scss @@ -0,0 +1,5 @@ +::ng-deep app-client { + ::ng-deep .box-title { + width: 100%; + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.ts b/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.ts new file mode 100644 index 00000000..70394ba4 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/client/dhcp/client-dhcp.component.ts @@ -0,0 +1,223 @@ +import {Component, OnInit} from '@angular/core'; + +import {ClientService} from 'src/app/api/client.service'; +import {ActivatedRoute, ParamMap, Router} from '@angular/router'; +import {forkJoin, Observable} from 'rxjs'; +import {NetbootService} from '../../../api/netboot.service'; +import {ToasterService} from '../../../service/toaster.service'; +import {RepositoryService} from '../../../api/repository.service'; +import {HardwareProfileService} from '../../../api/hardware-profile.service'; +import {Repository} from '../../../model/repository'; +import {HardwareProfile} from '../../../model/hardware-profile'; +import {OgCommonService} from '../../../service/og-common.service'; +import {ClientFormType} from '../../../form-type/client.form-type'; +import {TranslateService} from '@ngx-translate/core'; + +@Component({ + selector: 'app-client-dhcp', + templateUrl: './client-dhcp.component.html', + styleUrls: ['./client-dhcp.component.scss'] +}) +export class ClientDhcpComponent implements OnInit { + public netboots: any = []; + public repositories: Repository[] = []; + public hardwareProfiles: HardwareProfile[] = []; + public oglives: any[] = []; + private formType: ClientFormType; + public form; + public constants = {}; + private dhcpFile: string; + public commonProperties: any; + public dhcpText: string; + public clients: any[]; + public selectAll: boolean; + + // this tells the tabs component which Pages + // should be each tab's root Page + constructor(private router: Router, + private activatedRouter: ActivatedRoute, + private clientService: ClientService, + private netbootService: NetbootService, + private translate: TranslateService, + private toaster: ToasterService, + private repositoryService: RepositoryService, + private hardwareProfileService: HardwareProfileService, + private ogCommonService: OgCommonService) { + + this.commonProperties = {}; + this.clients = []; + + } + + ngOnInit() { + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.constants = data.constants; + } + ); + this.dhcpFile = '/etc/dhcp/dhcpd.conf'; + this.loadNetboots(); + // Los repositorios vienen cargados ya desde config.router + this.repositoryService.list().subscribe( + (repositories) => { + this.repositories = repositories; + this.commonProperties.repository = this.repositories[0].id; + if (!this.hardwareProfiles) { + this.hardwareProfileService.list().subscribe( + (response) => { + this.hardwareProfiles = response; + this.commonProperties.hardwareProfile = this.hardwareProfiles[0].id; + }, + (error) => { + alert(error); + } + ); + } else { + this.hardwareProfiles = this.hardwareProfiles; + } + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + + } + + loadNetboots() { + this.netbootService.list().subscribe( + (result) => { + this.netboots = result; + this.commonProperties.netboot = this.netboots[0]; + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + + + downloadFromServer() { + this.dhcpText = 'ddns-update-style none;\n' + + 'option domain-name "example.org";\n' + + 'log-facility local7;\n' + + 'not-authoritative;\n' + + '\n' + + 'subnet 172.16.140.0 netmask 255.255.255.0 {\n' + + ' option domain-name-servers 172.16.3.1;\n' + + ' option routers 172.16.140.254;\n' + + ' option broadcast-address 172.16.140.255;\n' + + ' default-lease-time 600;\n' + + ' max-lease-time 7200;\n' + + ' next-server 172.16.140.210;\n' + + ' filename "grldr";\n' + + ' use-host-decl-names on;\n' + + '\n' + + '# host HOSTNAME1 {\n' + + '# hardware ethernet HOSTMAC1;\n' + + '# fixed-address HOSTIP1;\n' + + '# }\n' + + '\n' + + ' host pc-pruebas {\n' + + ' hardware ethernet 00:1B:21:1F:EE:9D;\n' + + ' fixed-address 172.16.140.213;\n' + + ' }\n' + + ' host pc-virtualbox {\n' + + ' hardware ethernet 20:CF:30:BF:9A:39;\n' + + ' fixed-address 172.16.140.214;\n' + + ' }\n' + + '\n' + + '\n' + + '}\n'; + /* + ServerDchpResource.getDhcp().then( + function(response) { + this.dhcpText = response.text; + this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_loaded')}); + }, + function(error) { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + /**/ + } + + proccessDhcp() { + if (typeof this.dhcpText !== 'undefined' && this.dhcpText !== '') { + const lines = this.dhcpText.split('\n'); + this.clients = []; + for (let i = 0; i < lines.length; i++) { + // Comprobar si la línea actual contiene la palabra "host" sin ninguna # delante que sería comentario + if (/^host/.test(lines[i].trim())) { + // Unimos las siguientes líneas hasta encontrar "}" + let line = ''; + while(lines[i].indexOf("}") === -1 && i < lines.length){ + line += lines[i]; + i++; + } + // procesar la linea + // host pc53-151 { hardware ethernet 00:1E:33:61:49:B8; fixed-address 172.16.53.151; } + let parts = line.split('{'); + const hostname = parts[0].trim().split(' ')[1]; + + // Las siguientes partes pueden estar en la linea actual o las siguientes + parts = parts[1].trim().split(";"); + const mac = parts[0].trim().split('ethernet')[1]; + // lo mismo puede ocurrir con fixed-address puede estar en lineas diferentes + parts = parts[1].trim().split('fixed-address'); + const ip = parts[1]; + this.clients.push( + { + name: hostname, + ip: ip, + mac: mac, + $$selected: true + } + ); + } + } + } else { + this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('nothing_to_proccess')}); + } + } + + selectedUnselectAll() { + for (let c = 0; c < this.clients.length; c++) { + this.clients[c].$$selected = this.selectAll; + } + } + + save() { + const promises = []; + let ou = ''; + this.activatedRouter.queryParams.subscribe( + query => { + ou = query.ou; + } + ); + for (let c = 0; c < this.clients.length; c++) { + if (this.clients[c].$$selected === true) { + const client = Object.assign({}, this.clients[c]); + + // Si se indicó un padre en la url, se añade dicha propiedad + client.organizationalUnit = ou; + client.idproautoexec = 0; + client.netdriver = this.commonProperties.netdriver; + client.netiface = this.commonProperties.netiface; + // Propiedades comunes + // client.repository = this.commonProperties.repository; + // client.hardwareProfile = this.commonProperties.hardwareProfile; + promises.push(this.clientService.create(client)); + } + } + forkJoin(promises).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: 'success', body: 'Successfully saved'}); + 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/create-image-command/create-image-command.component.html b/admin/WebConsole3/frontend/src/app/pages/command/create-image-command/create-image-command.component.html new file mode 100644 index 00000000..9534718c --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/create-image-command/create-image-command.component.html @@ -0,0 +1,98 @@ +<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"> + <div class="box-header with-border"> + </div> + <div class="box-body"> + <form role="form" name="Form"> + <div class="form-group"> + <label> + <span translate="client"></span> + {{client.name}} + </label> + </div> + <div class="form-group" ng-class="{'has-error':Form.images.$dirty && Form.images.$invalid, 'has-success':Form.images.$valid}"> + <label for="images"> + <span translate="images"></span> + </label> + <select (change)="setCanonicalName()" class="form-control" type="text" [(ngModel)]="command.image" name="images"> + <option [ngValue]="null" translate="----"></option> + <option *ngFor="let image of images" [ngValue]="image">{{image.canonicalName}}</option> + </select> + </div> + <div class="form-group" ng-class="{'has-error':Form.canonicalName.$dirty && Form.canonicalName.$invalid, 'has-success':Form.canonicalName.$valid}"> + <label for="canonicalName"> + <span translate="canonicalName"></span> + <span class="symbol required"></span> + </label> + <input type="text" class="form-control" type="text" required="true" [(ngModel)]="command.canonicalName" name="canonicalName"> + </div> + <div> + <table class="table"> + <thead> + <tr> + <th translate="select"> + </th> + <th translate="disk"> + </th> + <th translate="partition"> + </th> + <th translate="filesystem"> + </th> + <th translate="osname"> + </th> + </tr> + </thead> + <tbody> + <ng-container *ngFor="let partition of client.partitions; let index = index;"> + <tr *ngIf="isClonable(partition)"> + <td> + <input icheck type="radio" [name]="'partition_'+partition.numPartition" checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" class="selection-checkbox" [(ngModel)]="selectedPartition" [value]="index" /> + </td> + <td> + {{partition.numDisk}} + </td> + <td> + {{partition.numPartition}} + </td> + <td> + {{partition.filesystem}} + </td> + <td> + {{partition.osName}} + </td> + </tr> + </ng-container> + </tbody> + </table> + </div> + </form> + </div> + </div> + </div> + </div> +</section> diff --git a/admin/WebConsole3/frontend/src/app/pages/command/create-image-command/create-image-command.component.scss b/admin/WebConsole3/frontend/src/app/pages/command/create-image-command/create-image-command.component.scss new file mode 100644 index 00000000..08b1082b --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/create-image-command/create-image-command.component.scss @@ -0,0 +1,3 @@ +app-execute-command { + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/create-image-command/create-image-command.component.ts b/admin/WebConsole3/frontend/src/app/pages/command/create-image-command/create-image-command.component.ts new file mode 100644 index 00000000..8f018e33 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/create-image-command/create-image-command.component.ts @@ -0,0 +1,188 @@ +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, Partition} from '../../../model/client'; +import {Repository} from '../../../model/repository'; +import {RepositoryService} from '../../../api/repository.service'; +import {ImageService} from '../../../api/image.service'; +import {forkJoin} from 'rxjs'; + +@Component({ + selector: 'app-create-image-command', + templateUrl: './create-image-command.component.html', + styleUrls: [ './create-image-command.component.scss' ] +}) +export class CreateImageCommandComponent implements OnInit { + private readonly user: User; + private constants: any; + public repositories: Repository[]; + public execution = new Excecution(); + public commands: Command[] = []; + public client: Client; + public images = []; + public command = {canonicalName: '', image: new Image()}; + public selectedPartition: number; + + + // 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) { + const clientId = Object.keys(this.ogCommonService.selectedClients)[0]; + this.client = this.ogCommonService.selectedClients[clientId]; + this.execution.clients = clientId; + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.constants = data.constants; + } + ); + this.loadRepositories(); + this.loadImages(); + } else { + // TODO - dar error? + this.ogSweetAlert.error(this.translate.instant('opengnsys_error'), this.translate.instant('not_clients_selected')); + this.router.navigate(['app.ous']); + } + } + + + sendCommand() { + if (!this.selectedPartition) { + this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('you_must_select_partition')}); + } else { + const disk = this.client.partitions[this.selectedPartition].numDisk; + const partition = this.client.partitions[this.selectedPartition].numPartition; + // Al crear la imagen, le asociamos un perfil software + // @ts-ignore + this.execution.script = this.constants.commands.SOFTWARE_INVENTORY + ' ' + disk + ' ' + partition + '\n'; + this.execution.script += this.constants.commands.CREATE_IMAGE + ' ' + disk + ' ' + partition + ' ' + this.command.canonicalName + ' REPO '; + // @ts-ignore + this.execution.script = this.execution.script.replace(/\"/g, '\\"').replace(/\$/g, '\\\$'); + + let image: Image = this.command.image; + let newImage = false; + + let result = true; + // Crear la imagen si no existe + if (!image) { + newImage = true; + // Comprobar que exista el repositorio, sino no podemos crear la nueva imagen + if (!this.repositories) { + result = false; + this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('no_repository_exist')}); + } else { + // Usar el repositorio por defecto + const repository = this.repositories[0]; + image = new Image(); + image.canonicalName = this.command.canonicalName; + image.description = this.translate.instant('image_created_automatically'); + image.repository = repository; + } + } + + // Asignar a la imagen los atributos del sistema operativo elegido + image.client = this.client; + + // Si no hubo ningun error se guardan todas las pgms + if (result === true) { + const promises = []; + if (newImage === true) { + promises.push(this.imageService.create(image)); + } else { + const imageCopy = Object.assign({}, image); + delete imageCopy.id; + delete imageCopy.softwareProfile; + promises.push(this.imageService.update(imageCopy)); + } + this.execution.type = 'CREATE_IMAGE'; + promises.push(this.commandService.execute(this.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}); + } + ); + } + } + } + + setCanonicalName() { + if (this.command.image !== null) { + this.command.canonicalName = this.command.image.canonicalName; + } else { + this.command.canonicalName = ''; + } + } + + private loadImages() { + this.imageService.list().subscribe( + (response) => { + this.images = response; + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + private loadRepositories() { + this.repositoryService.list().subscribe( + (response) => { + this.repositories = response; + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + + isClonable(partition) { + let clonable = false; + let index = 0; + const code = partition.partitionCode; + + if (partition.numPartition !== 0) { + // Buscar el codigo entre las constantes + while (index < this.constants.partitiontable.length && !clonable) { + // para cada tabla de particiones, buscamos el codigo de la particion + const elements = this.constants.partitiontable[index].partitions.filter(function(part) { + return (part.id === partition.partitionCode.padStart(2, '0')); + } + ); + clonable = (elements.length > 0 && elements[0].clonable === true); + index++; + } + } + + return clonable; + } + +} 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/deploy-image-command/deploy-image-command.component.html b/admin/WebConsole3/frontend/src/app/pages/command/deploy-image-command/deploy-image-command.component.html new file mode 100644 index 00000000..7dc55c29 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/deploy-image-command/deploy-image-command.component.html @@ -0,0 +1,146 @@ +<section class="content-header"> + <h1 translate="deploy_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="deploy_image"></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> + <!-- /.box-header --> + <div class="box-body"> + <form role="form" name="Form"> + <div class="row"> + <div class="form-group col-md-3" > + <div class="checkbox clip-check check-primary checkbox-inline"> + <input id="deployImageParameters" name="deployImage" icheck checkbox-class="iradio_square-blue" radio-class="iradio_square-blue" type="radio" class="selection-checkbox" value="true" [(ngModel)]="deployImage" (change)="updateDeployOptions()" /> + </div> + <label for="deployImage"> + <span translate="deploy_image_update_restore"></span> + </label> + </div> + <div class="form-group col-md-3"> + <div class="checkbox clip-check check-primary checkbox-inline"> + <input id="parameters" name="updateCache" icheck checkbox-class="iradio_square-blue" radio-class="iradio_square-blue" type="radio" class="selection-checkbox" value="false" [(ngModel)]="deployImage" (change)="updateDeployOptions()" /> + </div> + <label for="updateCache"> + <span translate="update_cache_only_download"></span> + </label> + </div> + </div> + <div class="row"> + <div class="form-group col-md-4"> + <div class="form-group" ng-class="{'has-error':Form.images.$dirty && Form.images.$invalid, 'has-success':Form.images.$valid}"> + <label> + <span translate="images"></span> + </label> + <select (change)="setCanonicalName()" class="form-control" type="text" [(ngModel)]="image" name="images"> + <option *ngFor="let image of images" [ngValue]="image">{{image.canonicalName}}</option> + </select> + </div> + </div> + <div class="form-group col-md-2"> + <label for="disk" translate="disk"> + </label> + <input name="disk" type="number" min="1" [(ngModel)]="disk" class="form-control"> + + </div> + <div class="form-group col-md-2"> + <label for="partition" translate="partition"> + </label> + <input name="partition" type="number" min="1" [(ngModel)]="partition" class="form-control"> + </div> + <div class="form-group col-md-4"> + <label for="deployMethod" translate="deploy_method"> + </label> + <select class="form-control" name="deployMethod" [(ngModel)]="deployMethod"> + <option *ngFor="let deployMethod of deployMethods" [value]="deployMethod">{{deployMethod}}</option> + </select> + </div> + </div> + <div *ngIf="deployMethod == 'MULTICAST' || deployMethod == 'MULTICAST-DIRECT'"> + <div class="row"> + <div class="form-group col-md-4"> + <label for="port" translate="port"> + </label> + <input class="form-control" type="text" name="port" [(ngModel)]="multicast.port"/> + </div> + <div class="form-group col-md-4"> + <label for="address" translate="address"> + </label> + <input class="form-control" type="text" name="address" [(ngModel)]="multicast.address"/> + </div> + <div class="form-group col-md-4"> + <label for="multicastMode" translate="mode"> + </label> + <input class="form-control" type="text" name="multicastMode" [(ngModel)]="multicast.mode"/> + </div> + </div> + <div class="row"> + <div class="form-group col-md-4"> + <label for="speed" translate="speed"> + </label> + <input class="form-control" type="text" name="speed" [(ngModel)]="multicast.speed"/> + </div> + <div class="form-group col-md-4"> + <label for="maxClients" translate="max_clients"> + </label> + <input class="form-control" type="text" name="maxClients" [(ngModel)]="multicast.maxClients"/> + </div> + <div class="form-group col-md-4"> + <label for="maxWaitTime" translate="max_wait_time"> + </label> + <input class="form-control" type="text" name="maxWaitTime" [(ngModel)]="multicast.maxWaitTime"/> + </div> + </div> + </div> + <div class="row" *ngIf="deployMethod == 'TORRENT'"> + <div class="form-group col-md-6"> + <label for="torrentMode" translate="mode"> + </label> + <input class="form-control" type="text" name="torrentMode" [(ngModel)]="torrent.mode"/> + </div> + <div class="form-group col-md-6"> + <label for="seedTime" translate="seed_time"> + + </label> + <input class="form-control" type="text" name="seedTime" [(ngModel)]="torrent.seedTime"/> + </div> + </div> + </form> + + </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/deploy-image-command/deploy-image-command.component.scss b/admin/WebConsole3/frontend/src/app/pages/command/deploy-image-command/deploy-image-command.component.scss new file mode 100644 index 00000000..ccb34440 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/deploy-image-command/deploy-image-command.component.scss @@ -0,0 +1,3 @@ +command { + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/deploy-image-command/deploy-image-command.component.ts b/admin/WebConsole3/frontend/src/app/pages/command/deploy-image-command/deploy-image-command.component.ts new file mode 100644 index 00000000..b8312964 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/deploy-image-command/deploy-image-command.component.ts @@ -0,0 +1,137 @@ +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 {ImageService} from '../../../api/image.service'; +import {OGCommandsService} from '../../../service/og-commands.service'; + +@Component({ + selector: 'app-deploy-image-command', + templateUrl: './deploy-image-command.component.html', + styleUrls: [ './deploy-image-command.component.scss' ] +}) +export class DeployImageCommandComponent implements OnInit { + torrent = { + mode: 'peer', + seedTime: '60' + }; + multicast = { + port: '9000', + address: '239.194.16.140', + mode: 'full-duplex', + speed: 90, + maxClients: 50, + maxWaitTime: 60 + }; + disk = 1; + partition = 1; + + images = []; + deployMethods = []; + deployMethod = 'MULTICAST'; + private deployImage: string; + private user: User; + private constants: any; + public image: any; + public editInstructions = false; + + + // 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 imageService: ImageService, + private ogSweetAlert: OgSweetAlertService, + private toaster: ToasterService, + private translate: TranslateService) { + this.user = this.authModule.getLoggedUser(); + } + + + ngOnInit() { + this.deployImage = 'true'; + this.updateDeployOptions(); + if (this.user) { + // Comprobar la selección de clientes + if (this.ogCommonService.getSelectionSize() > 0) { + this.imageService.list().subscribe( + (response) => { + this.images = response; + }, + (error) => { + this.images = []; + + } + ); + + } else { + // TODO - dar error? + this.toaster.pop({type: 'error', body: this.translate.instant('not_clients_selected'), title: this.translate.instant('opengnsys_error')}); + this.router.navigate(['app.ous']); + } + } + } + + updateDeployOptions() { + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.constants = data.constants; + if (this.deployImage === 'true') { + this.deployMethods = this.constants.deployMethods.deployImage; + } else { + // Si es updateCache, se quitan las opciones de deploy direct + this.deployMethods = this.constants.deployMethods.updateCache; + } + } + ); + + } + + /**/ + generateOgInstruction() { + let script = ''; + const disk = this.disk; + const partition = this.partition; + // Capturar ip del repositorio de la imagen elegida + let ip = '172.16.140.210'; + let imgName = this.image.canonicalName; + let target = ' ' + disk + ' ' + partition; + let log = 'ogEcho log session "[0] $MSG_SCRIPTS_TASK_START '; + + // Modo deploy + if (this.deployImage === 'true') { + script = 'deployImage '; + } else { + script = 'updateCache '; + ip = 'REPO'; + imgName += '.img'; + target = ''; + } + script += ip + ' /' + imgName + target + ' ' + this.deployMethod; + log += script + '"\n'; + script = log + script; + + // Modo + let params = ''; + if (this.deployMethod === 'MULTICAST' || this.deployMethod === 'MULTICAST-DIRECT') { + params = this.multicast.port + ':' + this.multicast.mode + ':' + this.multicast.address + ':' + this.multicast.speed + 'M:' + this.multicast.maxClients + ':' + this.multicast.maxWaitTime; + } else if (this.deployMethod === 'TORRENT') { + params = this.torrent.mode + ':' + this.torrent.seedTime; + } + script += ' ' + params; + + this.ogCommandsService.ogInstructions = script; + } + + setCanonicalName() { + //this.command.canonicalName = this.command.image.canonicalName; + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.html b/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.html new file mode 100644 index 00000000..37d7f8e2 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.html @@ -0,0 +1,95 @@ +<section class="content-header"> + <h1 translate="new_command"> + </h1> + <ol class="breadcrumb"> + <li><a [routerLink]="'/app/dashboard'"><i class="fa fa-dashboard"></i> {{'dashboard'|translate}}</a></li> + <li><a [routerLink]="'/app/commands'"><i class="fa fa-terminal"></i> {{'commands'|translate}}</a></li> + <li class="active" translate="command"></li> + </ol> +</section> +<section style="padding: 0 30px;"> + <h3 translate="selected_clients"></h3> + <app-og-selected-clients></app-og-selected-clients> +</section> +<section fixed-toolboxbar class="toolboxbar"> + <div> + <div class="col-md-12"> + <app-og-execute-command-options></app-og-execute-command-options> + </div> + </div> +</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 class="form-inline"> + <div class="form-group" ng-class="{'has-error':Form.newCommand.$dirty && Form.newCommand.$invalid, 'has-success':Form.newCommand.$valid}"> + <div class="checkbox clip-check check-primary checkbox-inline"> + <input id="parameters" name="newCommand" icheck checkbox-class="iradio_square-blue" radio-class="iradio_square-blue" type="radio" class="selection-checkbox" value="true" [(ngModel)]="newCommand" /> + </div> + <label for="newCommand"> + <span translate="new_command"></span> + </label> + </div> + <div class="form-group" ng-class="{'has-error':Form.savedCommand.$dirty && Form.savedCommand.$invalid, 'has-success':Form.savedCommand.$valid}"> + <div class="checkbox clip-check check-primary checkbox-inline"> + <input id="parameters" name="savedCommand" icheck checkbox-class="iradio_square-blue" radio-class="iradio_square-blue" type="radio" class="selection-checkbox" value="false" [(ngModel)]="newCommand" /> + </div> + <label for="savedCommand"> + <span translate="saved_command"></span> + </label> + </div> + </div> + </div> + <div class="box-body"> + <form role="form" name="Form" *ngIf="newCommand == 'true'"> + <div class="form-group" ng-class="{'has-error':Form.script.$dirty && Form.script.$invalid, 'has-success':Form.script.$valid}"> + <label for="script"> + <span translate="script"></span> + <span *ngIf="required == 'true'" class="symbol required"></span> + </label> + <textarea class="form-control" class="og-instructions" type="text" required="true" [(ngModel)]="ogCommandsService.ogInstructions" name="script"></textarea> + </div> + </form> + <form role="form" name="Form" *ngIf="newCommand == 'false'"> + <div class="row"> + <div class="col-md-12"> + <div class="form-group"> + <label translate="commands"></label> + <select class="form-control" name="command" [(ngModel)]="selectedCommand" (change)="updateSelectedCommand()"> + <option *ngFor="let command of commands" [ngValue]="command">{{command.title}}</option> + </select> + </div> + <div class="form-group" *ngIf="selectedCommand.parameters == true"> + <label translate="parameters"></label> + <p class="help-block"><span translate="detected_params"></span> <span style="font-weight: bold" ng-bind-html="selectedCommand.inputs.length"></span></p> + <div class="row"> + <div class="col-md-2" *ngFor="let input of selectedCommand.inputs; let index = index"> + <label for="param{{index}}"> + <span translate="parameter"></span> {{index+1}} + </label> + <input type="text" name="param{{index}}" [value]="selectedCommand.inputs[index]" (change)="updateScript(index, $event)"> + </div> + </div> + </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> + </form> + </div> + </div> + </div> + </div> +</section> diff --git a/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.scss b/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.scss new file mode 100644 index 00000000..08b1082b --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.scss @@ -0,0 +1,3 @@ +app-execute-command { + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.ts b/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.ts new file mode 100644 index 00000000..9560258a --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/execute-command/execute-command.component.ts @@ -0,0 +1,142 @@ +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 {CommandFormType} from '../../../form-type/command.form-type'; +import {Command} from '../../../model/command'; + +@Component({ + selector: 'app-execute-command', + templateUrl: './execute-command.component.html', + styleUrls: [ './execute-command.component.scss' ] +}) +export class ExecuteCommandComponent implements OnInit { + execution = {script: '', clients: ''}; + selectedCommand = { + inputs: [], + script: '' + }; + newCommand = 'true'; + private user: User; + private selectedClients = []; + private form = []; + private formType: CommandFormType; + public commands: Command[] = []; + editInstructions = false; + + + // 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 ogSweetAlert: OgSweetAlertService, + private toaster: ToasterService, + private translate: TranslateService) { + this.user = this.authModule.getLoggedUser(); + } + + + + + + + ngOnInit(): void { + this.selectedClients = this.ogCommonService.selectedClients; + if (this.user && this.selectedClients) { + this.loadFormOptions(); + this.loadCommands(); + } else { + // TODO - dar error? + this.ogSweetAlert.error(this.translate.instant('opengnsys_error'), this.translate.instant('not_clients_selected')); + this.router.navigate(['/app/ous']); + } + } + + sendCommand() { + let result = true; + + if (!this.execution.script) { + result = false; + this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('command_not_valid')}); + } else if (!this.execution.clients) { + result = false; + this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('not_clients_selected')}); + } + // Si no hubo ningun error + if (result === true) { + this.execution.script = this.execution.script.replace(/\"/g, '\\"').replace(/\$/g, '\\\$'); + // Resetar las instrucciones del script opengnsys almacenadas. + this.ogCommandsService.ogInstructions = ''; + this.commandService.execute(this.execution).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: 'success', body: 'Successfully saved'}); + this.router.navigate(['/app/ous']); + }, + function(error) { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + } + + loadFormOptions() { + this.formType = new CommandFormType(); + this.form = this.formType.getForm(); + } + + loadCommands() { + this.commandService.list().subscribe( + (result) => { + this.commands = result; + } + ); + } + + + executeSelectedCommand() { + // Ejecuta el contenido de ogInstructions + this.ogCommandsService.execute('RUN_SCRIPT'); + } + + updateSelectedCommand() { + this.getParamsNumber(this.selectedCommand); + this.ogCommandsService.ogInstructions = this.selectedCommand.script; + } + + + updateScript(i, value) { + this.selectedCommand.inputs[i] = value.target.value; + let script = this.selectedCommand.script; + for (let index = 0; index < this.selectedCommand.inputs.length; index++) { + script = script.replace('@' + (index + 1), this.selectedCommand.inputs[index]); + } + this.ogCommandsService.ogInstructions = script; + + } + + getParamsNumber(command) { + const params = []; + if (command.parameters === true) { + const allparams = command.script.match(/@[1-9]+/g)||[]; + for (let index = 0; index < allparams.length; index++) { + if (params.indexOf(allparams[index]) === -1) { + params.push(allparams[index]); + } + } + this.selectedCommand.inputs = params; + } + return params.length; + } + +} 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/login-command/login-command.component.html b/admin/WebConsole3/frontend/src/app/pages/command/login-command/login-command.component.html new file mode 100644 index 00000000..e8bcdbc5 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/login-command/login-command.component.html @@ -0,0 +1,81 @@ +<section class="content-header"> + <h1 translate="login_command"> + </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="login_command"></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"> + <form role="form" name="vm.Form"> + <div *ngFor="let client of selectedClients"> + <table class="table" *ngIf="client && client.id" > + <thead> + <tr> + <th colspan="5"> + {{client.name}} + </th> + </tr> + <tr> + <th translate="select"> + </th> + <th translate="disk"> + </th> + <th translate="partition"> + </th> + <th translate="filesystem"> + </th> + <th translate="osname"> + </th> + </tr> + </thead> + <tbody> + <ng-container *ngFor="let partition of client.partitions"> + <tr *ngIf="canLogin(partition)"> + <td> + <input icheck type="radio" name="partition" checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" class="selection-checkbox" [(ngModel)]="selectedPartition" [value]="partition" /> + </td> + <td> + {{partition.numDisk}} + </td> + <td> + {{partition.numPartition}} + </td> + <td> + {{partition.filesystem}} + </td> + <td> + {{partition.osName}} + </td> + </tr> + </ng-container> + </tbody> + </table> + </div> + </form> + </div> + </div> + </div> + </div> +</section> diff --git a/admin/WebConsole3/frontend/src/app/pages/command/login-command/login-command.component.scss b/admin/WebConsole3/frontend/src/app/pages/command/login-command/login-command.component.scss new file mode 100644 index 00000000..ccb34440 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/login-command/login-command.component.scss @@ -0,0 +1,3 @@ +command { + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/command/login-command/login-command.component.ts b/admin/WebConsole3/frontend/src/app/pages/command/login-command/login-command.component.ts new file mode 100644 index 00000000..af234984 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/command/login-command/login-command.component.ts @@ -0,0 +1,80 @@ +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 {ImageService} from '../../../api/image.service'; +import {OGCommandsService} from '../../../service/og-commands.service'; +import {Client} from '../../../model/client'; +import {CommandService} from '../../../api/command.service'; + +@Component({ + selector: 'app-login-command', + templateUrl: './login-command.component.html', + styleUrls: [ './login-command.component.scss' ] +}) +export class LoginCommandComponent implements OnInit { + execution = {clients: '', script: '', type: ''}; + command = {}; + user: User; + selectedClients: Client[]; + selectedPartition: any; + client: Client; + + // 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 ogSweetAlert: OgSweetAlertService, + private toaster: ToasterService, + private translate: TranslateService) { + this.user = this.authModule.getLoggedUser(); + } + + + + ngOnInit() { + this.selectedClients = this.ogCommonService.selectedClients; + if (this.user && this.selectedClients) { + this.execution.clients = Object.keys(this.selectedClients).join(','); + } + } + + + sendCommand() { + if (!this.selectedPartition) { + this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('you_must_select_partition')}); + } else { + const disk = this.selectedPartition.numDisk; + const partition = this.selectedPartition.numPartition; + + this.execution.script = 'bootOs ' + disk + ' ' + partition + ' &'; + 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}); + } + ); + } + } + + + canLogin(partition) { + return partition.osName !== '' && partition.osName !== 'DATA'; + } + +} 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-commands-options/og-commands-options.component.html b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-commands-options/og-commands-options.component.html index 7f114997..c008d3a1 100644 --- a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-commands-options/og-commands-options.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-commands-options/og-commands-options.component.html @@ -6,14 +6,14 @@ <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)"(click)="ogCommandsService.execute('POWER_OFF')" translate="power_off"></a></li> <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)"(click)="ogCommandsService.execute('POWER_ON')" translate="power_on"></a></li> <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)"(click)="ogCommandsService.execute('REBOOT')" translate="reboot"></a></li> - <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'app/commands/login'" translate="login_command"></a></li> + <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'/app/commands/login'" translate="login_command"></a></li> <li role="presentation" class="divider"></li> - <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'app/commands/execute'" translate="execute_command"></a></li> - <li role="presentation" class="{{ogCommonService.getSelectionSize() != 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'app/commands/create_image'" translate="create_image"></a></li> - <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)" [routerLink]="'app/commands/deploy_image'" translate="deploy_image"></a></li> - <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)" [routerLink]="'app/commands/delete_cache_image'" translate="delete_cache_image"></a></li> - <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'app/commands/partition_format'" translate="partition_format"></a></li> - <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'app/commands/format'" translate="format"></a></li> + <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'/app/commands/execute'" translate="execute_command"></a></li> + <li role="presentation" class="{{ogCommonService.getSelectionSize() != 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'/app/commands/create_image'" translate="create_image"></a></li> + <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)" [routerLink]="'/app/commands/deploy_image'" translate="deploy_image"></a></li> + <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)" [routerLink]="'/app/commands/delete_cache_image'" translate="delete_cache_image"></a></li> + <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'/app/commands/partition_format'" translate="partition_format"></a></li> + <li role="presentation" class="{{ogCommonService.getSelectionSize() < 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" [routerLink]="'/app/commands/format'" translate="format"></a></li> <li role="presentation" class="divider"></li> <li role="presentation" class="{{ogCommonService.getSelectionSize() != 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)"(click)="ogCommandsService.execute('SOFTWARE_INVENTORY')" translate="software_inventary"></a></li> <li role="presentation" class="{{ogCommonService.getSelectionSize() != 1?'disable-links':''}}"><a role="menuitem" tabindex="-1" href="javascript:void(0)"(click)="ogCommandsService.execute('HARDWARE_INVENTORY')" translate="hardware_inventary"></a></li> diff --git a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-execute-command-options/og-execute-command-options.component.html b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-execute-command-options/og-execute-command-options.component.html index efaabd65..2bc015f9 100644 --- a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-execute-command-options/og-execute-command-options.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-execute-command-options/og-execute-command-options.component.html @@ -1,3 +1,8 @@ -<p> - og-execute-command-options works! -</p> +<div class="btn-group"> + <button class="btn btn-default"(click)="ogCommandsService.execute('RUN_SCRIPT',{script: ogCommandsService.ogInstructions})"> + <span translate="execute"></span> + </button> + <button class="btn btn-primary"(click)="ogCommandsService.execute('RUN_SCRIPT',{script: ogCommandsService.ogInstructions, save: true})"> + <span translate="save"></span> + </button> +</div> diff --git a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-execute-command-options/og-execute-command-options.component.ts b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-execute-command-options/og-execute-command-options.component.ts index 55da5e36..86ccbf53 100644 --- a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-execute-command-options/og-execute-command-options.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-execute-command-options/og-execute-command-options.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import {OGCommandsService} from '../../../../service/og-commands.service'; @Component({ selector: 'app-og-execute-command-options', @@ -7,7 +8,7 @@ import { Component, OnInit } from '@angular/core'; }) export class OgExecuteCommandOptionsComponent implements OnInit { - constructor() { } + constructor(public ogCommandsService: OGCommandsService) { } ngOnInit() { } 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 e87b931a..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,3 +1,21 @@ -<p> - og-selected-clients works! -</p> +<div class="row"> + <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.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.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> + </div> +</div> diff --git a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-selected-clients/og-selected-clients.component.ts b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-selected-clients/og-selected-clients.component.ts index 9c6f1c4e..a6c7a606 100644 --- a/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-selected-clients/og-selected-clients.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/common/og-options/og-selected-clients/og-selected-clients.component.ts @@ -1,4 +1,6 @@ import { Component, OnInit } from '@angular/core'; +import {OgCommonService} from '../../../../service/og-common.service'; +import {Client} from '../../../../model/client'; @Component({ selector: 'app-og-selected-clients', @@ -6,10 +8,17 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./og-selected-clients.component.css'] }) export class OgSelectedClientsComponent implements OnInit { + public selectedClients: Client[]; - constructor() { } + constructor(public ogCommonService: OgCommonService) { + this.selectedClients = []; + } ngOnInit() { + this.selectedClients = this.ogCommonService.selectedClients; } + getClientInfo(c: Client) { + console.log(c); + } } 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/hardware/hardware-profiles/profiles-group/profiles-group.component.html b/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-group/hardware-profiles-group.component.html index 365ceebe..d7a73e89 100644 --- a/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-group/profiles-group.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-group/hardware-profiles-group.component.html @@ -1,47 +1,47 @@ -<mk-box boxColor="warning" headerBorder="true" isCollapsable="true" [isRemovable]="false">
- <mk-box-header class="box-header with-border">
- <i class="fa fa-folder-open-o"></i>
- <h3 class="box-title">
- <div class="btn-group" *ngIf="!content.editing">
- <div mk-dropdown class="btn-group" [isWrapper]="false" >
- <mk-dropdown-toggle>
- <button #toggleElement type="button" class="btn btn-default dropdown-toggle">
- {{content.name}}
- <span class="fa fa-caret-down"></span>
- </button>
- </mk-dropdown-toggle>
- <mk-dropdown-menu>
- <li >
- <a role="menuitem" tabindex="-1" href="#" translate="add_group"></a>
- </li>
- <li >
- <a role="menuitem" tabindex="-1" href="#" translate="add_profile" [routerLink]="'/app/hardware/profile/create/' + content.id "></a>
- </li>
- <li class="divider"></li>
- <li>
- <a class="" href="javascript:void(0)" translate="edit" (click)="content.$$tmpName = content.name; content.editing = true"></a>
- </li>
- <li role="presentation" class="divider"></li>
- <li role="presentation" class="delete-option">
- <a class="" href="javascript:void(0)" translate="delete" (click)="deleteGroup(content.id)"></a>
- </li>
- </mk-dropdown-menu>
- </div>
- </div>
- <div *ngIf="content.editing" class="col-xs-6">
- <input type="text" class="form-control" [(ngModel)]="content.$$tmpName">
- <span class="input-group-btn">
- <button type="button" class="btn btn-default btn-flat" translate="done" (click)="changeGroupName(content)"></button>
- <button type="button" class="btn btn-info btn-flat" translate="cancel" (click)="content.editing = false"></button>
- </span>
- </div>
- </h3>
- <!-- /.box-tools -->
- </mk-box-header>
- <!-- /.box-header -->
- <mk-box-content class="box-body folders" style="display: block;">
- <app-profiles-group *ngFor="let content of content.groups" [content]="content">
- </app-profiles-group>
- <app-profiles-table *ngIf="content.profiles" [profiles]="content.profiles"></app-profiles-table>
- </mk-box-content>
-</mk-box>
+<mk-box boxColor="warning" headerBorder="true" isCollapsable="true" [isRemovable]="false"> + <mk-box-header class="box-header with-border"> + <i class="fa fa-folder-open-o"></i> + <h3 class="box-title"> + <div class="btn-group" *ngIf="!content.editing"> + <div mk-dropdown class="btn-group" [isWrapper]="false" > + <mk-dropdown-toggle> + <button #toggleElement type="button" class="btn btn-default dropdown-toggle"> + {{content.name}} + <span class="fa fa-caret-down"></span> + </button> + </mk-dropdown-toggle> + <mk-dropdown-menu> + <li > + <a role="menuitem" tabindex="-1" href="#" translate="add_group"></a> + </li> + <li > + <a role="menuitem" tabindex="-1" href="#" translate="add_profile" [routerLink]="'/app/hardware/profile/create/' + content.id "></a> + </li> + <li class="divider"></li> + <li> + <a class="" href="javascript:void(0)" translate="edit" (click)="content.$$tmpName = content.name; content.editing = true"></a> + </li> + <li role="presentation" class="divider"></li> + <li role="presentation" class="delete-option"> + <a class="" href="javascript:void(0)" translate="delete" (click)="deleteGroup(content.id)"></a> + </li> + </mk-dropdown-menu> + </div> + </div> + <div *ngIf="content.editing" class="col-xs-6"> + <input type="text" class="form-control" [(ngModel)]="content.$$tmpName"> + <span class="input-group-btn"> + <button type="button" class="btn btn-default btn-flat" translate="done" (click)="changeGroupName(content)"></button> + <button type="button" class="btn btn-info btn-flat" translate="cancel" (click)="content.editing = false"></button> + </span> + </div> + </h3> + <!-- /.box-tools --> + </mk-box-header> + <!-- /.box-header --> + <mk-box-content class="box-body folders" style="display: block;"> + <app-profiles-group *ngFor="let content of content.groups" [content]="content"> + </app-profiles-group> + <app-profiles-table *ngIf="content.profiles" [profiles]="content.profiles"></app-profiles-table> + </mk-box-content> +</mk-box> diff --git a/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-group/profiles-group.component.ts b/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-group/hardware-profiles-group.component.ts index f9dbd868..884bb680 100644 --- a/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-group/profiles-group.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-group/hardware-profiles-group.component.ts @@ -1,14 +1,14 @@ -import {ComponentMetadata} from 'codelyzer/angular/metadata';
-import {Component, Input} from '@angular/core';
-
-@Component({
- selector: 'app-profiles-group',
- templateUrl: 'profiles-group.component.html'
-})
-export class ProfilesGroupComponent {
- @Input() content;
-
- constructor() {
- console.log(this.content);
- }
-}
+import {ComponentMetadata} from 'codelyzer/angular/metadata'; +import {Component, Input} from '@angular/core'; + +@Component({ + selector: 'app-profiles-group', + templateUrl: 'hardware-profiles-group.component.html' +}) +export class HardwareProfilesGroupComponent { + @Input() content; + + constructor() { + console.log(this.content); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-table/profiles-table.component.html b/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-table/hardware-profiles-table.component.html index 45f17240..70bd2fd1 100644 --- a/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-table/profiles-table.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-table/hardware-profiles-table.component.html @@ -1,61 +1,61 @@ -<table class="table table-hover">
- <tbody>
- <tr>
- <th translate="description"></th>
- <th translate="windowsboot"></th>
- <th translate="options"></th>
- </tr>
- <tr *ngFor="let profile of profiles; let index = index" class="{{(index%2 == 0)?'odd':'even'}}">
- <td>
- <span *ngIf="profile.$$editing">
- <input type="text" class="form-control" [(ngModel)]="profile.$$tmpDescription">
- </span>
- <span *ngIf="!profile.$$editing">
- {{profile.description}}
- </span>
- </td>
- <td>
- <span *ngIf="profile.$$editing">
- <select class="form-control"[(ngModel)]="profile.$$tmpWindowsboot">
- <option [value]="item" *ngFor="let item of windowsboots"> {{item}}</option>
- </select>
- </span>
- <span *ngIf="!profile.$$editing">
- {{profile.windowsboot}}
- </span>
- </td>
- <td class="right">
- <ng-container *ngIf="profile.$$editing">
- <div class="btn-group ">
- <button class="btn btn-primary " translate="ok" (click)="saveHardwareProfile(profile)"></button>
- <button class="btn btn-default " translate="cancel" (click)="profile.$$editing = false"></button>
- </div>
- </ng-container>
- <ng-container *ngIf="!profile.$$editing">
- <app-table-action [rowData]="profile" [options]="tableOptions" (edit)="editHardwareProfile($event)" (delete)="deleteHardwareProfile($event)"></app-table-action>
-<!--
- <div class="btn-group visible-md visible-lg hidden-sm hidden-xs">
- <button class="btn btn-default" translate="edit" (click)="editHardwareProfile(profile)"></button>
- <button class="btn btn-default" href="javascript:void(0)" translate="config" (click)="goToEditProfile(profile)"></button>
- <button class="btn btn-danger " translate="delete" (click)="deleteHardwareProfile(profile)"></button>
- </div>
- <div class="btn-group hidden-md hidden-lg">
- <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
- <i class="fa fa-gear"></i>
- <span class="caret"></span>
- <span class="sr-only">Toggle Dropdown</span>
- </button>
- <ul class="dropdown-menu" role="menu">
- <li class="btn-group-vertical">
- <button class="btn btn-default" translate="edit" (click)="editHardwareProfile(profile)"></button>
- <button class="btn btn-default" href="javascript:void(0)" translate="config" ui-sref="goToEditProfile(profile)"></button>
- <button class="btn btn-danger" href="javascript:void(0)" translate="delete" (click)="deleteHardwareProfile(profile)"></button>
- </li>
- </ul>
- </div>
--->
- </ng-container>
- </td>
- </tr>
- </tbody>
- </table>
+<table class="table table-hover"> + <tbody> + <tr> + <th translate="description"></th> + <th translate="windowsboot"></th> + <th translate="options"></th> + </tr> + <tr *ngFor="let profile of profiles; let index = index" class="{{(index%2 == 0)?'odd':'even'}}"> + <td> + <span *ngIf="profile.$$editing"> + <input type="text" class="form-control" [(ngModel)]="profile.$$tmpDescription"> + </span> + <span *ngIf="!profile.$$editing"> + {{profile.description}} + </span> + </td> + <td> + <span *ngIf="profile.$$editing"> + <select class="form-control"[(ngModel)]="profile.$$tmpWindowsboot"> + <option [value]="item" *ngFor="let item of windowsboots"> {{item}}</option> + </select> + </span> + <span *ngIf="!profile.$$editing"> + {{profile.windowsboot}} + </span> + </td> + <td class="right"> + <ng-container *ngIf="profile.$$editing"> + <div class="btn-group "> + <button class="btn btn-primary " translate="ok" (click)="saveHardwareProfile(profile)"></button> + <button class="btn btn-default " translate="cancel" (click)="profile.$$editing = false"></button> + </div> + </ng-container> + <ng-container *ngIf="!profile.$$editing"> + <app-table-action [rowData]="profile" [options]="tableOptions" (edit)="editHardwareProfile($event)" (delete)="deleteHardwareProfile($event)"></app-table-action> +<!-- + <div class="btn-group visible-md visible-lg hidden-sm hidden-xs"> + <button class="btn btn-default" translate="edit" (click)="editHardwareProfile(profile)"></button> + <button class="btn btn-default" href="javascript:void(0)" translate="config" (click)="goToEditProfile(profile)"></button> + <button class="btn btn-danger " translate="delete" (click)="deleteHardwareProfile(profile)"></button> + </div> + <div class="btn-group hidden-md hidden-lg"> + <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> + <i class="fa fa-gear"></i> + <span class="caret"></span> + <span class="sr-only">Toggle Dropdown</span> + </button> + <ul class="dropdown-menu" role="menu"> + <li class="btn-group-vertical"> + <button class="btn btn-default" translate="edit" (click)="editHardwareProfile(profile)"></button> + <button class="btn btn-default" href="javascript:void(0)" translate="config" ui-sref="goToEditProfile(profile)"></button> + <button class="btn btn-danger" href="javascript:void(0)" translate="delete" (click)="deleteHardwareProfile(profile)"></button> + </li> + </ul> + </div> +--> + </ng-container> + </td> + </tr> + </tbody> + </table> diff --git a/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-table/profiles-table.component.ts b/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-table/hardware-profiles-table.component.ts index c1a155ff..76db2008 100644 --- a/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-table/profiles-table.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/hardware/hardware-profiles/profiles-table/hardware-profiles-table.component.ts @@ -1,101 +1,101 @@ -import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
-import {HardwareProfile} from '../../../../model/hardware-profile';
-import {HardwareProfileService} from '../../../../api/hardware-profile.service';
-import {ToasterService} from '../../../../service/toaster.service';
-import {TranslateService} from '@ngx-translate/core';
-import {OgSweetAlertService} from '../../../../service/og-sweet-alert.service';
-import {Router} from '@angular/router';
-import {environment} from '../../../../../environments/environment';
-import {PartitionInfo} from '../../../../model/image';
-import {Ng2TableActionComponent} from '../../../common/table-action/ng2-table-action.component';
-
-@Component({
- selector: 'app-profiles-table',
- templateUrl: 'profiles-table.component.html'
-})
-export class ProfilesTableComponent implements OnInit {
- @Input() profiles;
- windowsboots: any;
- tableOptions: any;
-
- public constructor(private router: Router, private hardwareProfileService: HardwareProfileService, private ogSweetAlert: OgSweetAlertService, private toaster: ToasterService, private translate: TranslateService) {
-
- }
- ngOnInit(): void {
- this.windowsboots = environment.windowsboots;
- this.tableOptions = {
- override: false,
- buttons: [
- {
- action: 'edit'
- },
- {
- label: 'configure',
- handler: (profile) => this.goToEditProfile(profile),
- classes: 'btn-default'
- },
- {
- action: 'delete'
- }
- ]
- };
- }
-
- editHardwareProfile(hardwareProfile) {
- hardwareProfile.$$editing = true;
- hardwareProfile.$$tmpName = hardwareProfile.name;
- hardwareProfile.$$tmpDescription = hardwareProfile.description;
- hardwareProfile.$$tmpWindowsboot = hardwareProfile.windowsboot;
- }
-
- saveHardwareProfile(hardwareProfile) {
- hardwareProfile.$$editing = false;
- hardwareProfile.name = hardwareProfile.$$tmpName;
- hardwareProfile.description = hardwareProfile.$$tmpDescription;
- hardwareProfile.windowsboot = hardwareProfile.$$tmpWindowsboot;
- const hpCopy = Object.assign({}, hardwareProfile);
- // TODO - Llamar al servidor para guardar el cambio
- this.hardwareProfileService.update(hpCopy).subscribe(
- (response) => {
- this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_saved')});
- },
- (error) => {
- this.toaster.pop({type: 'error', title: 'error', body: error});
- }
- );
- }
-
- deleteHardwareProfile(hardwareProfile) {
- const self = this;
-
- this.ogSweetAlert.question(this.translate.instant('sure_to_delete') + '?', this.translate.instant('action_cannot_be_undone'),
- function(result) {
- if (result.value === true) {
- self.hardwareProfileService.delete(hardwareProfile.id).subscribe(
- (response) => {
- self.toaster.pop({type: 'success', title: self.translate.instant('success'), body: self.translate.instant('successfully_deleted')});
- const index = self.profiles.indexOf(hardwareProfile);
- if (index !== -1) {
- self.profiles.splice(index, 1);
- }
- },
- (error) => {
- self.toaster.pop({type: 'error', title: self.translate.instant('error'), body: error});
- }
- );
- }
- }
- );
- }
-
- goToEditProfile(profile: HardwareProfile) {
- this.router.navigate(['/app/hardware/profile', profile.id]).then(
- success => {
- console.log(success);
- },
- error => {
- console.log(error);
- }
- );
- }
-}
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {HardwareProfile} from '../../../../model/hardware-profile'; +import {HardwareProfileService} from '../../../../api/hardware-profile.service'; +import {ToasterService} from '../../../../service/toaster.service'; +import {TranslateService} from '@ngx-translate/core'; +import {OgSweetAlertService} from '../../../../service/og-sweet-alert.service'; +import {Router} from '@angular/router'; +import {environment} from '../../../../../environments/environment'; +import {PartitionInfo} from '../../../../model/image'; +import {Ng2TableActionComponent} from '../../../common/table-action/ng2-table-action.component'; + +@Component({ + selector: 'app-profiles-table', + templateUrl: 'hardware-profiles-table.component.html' +}) +export class HardwareProfilesTableComponent implements OnInit { + @Input() profiles; + windowsboots: any; + tableOptions: any; + + public constructor(private router: Router, private hardwareProfileService: HardwareProfileService, private ogSweetAlert: OgSweetAlertService, private toaster: ToasterService, private translate: TranslateService) { + + } + ngOnInit(): void { + this.windowsboots = environment.windowsboots; + this.tableOptions = { + override: false, + buttons: [ + { + action: 'edit' + }, + { + label: 'configure', + handler: (profile) => this.goToEditProfile(profile), + classes: 'btn-default' + }, + { + action: 'delete' + } + ] + }; + } + + editHardwareProfile(hardwareProfile) { + hardwareProfile.$$editing = true; + hardwareProfile.$$tmpName = hardwareProfile.name; + hardwareProfile.$$tmpDescription = hardwareProfile.description; + hardwareProfile.$$tmpWindowsboot = hardwareProfile.windowsboot; + } + + saveHardwareProfile(hardwareProfile) { + hardwareProfile.$$editing = false; + hardwareProfile.name = hardwareProfile.$$tmpName; + hardwareProfile.description = hardwareProfile.$$tmpDescription; + hardwareProfile.windowsboot = hardwareProfile.$$tmpWindowsboot; + const hpCopy = Object.assign({}, hardwareProfile); + // TODO - Llamar al servidor para guardar el cambio + this.hardwareProfileService.update(hpCopy).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_saved')}); + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + + deleteHardwareProfile(hardwareProfile) { + const self = this; + + this.ogSweetAlert.question(this.translate.instant('sure_to_delete') + '?', this.translate.instant('action_cannot_be_undone'), + function(result) { + if (result.value === true) { + self.hardwareProfileService.delete(hardwareProfile.id).subscribe( + (response) => { + self.toaster.pop({type: 'success', title: self.translate.instant('success'), body: self.translate.instant('successfully_deleted')}); + const index = self.profiles.indexOf(hardwareProfile); + if (index !== -1) { + self.profiles.splice(index, 1); + } + }, + (error) => { + self.toaster.pop({type: 'error', title: self.translate.instant('error'), body: error}); + } + ); + } + } + ); + } + + goToEditProfile(profile: HardwareProfile) { + this.router.navigate(['/app/hardware/profile', profile.id]).then( + success => { + console.log(success); + }, + error => { + console.log(error); + } + ); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/image/edit/image-edit.component.ts b/admin/WebConsole3/frontend/src/app/pages/image/edit/image-edit.component.ts index 1117762b..8bc33f44 100644 --- a/admin/WebConsole3/frontend/src/app/pages/image/edit/image-edit.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/image/edit/image-edit.component.ts @@ -1,76 +1,100 @@ -import {Component, OnInit} from '@angular/core';
-
-import { ImageService } from 'src/app/api/image.service';
-import {Image, PartitionInfo} from 'src/app/model/image';
-
-import {ActivatedRoute, Router} from '@angular/router';
-import {ToasterService} from '../../../service/toaster.service';
-import {TranslateService} from '@ngx-translate/core';
-import {ImageFormType} from '../../../form-type/image.form-type';
-import {RepositoryService} from '../../../api/repository.service';
-
-@Component({
- selector: 'app-image',
- templateUrl: './image-edit.component.html',
- styleUrls: [ './image-edit.component.scss' ]
-})
-export class ImageEditComponent implements OnInit {
- image: Image;
- constants: any;
- private formType = new ImageFormType();
- public form: any;
-
- // this tells the tabs component which Pages
- // should be each tab's root Page
- constructor(private router: Router, private activatedRouter: ActivatedRoute, private imageService: ImageService, private repositoryService: RepositoryService, private translate: TranslateService, private toaster: ToasterService) {
- this.form = this.formType.getForm();
- }
-
- ngOnInit(): void {
- this.image = new Image();
- this.activatedRouter.paramMap.subscribe(
- (data: any) => {
- if (data.params.id) {
- this.imageService.read(data.params.id).subscribe(
- image => {
- this.image = image;
- },
- error => {
- this.toaster.pop({type: 'error', title: 'error', body: error});
- }
- );
- }
- }
- );
- this.repositoryService.list().subscribe(
- data => {
- this.formType.getField(this.form, 'repository').options = {
- items: data,
- label: 'name',
- value: 'id'
- }
- }
- )
- }
-
- 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;
- }
-
-}
+import {Component, OnInit} from '@angular/core'; + +import { ImageService } from 'src/app/api/image.service'; +import {Image, PartitionInfo} from 'src/app/model/image'; + +import {ActivatedRoute, Router} from '@angular/router'; +import {ToasterService} from '../../../service/toaster.service'; +import {TranslateService} from '@ngx-translate/core'; +import {ImageFormType} from '../../../form-type/image.form-type'; +import {RepositoryService} from '../../../api/repository.service'; +import {Observable} from 'rxjs'; + +@Component({ + selector: 'app-image', + templateUrl: './image-edit.component.html', + styleUrls: [ './image-edit.component.scss' ] +}) +export class ImageEditComponent implements OnInit { + image: Image; + constants: any; + private formType = new ImageFormType(); + public form: any; + + // this tells the tabs component which Pages + // should be each tab's root Page + constructor(private router: Router, private activatedRouter: ActivatedRoute, private imageService: ImageService, private repositoryService: RepositoryService, private translate: TranslateService, private toaster: ToasterService) { + this.form = this.formType.getForm(); + } + + ngOnInit(): void { + this.image = new Image(); + this.activatedRouter.paramMap.subscribe( + (data: any) => { + if (data.params.id) { + this.imageService.read(data.params.id).subscribe( + image => { + this.image = image; + if (typeof this.image.partitionInfo === 'string') { + this.image.partitionInfo = JSON.parse(this.image.partitionInfo); + } else if (!this.image.partitionInfo) { + this.image.partitionInfo = new PartitionInfo(); + } + }, + error => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + } + ); + this.repositoryService.list().subscribe( + data => { + this.formType.getField(this.form, 'repository').options = { + items: data, + label: 'name', + value: 'id' + }; + } + ); + } + + 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; + } + + save() { + let request: Observable<any>; + if (this.image.id !== 0) { + request = this.imageService.update(this.image); + } else { + request = this.imageService.create(this.image); + } + request.subscribe( + (response) => { + this.toaster.pop({type: 'success', title: this.translate.instant('success'), body: this.translate.instant('successfully_saved')}); + this.router.navigate(['/app/images']); + + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/image/image.component.html b/admin/WebConsole3/frontend/src/app/pages/image/image.component.html index 09ba6036..a5b700ac 100644 --- a/admin/WebConsole3/frontend/src/app/pages/image/image.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/image/image.component.html @@ -9,16 +9,16 @@ </section> <section fixed-toolboxbar class="toolboxbar"> <div class="row"> - <div class="col-10"> + <div class="col-md-10 col-10"> <div class="input-group"> - <input type="text" name="q" class="form-control" placeholder="{{'search'|translate}}..." ng-model="vm.ngTableSearch.text"> + <input type="text" name="q" class="form-control" placeholder="{{'search'|translate}}..." > <span class="input-group-btn"> <button type="button" name="search" id="search-btn" class="btn btn-flat btn-default"><i class="fa fa-search"></i> </button> </span> </div> </div> - <div class="col-2" style="margin-top: 5px;margin-bottom: 5px;"> + <div class="col-md-2 col-2" style="margin-top: 5px;margin-bottom: 5px;"> <div class="box-tools"> <mk-dropdown class="btn-group" [isWrapper]="false" > <mk-dropdown-toggle> 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 new file mode 100644 index 00000000..dd147bf3 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.html @@ -0,0 +1,37 @@ +<section class="content-header"> + <h1 translate="new_image"> + </h1> + <ol class="breadcrumb"> + <li><a [routerLink]="'/app/dashboard'"><i class="fa fa-dashboard"></i> {{'dashboard'|translate}}</a></li> + <li><a [routerLink]="'/app/images'"><i class="fa fa-cubes"></i> {{'images'|translate}}</a></li> + <li class="active" translate="edit_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="save" (click)="save()"></button> + </div> + </div> + </div> +</section> +<!-- Main content --> +<section class="content"> + <div class="row"> + <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" 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.scss b/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.scss new file mode 100644 index 00000000..1382f194 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.scss @@ -0,0 +1,3 @@ +image { + +} 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 new file mode 100644 index 00000000..c16bf372 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/menu/edit/menu-edit.component.ts @@ -0,0 +1,78 @@ +import {Component, OnInit} from '@angular/core'; + + +import {ActivatedRoute, Router} from '@angular/router'; +import {ToasterService} from '../../../service/toaster.service'; +import {TranslateService} from '@ngx-translate/core'; +import {MenuFormType} from '../../../form-type/menu.form-type'; +import {RepositoryService} from '../../../api/repository.service'; +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', + templateUrl: './menu-edit.component.html', + styleUrls: [ './menu-edit.component.scss' ] +}) +export class MenuEditComponent implements OnInit { + menu: Menu; + constants: any; + private formType = new MenuFormType(); + public form: any; + + // this tells the tabs component which Pages + // should be each tab's root Page + 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(); + } + + ngOnInit(): void { + this.menu = new Menu(); + this.ogCommonService.loadEngineConfig().subscribe( + data => { + this.formType.getField(this.form, 'resolution').options = { + items: data.constants.menus.resolutions, + label: 'text', + value: 'id' + }; + } + ); + this.activatedRouter.paramMap.subscribe( + (data: any) => { + if (data.params.id) { + this.menuService.read(data.params.id).subscribe( + menu => { + this.menu = menu; + }, + error => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + } + ); + } + + + save() { + let request: Observable<any>; + if (this.menu.id !== 0) { + request = this.menuService.update(this.menu); + } else { + request = this.menuService.create(this.menu); + } + request.subscribe( + (response) => { + this.toaster.pop({type: 'success', title: this.translate.instant('success'), body: this.translate.instant('successfully_saved')}); + this.router.navigate(['/app/menus']); + + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.html b/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.html index 93bab7ab..bc65667b 100644 --- a/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.html @@ -1 +1,51 @@ -<div>Html template for class Menu</div>
\ No newline at end of file +<div ui-view> + <section class="content-header"> + <h1 translate="menus"> + </h1> + <ol class="breadcrumb"> + <li><a [routerLink]="'app/dashboard'"><i class="fa fa-dashboard"></i> {{'dashboard'|translate}}</a></li> + <li class="active" translate="menus"></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'|translate}}..." ng-model="vm.ngTableSearch.text"> + <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"> + <a [routerLink]="'/app/menus/create'" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> + <span translate="new"></span> + </a> + </div> + </div> + </div> + </section> + <!-- Main content --> + <section class="content"> + <div class="row"> + <div class="col-md-12"> + <!-- Info boxes --> + <div class="box box-primary"> + <div class="box-header with-border"> + <h3 class="box-title" translate="image_list"></h3> + </div> + <div class="box-body"> + <ng2-smart-table [source]="menus" [settings]="tableSettings"></ng2-smart-table> + </div> + <div class="box-footer"> + </div> + <!-- /.col --> + </div> + </div> + </div> + <!-- /.row --> + </section> +</div> 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 a5cecb4e..2d803c86 100644 --- a/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/menu/menu.component.ts @@ -1,17 +1,105 @@ -import { Component } from '@angular/core';
-
-import { MenuService } from 'src/app/api/menu.service';
-import { Menu } from 'src/app/model/menu';
-
-@Component({
- selector: 'menu',
- templateUrl: './menu.component.html',
- styleUrls: [ './menu.component.scss' ]
-})
-export class MenuComponent {
- // this tells the tabs component which Pages
- // should be each tab's root Page
- constructor(public menuService: MenuService) {
- }
-
-}
+import {Component, OnInit} from '@angular/core'; + +import { MenuService } from 'src/app/api/menu.service'; +import { Menu } from 'src/app/model/menu'; +import {PartitionInfo} from '../../model/image'; +import {Ng2TableActionComponent} from '../common/table-action/ng2-table-action.component'; +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', + templateUrl: './menu.component.html', + styleUrls: [ './menu.component.scss' ] +}) +export class MenuComponent implements OnInit { + public menus: Menu[]; + 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 toaster: ToasterService, private translate: TranslateService) { + } + + ngOnInit(): void { + this.menuService.list().subscribe( + data => { + this.menus = data; + }, + error => { + + } + ); + const self = this; + this.tableSettings = { + columns: { + title: { + title: this.translate.instant('title') + }, + description: { + title: this.translate.instant('description') + }, + comments: { + title: this.translate.instant('comments'), + }, + resolution: { + title: this.translate.instant('resolution') + }, + options: { + title: 'Options', + filter: false, + sort: false, + type: 'custom', + renderComponent: Ng2TableActionComponent, + onComponentInitFunction(instance) { + instance.edit.subscribe(row => { + self.router.navigate(['/app/menus/edit/', row.id]); + }); + instance.delete.subscribe(row => { + self.deleteMenu(row); + }); + } + }, + }, + actions: { + position: 'right', + add: false, + edit: false, + delete: false + } + }; + } + + deleteMenu(menu) { + const self = this; + this.ogSweetAlert.swal({ + title: this.translate.instant('sure_to_delete') + '?', + message: this.translate.instant('action_cannot_be_undone'), + type: 'warning', + showCancelButton: true, + confirmButtonColor: '#3c8dbc', + confirmButtonText: this.translate.instant('yes_delete'), + closeOnConfirm: true + }).then( + function(result) { + if (result.value === true) { + + 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 = self.menus.indexOf(menu); + if (index !== -1) { + self.menus.splice(menu, 1); + } + }, + (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 86508a31..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 @@ -24,7 +24,7 @@ </td> <td class="right"> <div class="btn-group visible-md visible-lg hidden-sm hidden-xs"> - <button class="btn btn-default " translate="edit" [routerLink]="'app/client.edit({ou: content.id, clientId: client.id})'"></button> + <button class="btn btn-default " translate="edit" [routerLink]="'/app/clients/edit/'+client.id"></button> <button class="btn btn-danger " translate="delete" (click)="deleteClient(client)"></button> </div> <div class="btn-group hidden-md hidden-lg"> @@ -35,7 +35,7 @@ </button> <ul class="dropdown-menu" role="menu"> <li class="btn-group-vertical"> - <button class="btn btn-default" href="javascript:void(0)" translate="edit" [routerLink]="'app/client.edit({ou: content.id, clientId: client.id})'"></button> + <button class="btn btn-default" href="javascript:void(0)" translate="edit" [routerLink]="'/app/clients/edit/'+client.id"></button> <button class="btn btn-danger" href="javascript:void(0)" translate="delete" (click)="deleteClient(client)"></button> </li> </ul> @@ -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 b65b2ffa..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; @@ -41,6 +42,7 @@ export class OuClientComponent { } deleteClient(client) { + const self = this; this.ogSweetAlert.swal( { title: this.translate.instant('sure_to_delete') + '?', @@ -52,18 +54,18 @@ export class OuClientComponent { }).then( function(response) { - if (response === true) { - this.clientService.delete(client.id).then( - function(success) { + if (response.value === true) { + self.clientService.delete(client.id).subscribe( + (success) => { // Lo borramos de la unidad organizativa - const index = this.ou.clients.indexOf(client); + const index = self.ou.clients.indexOf(client); if (index !== -1) { - this.ou.clients.splice(index, 1); + self.ou.clients.splice(index, 1); } - this.toaster.pop({type: 'success', title: 'success', body: 'Successfully deleted'}); + self.toaster.pop({type: 'success', title: 'success', body: 'Successfully deleted'}); }, - 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/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-components/software-components-group/software-components-group.component.html b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-group/software-components-group.component.html new file mode 100644 index 00000000..65c3cacb --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-group/software-components-group.component.html @@ -0,0 +1,45 @@ +<mk-box [boxColor]="'warning'" [isRemovable]="false"> + <mk-box-header class="box-header with-border"> + <i class="fa fa-folder-open-o"></i> + <h3 class="box-title"> + <div mk-dropdown class="btn-group" *ngIf="!content.$$editing"> + <mk-dropdown-toggle> + <button #toggleElement type="button" class="btn btn-default dropdown-toggle" > + {{content.name}} + <span class="fa fa-caret-down"></span> + </button> + </mk-dropdown-toggle> + <mk-dropdown-menu> + <li role="presentation"> + <a role="menuitem" tabindex="-1" href="#" translate="add_group"></a> + </li> + <li role="presentation"> + <a role="menuitem" tabindex="-1" href="#" translate="add_software_component"></a> + </li> + <li role="presentation" class="divider"></li> + <li> + <a class="" href="javascript:void(0)" translate="edit" (click)="content.$$tmpDescription = content.description; content.$$editing = true"></a> + </li> + <li role="presentation" class="divider"></li> + <li role="presentation" class="delete-option"> + <a class="" href="javascript:void(0)" translate="delete" (click)="vm.deleteGroup(content.id)"></a> + </li> + </mk-dropdown-menu> + </div> + <div *ngIf="content.$$editing" class="col-xs-6"> + <input type="text" class="form-control" [(ngModel)]="content.$$tmpDescription"> + <span class="input-group-btn"> + <button type="button" class="btn btn-default btn-flat" translate="done" (click)="changeGroupName(content)"></button> + <button type="button" class="btn btn-info btn-flat" translate="cancel" (click)="content.$$editing = false"></button> + </span> + </div> + </h3> + <!-- /.box-tools --> + </mk-box-header> + <!-- /.box-header --> + <mk-box-content class="box-body folders" style="display: block;"> + <app-software-component-group *ngFor="let content of content.groups" [content]="content" [softwareTypes]="softwareTypes"> + </app-software-component-group> + <app-software-components-table *ngIf="content.components" [components]="content.components" [softwareTypes]="softwareTypes"></app-software-components-table> + </mk-box-content> +</mk-box> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-group/software-components-group.component.ts b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-group/software-components-group.component.ts new file mode 100644 index 00000000..d27504a4 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-group/software-components-group.component.ts @@ -0,0 +1,10 @@ +import {Component, Input} from '@angular/core'; + +@Component({ + selector: 'app-software-component-group', + templateUrl: 'software-components-group.component.html' +}) +export class SoftwareComponentsGroupComponent { + @Input() content; + @Input() softwareTypes; +} diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-table/software-components-table.component.html b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-table/software-components-table.component.html new file mode 100644 index 00000000..fb4bdbd2 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-table/software-components-table.component.html @@ -0,0 +1,43 @@ +<table class="table table-hover"> + <tbody> + <tr> + <th translate="description"></th> + <th translate="type"></th> + <!--th translate="imageUrl"></th--> + <th translate="options"></th> + </tr> + <tr *ngFor="let softwareComponent of components; let index = index" class="{{(index%2 == 0)?'odd':'even'}}"> + <td> + <span *ngIf="softwareComponent.$$editing"> + <input type="text" class="form-control" [(ngModel)]="softwareComponent.$$tmpDescription"> + </span> + <span *ngIf="!softwareComponent.$$editing"> + {{softwareComponent.description}} + </span> + </td> + <td> + <span *ngIf="softwareComponent.$$editing"> + <select class="form-control" [(ngModel)]="softwareComponent.$$tmpType"> + <option [value]="softwareType.nemonic" *ngFor="let softwareType of softwareTypes">{{softwareType.name}}</option> + </select> + </span> + <span *ngIf="!softwareComponent.$$editing"> + <select disabled="disabled" class="form-control" [(ngModel)]="softwareComponent.type"> + <option [value]="softwareType.nemonic" *ngFor="let softwareType of softwareTypes">{{softwareType.name}}</option> + </select> + </span> + </td> + <td class="right"> + <span *ngIf="softwareComponent.$$editing"> + <div class="btn-group "> + <button class="btn btn-primary " translate="ok" (click)="saveSoftwareComponent(softwareComponent)"></button> + <button class="btn btn-default " translate="cancel" (click)="softwareComponent.$$editing = false"></button> + </div> + </span> + <span *ngIf="!softwareComponent.$$editing"> + <app-table-action [rowData]="softwareComponent" [options]="tableOptions" (edit)="editSoftwareComponent($event)" (delete)="deleteSoftwareComponent($event)"></app-table-action> + </span> + </td> + </tr> + </tbody> +</table> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-table/software-components-table.component.ts b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-table/software-components-table.component.ts new file mode 100644 index 00000000..30341c92 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components-table/software-components-table.component.ts @@ -0,0 +1,84 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {ToasterService} from '../../../../service/toaster.service'; +import {TranslateService} from '@ngx-translate/core'; +import {OgSweetAlertService} from '../../../../service/og-sweet-alert.service'; +import {Router} from '@angular/router'; +import {SoftwareComponent} from '../../../../model/software-component'; +import {SoftwareComponentService} from '../../../../api/software-component.service'; +import {environment} from '../../../../../environments/environment'; + +@Component({ + selector: 'app-software-components-table', + templateUrl: 'software-components-table.component.html' +}) +export class SoftwareComponentsTableComponent implements OnInit { + @Input() components; + @Input() softwareTypes; + public tableOptions: { buttons: ({ action: string } | { handler: (profile) => any; classes: string; label: string })[]; override: boolean }; + + public constructor(private router: Router, private softwareComponentService: SoftwareComponentService, private ogSweetAlert: OgSweetAlertService, private toaster: ToasterService, private translate: TranslateService) { + } + + ngOnInit(): void { + this.tableOptions = { + override: false, + buttons: [ + { + action: 'edit' + }, + { + action: 'delete' + } + ] + }; + } + + editSoftwareComponent(softwareComponent) { + softwareComponent.$$editing = true; + softwareComponent.$$tmpDescription = softwareComponent.description; + softwareComponent.$$tmpType = softwareComponent.type; + } + saveSoftwareComponent(softwareComponent: any) { + softwareComponent.$$editing = false; + softwareComponent.description = softwareComponent.$$tmpDescription; + softwareComponent.type = softwareComponent.$$tmpType; + const hcCopy = Object.assign({}, softwareComponent); + + // TODO - Llamar al servidor para guardar el cambio + this.softwareComponentService.update(hcCopy).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: this.translate.instant('success'), body: this.translate.instant('successfully_saved')}); + }, + (error) => { + this.toaster.pop({type: 'error', title: this.translate.instant('error'), body: error}); + } + ); + } + + deleteSoftwareComponent(softwareComponent: SoftwareComponent) { + this.ogSweetAlert.swal({ + title: this.translate.instant('sure_to_delete') + '?', + text: this.translate.instant('action_cannot_be_undone'), + type: 'warning', + showCancelButton: true, + confirmButtonColor: '#3c8dbc', + confirmButtonText: this.translate.instant('yes_delete'), + closeOnConfirm: true}).then( + function(result) { + if (result === true) { + this.softwareComponentService.delete(softwareComponent.id).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: this.translate.instant('success'), body: this.translate.instant('successfully_deleted')}); + const index = this.softwareComponentsGroups[0].components.indexOf(softwareComponent); + if (index !== -1) { + this.softwareComponentsGroups[0].components.splice(index, 1); + } + }, + (error) => { + this.toaster.pop({type: 'error', title: this.translate.instant('error'), body: error}); + } + ); + } + }); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.css b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.css diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.html b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.html new file mode 100644 index 00000000..043210de --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.html @@ -0,0 +1 @@ +<app-software-component-group *ngFor="let content of softwareComponentsGroups" [content]="content" [softwareTypes]="softwareTypes"></app-software-component-group> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.ts b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.ts new file mode 100644 index 00000000..78e80784 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-components/software-components.component.ts @@ -0,0 +1,17 @@ +import {Component, Input, OnInit} from '@angular/core'; + +@Component({ + selector: 'app-software-components', + templateUrl: './software-components.component.html', + styleUrls: ['./software-components.component.css'] +}) +export class SoftwareComponentsComponent implements OnInit { + @Input() softwareTypes; + @Input() softwareComponentsGroups; + constructor() { } + + ngOnInit() { + console.log(this.softwareComponentsGroups); + } + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-group/software-profiles-group.component.html b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-group/software-profiles-group.component.html new file mode 100644 index 00000000..95b2879a --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-group/software-profiles-group.component.html @@ -0,0 +1,47 @@ +<mk-box boxColor="warning" headerBorder="true" isCollapsable="true" [isRemovable]="false"> + <mk-box-header class="box-header with-border"> + <i class="fa fa-folder-open-o"></i> + <h3 class="box-title"> + <div class="btn-group" *ngIf="!content.editing"> + <div mk-dropdown class="btn-group" [isWrapper]="false" > + <mk-dropdown-toggle> + <button #toggleElement type="button" class="btn btn-default dropdown-toggle"> + {{content.name}} + <span class="fa fa-caret-down"></span> + </button> + </mk-dropdown-toggle> + <mk-dropdown-menu> + <li > + <a role="menuitem" tabindex="-1" href="#" translate="add_group"></a> + </li> + <li > + <a role="menuitem" tabindex="-1" href="#" translate="add_profile" [routerLink]="'/app/software/profile/create/' + content.id "></a> + </li> + <li class="divider"></li> + <li> + <a class="" href="javascript:void(0)" translate="edit" (click)="content.$$tmpName = content.name; content.editing = true"></a> + </li> + <li role="presentation" class="divider"></li> + <li role="presentation" class="delete-option"> + <a class="" href="javascript:void(0)" translate="delete" (click)="deleteGroup(content.id)"></a> + </li> + </mk-dropdown-menu> + </div> + </div> + <div *ngIf="content.editing" class="col-xs-6"> + <input type="text" class="form-control" [(ngModel)]="content.$$tmpName"> + <span class="input-group-btn"> + <button type="button" class="btn btn-default btn-flat" translate="done" (click)="changeGroupName(content)"></button> + <button type="button" class="btn btn-info btn-flat" translate="cancel" (click)="content.editing = false"></button> + </span> + </div> + </h3> + <!-- /.box-tools --> + </mk-box-header> + <!-- /.box-header --> + <mk-box-content class="box-body folders" style="display: block;"> + <app-software-profiles-group *ngFor="let content of content.groups" [content]="content"> + </app-software-profiles-group> + <app-software-profiles-table *ngIf="content.profiles" [profiles]="content.profiles"></app-software-profiles-table> + </mk-box-content> +</mk-box> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-group/software-profiles-group.component.ts b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-group/software-profiles-group.component.ts new file mode 100644 index 00000000..e3e65420 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-group/software-profiles-group.component.ts @@ -0,0 +1,14 @@ +import {ComponentMetadata} from 'codelyzer/angular/metadata'; +import {Component, Input} from '@angular/core'; + +@Component({ + selector: 'app-software-profiles-group', + templateUrl: 'software-profiles-group.component.html' +}) +export class SoftwareProfilesGroupComponent { + @Input() content; + + constructor() { + console.log(this.content); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-table/software-profiles-table.component.html b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-table/software-profiles-table.component.html new file mode 100644 index 00000000..6f362fa9 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-table/software-profiles-table.component.html @@ -0,0 +1,61 @@ +<table class="table table-hover"> + <tbody> + <tr> + <th translate="description"></th> + <th translate="windowsboot"></th> + <th translate="options"></th> + </tr> + <tr *ngFor="let profile of profiles; let index = index" class="{{(index%2 == 0)?'odd':'even'}}"> + <td> + <span *ngIf="profile.$$editing"> + <input type="text" class="form-control" [(ngModel)]="profile.$$tmpDescription"> + </span> + <span *ngIf="!profile.$$editing"> + {{profile.description}} + </span> + </td> + <td> + <span *ngIf="profile.$$editing"> + <select class="form-control"[(ngModel)]="profile.$$tmpWindowsboot"> + <option [value]="item" *ngFor="let item of windowsboots"> {{item}}</option> + </select> + </span> + <span *ngIf="!profile.$$editing"> + {{profile.windowsboot}} + </span> + </td> + <td class="right"> + <ng-container *ngIf="profile.$$editing"> + <div class="btn-group "> + <button class="btn btn-primary " translate="ok" (click)="saveSoftwareProfile(profile)"></button> + <button class="btn btn-default " translate="cancel" (click)="profile.$$editing = false"></button> + </div> + </ng-container> + <ng-container *ngIf="!profile.$$editing"> + <app-table-action [rowData]="profile" [options]="tableOptions" (edit)="editSoftwareProfile($event)" (delete)="deleteSoftwareProfile($event)"></app-table-action> +<!-- + <div class="btn-group visible-md visible-lg hidden-sm hidden-xs"> + <button class="btn btn-default" translate="edit" (click)="editSoftwareProfile(profile)"></button> + <button class="btn btn-default" href="javascript:void(0)" translate="config" (click)="goToEditProfile(profile)"></button> + <button class="btn btn-danger " translate="delete" (click)="deleteSoftwareProfile(profile)"></button> + </div> + <div class="btn-group hidden-md hidden-lg"> + <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> + <i class="fa fa-gear"></i> + <span class="caret"></span> + <span class="sr-only">Toggle Dropdown</span> + </button> + <ul class="dropdown-menu" role="menu"> + <li class="btn-group-vertical"> + <button class="btn btn-default" translate="edit" (click)="editSoftwareProfile(profile)"></button> + <button class="btn btn-default" href="javascript:void(0)" translate="config" ui-sref="goToEditProfile(profile)"></button> + <button class="btn btn-danger" href="javascript:void(0)" translate="delete" (click)="deleteSoftwareProfile(profile)"></button> + </li> + </ul> + </div> +--> + </ng-container> + </td> + </tr> + </tbody> + </table> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-table/software-profiles-table.component.ts b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-table/software-profiles-table.component.ts new file mode 100644 index 00000000..9ea76af2 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles-table/software-profiles-table.component.ts @@ -0,0 +1,100 @@ +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {SoftwareProfile} from '../../../../model/software-profile'; +import {SoftwareProfileService} from '../../../../api/software-profile.service'; +import {ToasterService} from '../../../../service/toaster.service'; +import {TranslateService} from '@ngx-translate/core'; +import {OgSweetAlertService} from '../../../../service/og-sweet-alert.service'; +import {Router} from '@angular/router'; +import {environment} from '../../../../../environments/environment'; + + +@Component({ + selector: 'app-software-profiles-table', + templateUrl: 'software-profiles-table.component.html' +}) +export class SoftwareProfilesTableComponent implements OnInit { + @Input() profiles; + windowsboots: any; + tableOptions: any; + + public constructor(private router: Router, private softwareProfileService: SoftwareProfileService, private ogSweetAlert: OgSweetAlertService, private toaster: ToasterService, private translate: TranslateService) { + + } + ngOnInit(): void { + this.windowsboots = environment.windowsboots; + this.tableOptions = { + override: false, + buttons: [ + { + action: 'edit' + }, + { + label: 'configure', + handler: (profile) => this.goToEditProfile(profile), + classes: 'btn-default' + }, + { + action: 'delete' + } + ] + }; + } + + editSoftwareProfile(softwareProfile) { + softwareProfile.$$editing = true; + softwareProfile.$$tmpName = softwareProfile.name; + softwareProfile.$$tmpDescription = softwareProfile.description; + softwareProfile.$$tmpWindowsboot = softwareProfile.windowsboot; + } + + saveSoftwareProfile(softwareProfile) { + softwareProfile.$$editing = false; + softwareProfile.name = softwareProfile.$$tmpName; + softwareProfile.description = softwareProfile.$$tmpDescription; + softwareProfile.windowsboot = softwareProfile.$$tmpWindowsboot; + const hpCopy = Object.assign({}, softwareProfile); + // TODO - Llamar al servidor para guardar el cambio + this.softwareProfileService.update(hpCopy).subscribe( + (response) => { + this.toaster.pop({type: 'success', title: 'success', body: this.translate.instant('successfully_saved')}); + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + + deleteSoftwareProfile(softwareProfile) { + const self = this; + + this.ogSweetAlert.question(this.translate.instant('sure_to_delete') + '?', this.translate.instant('action_cannot_be_undone'), + function(result) { + if (result.value === true) { + self.softwareProfileService.delete(softwareProfile.id).subscribe( + (response) => { + self.toaster.pop({type: 'success', title: self.translate.instant('success'), body: self.translate.instant('successfully_deleted')}); + const index = self.profiles.indexOf(softwareProfile); + if (index !== -1) { + self.profiles.splice(index, 1); + } + }, + (error) => { + self.toaster.pop({type: 'error', title: self.translate.instant('error'), body: error}); + } + ); + } + } + ); + } + + goToEditProfile(profile: SoftwareProfile) { + this.router.navigate(['/app/software/profile', profile.id]).then( + success => { + console.log(success); + }, + error => { + console.log(error); + } + ); + } +} diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.css b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.css diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.html b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.html new file mode 100644 index 00000000..adf2e70b --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.html @@ -0,0 +1,3 @@ +<!-- Nested node template --> + +<app-software-profiles-group *ngFor="let content of softwareProfileGroups" [content]="content"></app-software-profiles-group> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.ts b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.ts new file mode 100644 index 00000000..d7d3987d --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-profiles/software-profiles.component.ts @@ -0,0 +1,19 @@ +import {Component, Input, OnInit} from '@angular/core'; + +@Component({ + selector: 'app-software-profiles', + templateUrl: './software-profiles.component.html', + styleUrls: ['./software-profiles.component.css'] +}) +export class SoftwareProfilesComponent implements OnInit { + + @Input() softwareProfileGroups; + + constructor() { + } + + ngOnInit() { + console.log(this.softwareProfileGroups); + } + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.css b/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.css diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.html b/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.html new file mode 100644 index 00000000..ceda7046 --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.html @@ -0,0 +1,66 @@ +<div class="box box-warning" > + <div class="box-header with-border"> + <i class="fa fa-folder-open-o"></i> + <h3 class="box-title"> + <div class="btn-group"> + <div> + <a href="javascript:void(0)" data-toggle="dropdown" class="dropdown-toggle" translate="software_types"> + </a> + <ul class="dropdown-menu"> + <li role="presentation"> + <a role="menuitem" tabindex="-1" href="#" translate="add_software_type"></a> + </li> + </ul> + </div> + </div> + </h3> + <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 folders" style="display: block;"> + <table class="table table-hover"> + <tbody><tr> + <th translate="name"></th> + <th translate="options"></th> + </tr> + <tr *ngFor="let softwareType of softwareTypes; let index = index;" class="{{( index%2 == 0)?'odd':'even'}}"> + <td> + <span *ngIf="!softwareType.$$editing">{{softwareType.name}}</span> + <span *ngIf="softwareType.$$editing"><input type="text" class="form-control" [(ngModel)]="softwareType.$$tmpName" /></span> + </td> + <td class="right"> + <span *ngIf="softwareType.$$editing"> + <div class="btn-group "> + <button class="btn btn-primary " translate="ok" (click)="saveSoftwareType(softwareType)"></button> + <button class="btn btn-default " translate="cancel" (click)="softwareType.$$editing = false"></button> + </div> + </span> + <span *ngIf="!softwareType.$$editing"> + <div class="btn-group visible-md visible-lg hidden-sm hidden-xs"> + <button class="btn btn-default " translate="edit" (click)="editSoftwareType(softwareType)"></button> + <button class="btn btn-danger " translate="delete" (click)="deleteClient(client.id)"></button> + </div> + <div class="btn-group hidden-md hidden-lg"> + <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> + <i class="fa fa-gear"></i> + <span class="caret"></span> + <span class="sr-only">Toggle Dropdown</span> + </button> + <ul class="dropdown-menu" role="menu"> + <li class="btn-group-vertical"> + <button class="btn btn-default" href="javascript:void(0)" translate="edit" (click)="editSoftwareType(softwareType)"></button> + <button class="btn btn-danger" href="javascript:void(0)" translate="delete" (click)="deleteClient(client.id)"></button> + </li> + </ul> + </div> + </span> + </td> + </tr> + </tbody> + </table> + </div> +</div> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.ts b/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.ts new file mode 100644 index 00000000..a5e62b2f --- /dev/null +++ b/admin/WebConsole3/frontend/src/app/pages/software/software-types/software-types.component.ts @@ -0,0 +1,26 @@ +import {Component, Input, OnInit} from '@angular/core'; + +@Component({ + selector: 'app-software-types', + templateUrl: './software-types.component.html', + styleUrls: ['./software-types.component.css'] +}) +export class SoftwareTypesComponent implements OnInit { + @Input() softwareTypes; + + constructor() { } + + ngOnInit() { + } + + editSoftwareType(softwareType) { + softwareType.$$editing = true; + softwareType.$$tmpName = softwareType.name; + } + saveSoftwareType(softwareType) { + softwareType.$$editing = false; + softwareType.name = softwareType.$$tmpName; + // TODO - Llamar al servidor para guardar el cambio + } + +} diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software.component.html b/admin/WebConsole3/frontend/src/app/pages/software/software.component.html index adb7ae48..fadb8848 100644 --- a/admin/WebConsole3/frontend/src/app/pages/software/software.component.html +++ b/admin/WebConsole3/frontend/src/app/pages/software/software.component.html @@ -1 +1,65 @@ -<div>Html template for class Software</div>
\ No newline at end of file +<ng-container> + <section class="content-header"> + <h1 translate="software"> + </h1> + <ol class="breadcrumb"> + <li><a [routerLink]="'/app/dashboard'"><i class="fa fa-dashboard"></i> {{'dashboard'|translate}}</a></li> + <li class="active" translate="software"></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'|translate}}..." ng-model="vm.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"> + <mk-dropdown class="btn-group"> + <mk-dropdown-toggle> + <span translate="new"></span><span class="caret"></span> + </mk-dropdown-toggle> + <mk-dropdown-menu> + <li role="presentation"><a role="menuitem" tabindex="-1" [routerLink]="'/app/software/profile/create'" translate="profile"></a></li> + <li role="presentation"><a role="menuitem" tabindex="-1" [routerLink]="'/app/software/component/create'" translate="component"></a></li> + <!--li role="presentation"><a role="menuitem" tabindex="-1" [routerLink]="app/software.type.new" translate="software_type"></a></li--> + </mk-dropdown-menu> + </mk-dropdown> + </div> + </div> + </div> + </section> + <section class="content"> + <div class="row"> + <span style="padding: 0 10px"></span> + <div class="col-md-12"> + <mk-tabs> + <mk-tab> + <mk-tab-header><span translate="software_profiles"></span></mk-tab-header> + <mk-tab-content> + <app-software-profiles [softwareProfileGroups]="softwareProfileGroups"></app-software-profiles> + </mk-tab-content> + </mk-tab> + <mk-tab> + <mk-tab-header><span translate="software_components"></span></mk-tab-header> + <mk-tab-content> + <app-software-components [softwareComponentsGroups]="softwareComponentsGroups" [softwareTypes]="softwareTypes"></app-software-components> + </mk-tab-content> + </mk-tab> + <mk-tab> + <mk-tab-header><span translate="software_types"></span></mk-tab-header> + <mk-tab-content> + <app-software-types [softwareTypes]="softwareTypes" ></app-software-types> + </mk-tab-content> + </mk-tab> + </mk-tabs> + <!-- /.tab-content --> + </div> + </div> + </section> +</ng-container> diff --git a/admin/WebConsole3/frontend/src/app/pages/software/software.component.scss b/admin/WebConsole3/frontend/src/app/pages/software/software.component.scss index b936796b..e69de29b 100644 --- a/admin/WebConsole3/frontend/src/app/pages/software/software.component.scss +++ b/admin/WebConsole3/frontend/src/app/pages/software/software.component.scss @@ -1,3 +0,0 @@ -software {
-
-}
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 ef75722b..59b81298 100644 --- a/admin/WebConsole3/frontend/src/app/pages/software/software.component.ts +++ b/admin/WebConsole3/frontend/src/app/pages/software/software.component.ts @@ -1,17 +1,67 @@ -import { Component } from '@angular/core';
-
-import { SoftwareService } from 'src/app/api/software.service';
-import { Software } from 'src/app/model/software';
-
-@Component({
- selector: 'software',
- templateUrl: './software.component.html',
- styleUrls: [ './software.component.scss' ]
-})
-export class SoftwareComponent {
- // this tells the tabs component which Pages
- // should be each tab's root Page
- constructor(public softwareService: SoftwareService) {
- }
-
-}
+import { Component, OnInit } from '@angular/core'; +import {AuthModule} from 'globunet-angular/core'; +import {SoftwareProfileService} from '../../api/software-profile.service'; +import {OgCommonService} from '../../service/og-common.service'; +import {TranslateService} from '@ngx-translate/core'; +import {SoftwareComponentService} from '../../api/software-component.service'; +import {OgSweetAlertService} from '../../service/og-sweet-alert.service'; +import {ToasterService} from '../../service/toaster.service'; +import {SoftwareTypeService} from '../../api/software-type.service'; + +@Component({ + selector: 'app-software', + templateUrl: './software.component.html', + styleUrls: ['./software.component.scss'] +}) +export class SoftwareComponent implements OnInit { + public softwareProfileGroups: any[] = []; + public softwareComponentsGroups: any[][]; + public softwareComponents: any[] = []; + public softwareTypes: any[] = []; + + constructor(private authModule: AuthModule, + private ogSweetAlert: OgSweetAlertService, + private toaster: ToasterService, + private softwareComponentService: SoftwareComponentService, + private softwareTypeService: SoftwareTypeService, + private softwareProfileService: SoftwareProfileService, + private OGCommonService: OgCommonService, + private translate: TranslateService) { } + + ngOnInit() { + if (this.authModule.getLoggedUser().id !== 0) { + this.softwareProfileService.list().subscribe( + (response) => { + this.softwareProfileGroups = [ + this.OGCommonService.createGroups(response, 'profiles') + ]; + this.softwareProfileGroups[0].name = this.translate.instant('software_profiles'); + }, + (error) => { + alert(error); + } + ); + this.softwareTypeService.list().subscribe( + data => { + this.softwareTypes = data; + }, + (error) => { + alert(error); + } + ); + this.softwareComponentService.list().subscribe( + data => { + this.softwareComponents = data; + this.softwareComponentsGroups = [ + this.OGCommonService.createGroups(this.softwareComponents, 'components') + ]; + // @ts-ignore + this.softwareComponentsGroups[0].name = this.translate.instant('software_components'); + }, + error => { + alert(error); + } + ); + } + } +} 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/serializer/image.serializer.ts b/admin/WebConsole3/frontend/src/app/serializer/image.serializer.ts index f6004e6a..c2043805 100644 --- a/admin/WebConsole3/frontend/src/app/serializer/image.serializer.ts +++ b/admin/WebConsole3/frontend/src/app/serializer/image.serializer.ts @@ -1,5 +1,16 @@ -import { Serializer } from "globunet-angular/core/providers/api/serializer";
-
-export class ImageSerializer extends Serializer {
-
-}
\ No newline at end of file +import { Serializer } from 'globunet-angular/core/providers/api/serializer'; +import {Image} from '../model/image'; + +export class ImageSerializer extends Serializer { + + toJson(resource: Image): any { + const image: any = Object.assign({}, resource); + if (image.client && image.client.id) { + image.client = image.client.id; + } + if (image.repository && image.repository.id) { + image.repository = image.repository.id; + } + return super.toJson(image); + } +} 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 7ea08bb8..1ea33faa 100644 --- a/admin/WebConsole3/frontend/src/app/service/og-commands.service.ts +++ b/admin/WebConsole3/frontend/src/app/service/og-commands.service.ts @@ -1,236 +1,235 @@ -import {ToasterService} from './toaster.service';
-import {OgSweetAlertService} from './og-sweet-alert.service';
-import {CommandService} from '../api/command.service';
-import {OgCommonService} from './og-common.service';
-import {TranslateService} from '@ngx-translate/core';
-import {Router} from '@angular/router';
-import {Injectable} from '@angular/core';
-import * as _ from 'lodash';
-import {environment} from '../../environments/environment';
-
-@Injectable({
- providedIn: 'root'
-})
-export class OGCommandsService {
- public ogInstructions = '';
- public execution: any;
- private commands = [];
-
- 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;
- }
- );
- }
-
- sendCommand() {
- let result = true;
- // TODO - Comprobar parametros
- if (!this.execution.script) {
- result = false;
- this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('command_not_valid')});
- } else if (!this.execution.clients) {
- result = false;
- this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('not_clients_selected')});
- }
- // Si no hubo ningun error
- if (result === true) {
- this.execution.script = this.execution.script.replace(/\"/g, '\\"').replace(/\$/g, '\\\$');
- // Resetar las instrucciones del script opengnsys almacenadas.
- this.ogInstructions = '';
- this.commandService.execute(this.execution).subscribe(
- (response: any[]) => {
- // Buscar en la respuesta si hay algún statuscode diferente de 200
- const errors = response.filter(function(value) {
- return (value.statusCode && value.statusCode !== '!200');
- } );
- let errorStr = '';
- let toasterOpts = {type: 'success', title: 'success', body: this.translate.instant('successfully_executed')};
- if (errors.length > 0) {
- for (let e = 0; e < errors.length; e++) {
- errorStr += this.translate.instant('execution_failed_in') + ' ' + errors[e].name + '\n';
- }
-
- toasterOpts = {type: 'error', title: 'error', body: errorStr};
- }
- this.toaster.pop(toasterOpts);
- this.router.navigate(['/app/ous']);
- },
- (error) => {
- this.toaster.pop({type: 'error', title: 'error', body: error});
- }
- );
- }
- }
-
- execute(command, params?) {
-
- this.execution.type = command;
-
- if (command === 'HISTORY_LOG') {
- let clientIp = null;
- // Abrir ventana de log
- if (typeof params === 'undefined' || typeof params.clientIp === 'undefined') {
- const client = this.ogCommonService.selectedClients[Object.keys(this.ogCommonService.selectedClients)[0]];
- if (client) {
- clientIp = client.ip;
- }
- } else {
- clientIp = params.clientIp;
- }
-
- if (clientIp) {
- const url = 'http://' + clientIp + environment.commands.HISTORY_LOG;
- window.open(url, '', 'resizable=yes,toolbar=no,status=no,location=no,menubar=no,scrollbars=yes');
- }
- } else if (command === 'REALTIME_LOG') {
- let clientIp = null;
- // Abrir ventana de log
- if (typeof params === 'undefined' || typeof params.clientIp === 'undefined') {
- const client = this.ogCommonService.selectedClients[Object.keys(this.ogCommonService.selectedClients)[0]];
- if (client) {
- clientIp = client.ip;
- }
- } else {
- clientIp = params.clientIp;
- }
-
- if (clientIp) {
- const url = 'http://' + clientIp + environment.commands.REALTIME_LOG;
- window.open(url, '', 'resizable=yes,toolbar=no,status=no,location=no,menubar=no,scrollbars=yes');
- }
- } else if (command === 'SOFTWARE_INVENTORY') {
- const client = this.ogCommonService.selectedClients[Object.keys(this.ogCommonService.selectedClients)[0]];
- // Preparar el scope para el sweet alert
- const options = {
- scope: {
- partitions: [],
- selectedPart: null
- }
- };
-
- // 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
- 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);
- }
- }
-
- this.ogSweetAlert.swal({
- title: this.translate.instant('select_partition_to_inventary'),
- // text: $filter("translate")("action_cannot_be_undone"),
- type: 'info',
- input: 'select',
- inputOptions: options.scope.partitions,
- showCancelButton: true,
- confirmButtonColor: '#3c8dbc',
- confirmButtonText: this.translate.instant('done'),
- closeOnConfirm: true
- }).then(
- 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();
- }
- },
- null);
- } else {
- if (command === 'REBOOT') {
- this.execution.script = environment.commands.REBOOT;
- } else if (command === 'POWER_OFF') {
- this.execution.script = environment.commands.POWER_OFF;
- } else if (command === 'POWER_ON') {
- this.execution.script = 'wakeonlan';
- } else if (command === 'HARDWARE_INVENTORY') {
- this.execution.script = environment.commands.HARDWARE_INVENTORY;
- } else if (command === 'RUN_SCRIPT') {
- this.execution.script = params ? (params.script || this.ogInstructions) : this.ogInstructions;
- } else if (command === 'REFRESH_INFO') {
- this.execution.script = environment.commands.REFRESH_INFO;
- }
-
- // Comprobar si en los parametros viene la opcion de guardar
- if (typeof params !== 'undefined' && params.save === true) {
- const self = this;
- // Mostrar cuadro de dialogo para guardar procedimiento
- this.ogSweetAlert.swal({
- title: this.translate.instant('new_command_name'),
- type: 'info',
- html:
- '<form style="text-align: left; padding-left: 10px">\
- <div class="form-group">\
- <label for="execute" translate="execute">\
- </label>\
- <div class="checkbox clip-check check-primary checkbox-inline">\
- <input id="execute" icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" />\
- </div>\
- </div>\
- <div class="form-group">\
- <label translate="title"></label>\
- <input type="text" class="form-control" id="command.title" />\
- </div>\
- <div class="form-group">\
- <label for="parameters" translate="parameters"></label>\
- <div class="checkbox clip-check check-primary checkbox-inline">\
- <input id="parameters" icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" />\
- </div>\
- <p class="help-block" translate="help_command_parameters"></p>\
- </div>\
- </form>',
- showCancelButton: true,
- confirmButtonColor: '#3c8dbc',
- confirmButtonText: this.translate.instant('done'),
- closeOnConfirm: true,
- preConfirm: () => {
- return {
- execute: (<HTMLInputElement>document.getElementById('execute')).value,
- command: {
- title: (<HTMLInputElement>document.getElementById('command.title')).value,
- parameters: (<HTMLInputElement>document.getElementById('parameters')).value
- }
- };
- }
- }).then(
- function(response) {
- if (response.value) {
- response.value.command.script = this.execution.script;
- response.value.command.type = this.execution.type;
- self.commandService.create(response.value.command).subscribe(
- (success) => {
- // Si se seleccionó continuar con la ejecución
- if (response.value.execute) {
- self.loadClients();
- self.sendCommand();
- } else {
- self.router.navigate(['app/commands']);
- }
- },
- (error) => {
- self.toaster.pop({type: 'error', title: 'error', body: error});
- }
- );
- }
- });
- } else {
- this.loadClients();
- this.sendCommand();
- }
- }
- }
-
- loadClients() {
- if (this.ogCommonService.selectedClients) {
- this.execution.clients = _.join(Object.keys(this.ogCommonService.selectedClients));
- }
- }
-
-}
+import {ToasterService} from './toaster.service'; +import {OgSweetAlertService} from './og-sweet-alert.service'; +import {CommandService} from '../api/command.service'; +import {OgCommonService} from './og-common.service'; +import {TranslateService} from '@ngx-translate/core'; +import {Router} from '@angular/router'; +import {Injectable} from '@angular/core'; +import * as _ from 'lodash'; +import {environment} from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class OGCommandsService { + public ogInstructions = ''; + public execution: any; + 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.commands = environment.commands; + } + + sendCommand() { + let result = true; + // TODO - Comprobar parametros + if (!this.execution.script) { + result = false; + this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('command_not_valid')}); + } else if (!this.execution.clients) { + result = false; + this.toaster.pop({type: 'error', title: 'error', body: this.translate.instant('not_clients_selected')}); + } + // Si no hubo ningun error + if (result === true) { + this.execution.script = this.execution.script.replace(/\"/g, '\\"').replace(/\$/g, '\\\$'); + // Resetar las instrucciones del script opengnsys almacenadas. + this.ogInstructions = ''; + this.commandService.execute(this.execution).subscribe( + (response: any[]) => { + // Buscar en la respuesta si hay algún statuscode diferente de 200 + const errors = response.filter(function(value) { + return (value.statusCode && value.statusCode !== '!200'); + } ); + let errorStr = ''; + let toasterOpts = {type: 'success', title: 'success', body: this.translate.instant('successfully_executed')}; + if (errors.length > 0) { + for (let e = 0; e < errors.length; e++) { + errorStr += this.translate.instant('execution_failed_in') + ' ' + errors[e].name + '\n'; + } + + toasterOpts = {type: 'error', title: 'error', body: errorStr}; + } + this.toaster.pop(toasterOpts); + this.router.navigate(['/app/ous']); + }, + (error) => { + this.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + } + + execute(command, params?) { + + this.execution.type = command; + + if (command === 'HISTORY_LOG') { + let clientIp = null; + // Abrir ventana de log + if (typeof params === 'undefined' || typeof params.clientIp === 'undefined') { + const client = this.ogCommonService.selectedClients[Object.keys(this.ogCommonService.selectedClients)[0]]; + if (client) { + clientIp = client.ip; + } + } else { + clientIp = params.clientIp; + } + + if (clientIp) { + const url = 'http://' + clientIp + environment.commands.HISTORY_LOG; + window.open(url, '', 'resizable=yes,toolbar=no,status=no,location=no,menubar=no,scrollbars=yes'); + } + } else if (command === 'REALTIME_LOG') { + let clientIp = null; + // Abrir ventana de log + if (typeof params === 'undefined' || typeof params.clientIp === 'undefined') { + const client = this.ogCommonService.selectedClients[Object.keys(this.ogCommonService.selectedClients)[0]]; + if (client) { + clientIp = client.ip; + } + } else { + clientIp = params.clientIp; + } + + if (clientIp) { + const url = 'http://' + clientIp + environment.commands.REALTIME_LOG; + window.open(url, '', 'resizable=yes,toolbar=no,status=no,location=no,menubar=no,scrollbars=yes'); + } + } else if (command === 'SOFTWARE_INVENTORY') { + const client = this.ogCommonService.selectedClients[Object.keys(this.ogCommonService.selectedClients)[0]]; + // Preparar el scope para el sweet alert + const options = { + scope: { + partitions: [], + selectedPart: null + } + }; + + // 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]); + 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"), + type: 'info', + input: 'select', + inputOptions: options.scope.partitions, + showCancelButton: true, + confirmButtonColor: '#3c8dbc', + confirmButtonText: this.translate.instant('done'), + closeOnConfirm: true + }).then( + function(result) { + if (result.value) { + // Montar el script con el disco y partición elegida + self.execution.script = self.commands.SOFTWARE_INVENTORY + ' ' + clonablePartitions[result.value]; + self.loadClients(); + self.sendCommand(); + } + }, + null); + } else { + if (command === 'REBOOT') { + this.execution.script = environment.commands.REBOOT; + } else if (command === 'POWER_OFF') { + this.execution.script = environment.commands.POWER_OFF; + } else if (command === 'POWER_ON') { + this.execution.script = 'wakeonlan'; + } else if (command === 'HARDWARE_INVENTORY') { + this.execution.script = environment.commands.HARDWARE_INVENTORY; + } else if (command === 'RUN_SCRIPT') { + this.execution.script = params ? (params.script || this.ogInstructions) : this.ogInstructions; + } else if (command === 'REFRESH_INFO') { + this.execution.script = environment.commands.REFRESH_INFO; + } + + // Comprobar si en los parametros viene la opcion de guardar + if (typeof params !== 'undefined' && params.save === true) { + const self = this; + // Mostrar cuadro de dialogo para guardar procedimiento + this.ogSweetAlert.swal({ + title: this.translate.instant('new_command_name'), + type: 'info', + html: + '<form style="text-align: left; padding-left: 10px">\ + <div class="form-group">\ + <label for="execute">' + this.translate.instant('execute') + '</label>\ + <div class="checkbox clip-check check-primary checkbox-inline">\ + <input id="execute" icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" />\ + </div>\ + </div>\ + <div class="form-group">\ + <label>' + this.translate.instant('title') + '</label>\ + <input type="text" class="form-control" id="command.title" />\ + </div>\ + <div class="form-group">\ + <label for="parameters">' + this.translate.instant('parameters') + '</label>\ + <div class="checkbox clip-check check-primary checkbox-inline">\ + <input id="parameters" icheck checkbox-class="icheckbox_square-blue" radio-class="iradio_square-blue" type="checkbox" class="selection-checkbox" />\ + </div>\ + <p class="help-block">' + this.translate.instant('help_command_parameters') + '</p>\ + </div>\ + </form>', + showCancelButton: true, + confirmButtonColor: '#3c8dbc', + confirmButtonText: this.translate.instant('done'), + closeOnConfirm: true, + preConfirm: () => { + return { + execute: (<HTMLInputElement>document.getElementById('execute')).checked, + command: { + title: (<HTMLInputElement>document.getElementById('command.title')).value, + parameters: (<HTMLInputElement>document.getElementById('parameters')).checked + } + }; + } + }).then( + function(response) { + if (response.value) { + response.value.command.script = self.execution.script; + response.value.command.type = self.execution.type; + self.commandService.create(response.value.command).subscribe( + (success) => { + // Si se seleccionó continuar con la ejecución + if (response.value.execute) { + self.loadClients(); + self.sendCommand(); + } else { + self.router.navigate(['app/commands']); + } + }, + (error) => { + self.toaster.pop({type: 'error', title: 'error', body: error}); + } + ); + } + }); + } else { + this.loadClients(); + this.sendCommand(); + } + } + } + + loadClients() { + if (this.ogCommonService.selectedClients) { + this.execution.clients = _.join(Object.keys(this.ogCommonService.selectedClients)); + } + } + +} 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 ee5a8a9d..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}> { @@ -42,21 +45,23 @@ export class OgCommonService { ou: environment.ou, themes: environment.themes, menus: environment.menus, - languages: environment.languages + languages: environment.languages, + 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 }, @@ -189,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/assets/i18n/es.json b/admin/WebConsole3/frontend/src/assets/i18n/es.json index dfc609c3..7427ea51 100644 --- a/admin/WebConsole3/frontend/src/assets/i18n/es.json +++ b/admin/WebConsole3/frontend/src/assets/i18n/es.json @@ -110,7 +110,7 @@ "images_info": "Info imágenes", "imageUrl": "Url imagen", "info": "Información", - "initializing": "Opengnsys", + "initializing": "Inicializando", "invalid_login": "Usuario o contraseña no válido", "ip": "Ip", "january": "Enero", diff --git a/admin/WebConsole3/frontend/src/environments/environment.ts b/admin/WebConsole3/frontend/src/environments/environment.ts index fcc0dcba..ef17e26d 100644 --- a/admin/WebConsole3/frontend/src/environments/environment.ts +++ b/admin/WebConsole3/frontend/src/environments/environment.ts @@ -9,8 +9,8 @@ export const environment = { API_PUBLIC_URL: url + '/api', API_BASE_URL: '/backend/web/app_dev.php/api', OAUTH_DOMAIN: '/backend/web/app_dev.php/oauth/v2/token', - OAUTH_CLIENT_ID: '1_23amzbdp4kskg80444oscko4w0w8wokocs88k0g8w88o4oggs4', - OAUTH_CLIENT_SECRET: '46rttt2trwo4gocgoc4w80k4s8ok48sg8s84kk0cw48csks8o8', + OAUTH_CLIENT_ID: 'CLIENTID', + OAUTH_CLIENT_SECRET: 'CLIENTSECRET', BASE_DIR: 'opengnsys3', clientstatus: ['off', 'initializing', 'oglive', 'busy', 'linux', 'linux_session', 'macos', 'windows', 'windows_session'], windowsboots: ['reboot', 'directo (kexec)'], diff --git a/admin/WebConsole3/frontend/src/styles.scss b/admin/WebConsole3/frontend/src/styles.scss index 57a04153..b0a5eaec 100644 --- a/admin/WebConsole3/frontend/src/styles.scss +++ b/admin/WebConsole3/frontend/src/styles.scss @@ -1,4 +1,8 @@ /* You can add global styles to this file, and also import other style files */ +.input-group { + width: 100%; +} + .capitalize { text-transform: capitalize; } @@ -15,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; } @@ -96,8 +108,7 @@ i.client { } .bg-lab{ - background-color: #d7dbef; - padding: 10px 10px 0 10px; + background-color: #d7dbef !important; } tr.odd { @@ -178,7 +189,7 @@ table.disk-partitions td { } table.disk-partitions td span { - padding: 5px 10px; + padding: 5px 5px; font-weight: bold; } @@ -302,3 +313,7 @@ ul.dropdown-menu.fit { ng2-smart-table { font-size: initial !important; } + +.swal2-popup { + font-size: 1em !important; +} |