Skip to content

Commit

Permalink
webapp setup: System Events settings
Browse files Browse the repository at this point in the history
  • Loading branch information
bennettpeter committed Mar 21, 2023
1 parent 6ab306d commit c70edac
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 8 deletions.
3 changes: 3 additions & 0 deletions mythtv/html/assets/i18n/en_US.json
Expand Up @@ -763,6 +763,9 @@
"mpeg2language_label": "SAP/Bilingual",
"mpeg2language_desc": "Chooses the language(s) to record when two languages are broadcast. Only Layer II supports the recording of two languages (Dual). Requires ivtv 0.4.0 or later.",
"mpeg2audvolume_desc": "Volume of the recording"
},
"sysevents": {
"title": "System Event Command Editor"
}
},
"sidenav": {
Expand Down
5 changes: 5 additions & 0 deletions mythtv/html/backend/src/app/app-routing.module.ts
Expand Up @@ -13,6 +13,7 @@ import { InputConnectionsComponent } from './config/settings/input-connections/i
import { StorageGroupsComponent } from './config/settings/storage-groups/storage-groups.component';
import { ChannelEditorComponent } from './config/settings/channel-editor/channel-editor.component';
import { RecordingProfilesComponent } from './config/settings/recording-profiles/recording-profiles.component';
import { SystemEventsComponent } from './config/settings/system-events/system-events.component';

const routes: Routes = [
{ path: '', component: DashboardComponent },
Expand Down Expand Up @@ -46,6 +47,10 @@ const routes: Routes = [
path: 'settings/storage-groups', component: StorageGroupsComponent,
canDeactivate: [CanDeactivateGuardService]
},
{
path: 'settings/system-events', component: SystemEventsComponent,
canDeactivate: [CanDeactivateGuardService]
},
{ path: 'testbed', component: TestbedComponent },
{ path: 'guide', component: GuideComponent },
];
Expand Down
2 changes: 2 additions & 0 deletions mythtv/html/backend/src/app/app.module.ts
Expand Up @@ -68,6 +68,7 @@ import { ChannelEditorComponent } from './config/settings/channel-editor/channel
import { RecordingProfilesComponent } from './config/settings/recording-profiles/recording-profiles.component';
import { ProfileGroupComponent } from './config/settings/recording-profiles/profile-group/profile-group.component';
import { RecprofileComponent } from './config/settings/recording-profiles/profile-group/recprofile/recprofile.component';
import { SystemEventsComponent } from './config/settings/system-events/system-events.component';

// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient) {
Expand Down Expand Up @@ -132,6 +133,7 @@ export function HttpLoaderFactory(http: HttpClient) {
RecordingProfilesComponent,
ProfileGroupComponent,
RecprofileComponent,
SystemEventsComponent,
],
imports: [
BrowserModule,
Expand Down
@@ -1,4 +1,4 @@
<form class="ml-3 mr-3" name="connform" #connform="ngForm">
<form class="ml-3 mr-3" name="connform" #connform="ngForm">nnform
<span #top></span>
<p-card class="m-5">
<ng-template pTemplate="title">
Expand Down
Empty file.
@@ -0,0 +1,35 @@
<form class="ml-3 mr-3" name="eventsform" #eventsform="ngForm">

<p-card class="m-5">
<ng-template pTemplate="title">
{{ 'settings.sysevents.title' | translate }}
</ng-template>
<ng-template pTemplate="subtitle">
</ng-template>
<ng-template pTemplate="content">
<div class="grid">
<div class="mb-4 w-full" *ngFor="let event of events; index as ix; first as isFirst">
<div class="form-group field">
<label for="{{event.Key}}" class="block">{{ event.LocalizedName }}</label>
<input pInputText id="{{event.Key}}" [(ngModel)]="event.Value" name="{{event.Key}}"
class="mb-2 form-control w-full" />
</div>
</div>
</div>
</ng-template>

<ng-template pTemplate="footer">
<div class="grid nogutter">
<div class="col-12">
<p-message *ngIf="successCount > 0 && successCount == expectedCount && errorCount == 0 && !eventsform.dirty"
severity="success" text="{{ 'common.savesuccess' | translate }}"></p-message>
<p-message *ngIf="this.errorCount > 0" severity="error"
text="{{ 'common.networkfail' | translate }}"></p-message>
</div>
<p-button class="col pr-1" label="{{'common.save' | translate}}" icon="pi pi-save" iconPos="left"
(onClick)="saveForm(); eventsform.form.markAsPristine()"
[disabled]="(!eventsform.dirty)"></p-button>
</div>
</ng-template>
</p-card>
</form>
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { SystemEventsComponent } from './system-events.component';

describe('SystemEventsComponent', () => {
let component: SystemEventsComponent;
let fixture: ComponentFixture<SystemEventsComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SystemEventsComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(SystemEventsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,108 @@
import { Component, HostListener, OnInit, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';
import { ConfigService } from 'src/app/services/config.service';
import { SystemEvent, SystemEventList } from 'src/app/services/interfaces/config.interface';
import { MythService } from 'src/app/services/myth.service';
import { SetupService } from 'src/app/services/setup.service';

@Component({
selector: 'app-system-events',
templateUrl: './system-events.component.html',
styleUrls: ['./system-events.component.css']
})

export class SystemEventsComponent implements OnInit {

@ViewChild("eventsform") currentForm!: NgForm;
eventList!: SystemEventList;

events: SystemEvent[] = [];

successCount = 0;
errorCount = 0;
expectedCount = 0;

warningText = 'settings.warning';

// See mythdb.cpp. This value is used to delete a setting
kClearSettingValue = "<clear_setting_value>";

constructor(private configService: ConfigService, private translate: TranslateService,
private setupService: SetupService, private mythService: MythService) {
this.configService.GetSystemEvents().subscribe(data => {
this.eventList = data;
this.events = data.SystemEventList.SystemEvents;
});
this.translate.get(this.warningText).subscribe(data => {
this.warningText = data
});
}

ngOnInit(): void {
}

jqbObserver = {
next: (x: any) => {
if (x.bool)
this.successCount++;
else {
this.errorCount++;
if (this.currentForm)
this.currentForm.form.markAsDirty();
}
},
error: (err: any) => {
console.error(err);
this.errorCount++;
if (this.currentForm)
this.currentForm.form.markAsDirty();
},
};

saveForm() {
this.successCount = 0;
this.errorCount = 0;
this.expectedCount = 0;
const hostName = this.setupService.getHostName();

this.events.forEach(entry => {
let value = entry.Value.trim();
if (value)
// value = this.kClearSettingValue;
this.mythService.PutSetting({
HostName: hostName, Key: entry.Key,
Value: value
}).subscribe(this.jqbObserver);
else
this.mythService.DeleteSetting({
HostName: hostName, Key: entry.Key
}).subscribe(this.jqbObserver);
this.expectedCount++;
});

}

confirm(message?: string): Observable<boolean> {
const confirmation = window.confirm(message);
return of(confirmation);
};

canDeactivate(): Observable<boolean> | boolean {
if (this.currentForm && this.currentForm.dirty)
return this.confirm(this.warningText);
return true;
}

@HostListener('window:beforeunload', ['$event'])
onWindowClose(event: any): void {
if (this.currentForm && this.currentForm.dirty) {
event.preventDefault();
event.returnValue = false;
}
}



}
Expand Up @@ -30,7 +30,7 @@ export class SidenavComponent implements OnInit {
{ routerLink: 'settings/input-connections'},
{ routerLink: 'settings/channel-editor'},
{ routerLink: 'settings/storage-groups'},
{ routerLink: ''},
{ routerLink: 'settings/system-events'},
]
}];

Expand Down
18 changes: 12 additions & 6 deletions mythtv/html/backend/src/app/services/config.service.ts
@@ -1,9 +1,8 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { MythConnectionInfo, Database } from './interfaces/myth.interface';
import { MythDatabaseStatus, IPAddressList } from './interfaces/config.interface';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Database } from './interfaces/myth.interface';
import { MythDatabaseStatus, IPAddressList, SystemEventList } from './interfaces/config.interface';
import { MythCountryList } from './interfaces/country.interface';
import { MythLanguageList } from './interfaces/language.interface';

Expand Down Expand Up @@ -31,7 +30,14 @@ export class ConfigService {

public GetIPAddresses(protocol: string): Observable<IPAddressList> {
let params = new HttpParams().set("Protocol", protocol);
return this.httpClient.get<IPAddressList>('/Config/GetIPAddresses', {params})
return this.httpClient.get<IPAddressList>('/Config/GetIPAddresses', { params })
}

public GetSystemEvents(host?: string): Observable<SystemEventList> {
let params = new HttpParams();
if (host)
params.set("Host", host);
return this.httpClient.get<SystemEventList>('/Config/GetSystemEvents', { params })
}
}

Expand Up @@ -19,3 +19,15 @@ export interface MythDatabaseStatus {
export interface IPAddressList {
IPAddresses: string[];
}

export interface SystemEvent {
Key: string;
LocalizedName: string;
Value: string;
}

export interface SystemEventList {
SystemEventList: {
SystemEvents: SystemEvent[];
}
}
4 changes: 4 additions & 0 deletions mythtv/html/backend/src/app/services/myth.service.ts
Expand Up @@ -138,6 +138,10 @@ export class MythService {
return this.httpClient.post<BoolResponse>('/Myth/PutSetting', setting)
}

public DeleteSetting(setting: {HostName: string, Key: string}) : Observable<BoolResponse> {
return this.httpClient.post<BoolResponse>('/Myth/DeleteSetting', setting)
}

public RemoveStorageGroupDir(request: RemoveStorageGroupRequest) : Observable<BoolResponse> {
return this.httpClient.post<BoolResponse>('/Myth/RemoveStorageGroupDir', request);
}
Expand Down

0 comments on commit c70edac

Please sign in to comment.