1
0
mirror of https://github.com/dkmstr/openuds-gui.git synced 2024-10-26 08:55:23 +03:00

fixing and refactoring frontend

This commit is contained in:
Adolfo Gómez García 2023-01-18 12:52:57 +01:00
parent 6bb8239e84
commit 23389fca6a
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
12 changed files with 74 additions and 101 deletions

View File

@ -5,8 +5,7 @@
# You can see what browsers were selected by your queries by running: # You can see what browsers were selected by your queries by running:
# npx browserslist # npx browserslist
> 0.2% > 0.2% and not dead
last 2 versions last 2 versions
last 4 years last 4 years
Firefox ESR Firefox ESR
not dead

View File

@ -7,5 +7,10 @@
"axe/text-alternatives": "off", "axe/text-alternatives": "off",
"button-type": "off", "button-type": "off",
"typescript-config/consistent-casing": "off" "typescript-config/consistent-casing": "off"
} },
"browserslist": [
"defaults",
"not ie 11",
"not ie <= 10"
]
} }

View File

@ -51,7 +51,7 @@ export class ModalComponent implements OnInit {
this.extra = ' (' + Math.floor(miliseconds / 1000) + ' ' + django.gettext('seconds') + ') '; this.extra = ' (' + Math.floor(miliseconds / 1000) + ' ' + django.gettext('seconds') + ') ';
} }
async initAlert(): Promise<void> { async initAlert() {
const autoclose = this.data.autoclose || 0; const autoclose = this.data.autoclose || 0;
if (autoclose > 0) { if (autoclose > 0) {
this.dialogRef.afterClosed().subscribe((res) => { this.dialogRef.afterClosed().subscribe((res) => {

View File

@ -15,7 +15,7 @@ export class Plugin {
this.delay = api.config.launcher_wait_time; this.delay = api.config.launcher_wait_time;
} }
async launchURL(url: string): Promise<void> { async launchURL(url: string) {
// If uds url... // If uds url...
if (url.substring(0, 7) === 'udsa://') { if (url.substring(0, 7) === 'udsa://') {
await this.processUDSUrl(url); await this.processUDSUrl(url);
@ -45,27 +45,7 @@ export class Plugin {
* @param url uds url to be lauhcned * @param url uds url to be lauhcned
*/ */
private launchUDSUrl(url: string) { private launchUDSUrl(url: string) {
let elem: HTMLIFrameElement = document.getElementById( this.api.download(url);
'hiddenUdsLauncherIFrame'
) as HTMLIFrameElement;
if (elem === null) {
const i = document.createElement('div');
i.id = 'testID';
i.innerHTML =
'<iframe id="hiddenUdsLauncherIFrame" src="about:blank" style="display:none"></iframe>';
document.body.appendChild(i);
elem = document.getElementById(
'hiddenUdsLauncherIFrame'
) as HTMLIFrameElement;
}
// Ensure all is ok
if (elem === null) {
throw new Error('Unable to create hidden iframe');
}
if (elem.contentWindow === null) {
throw new Error('Unable to get content window');
}
elem.contentWindow.location.href = url;
} }
/** /**
@ -74,7 +54,7 @@ export class Plugin {
* @param url uds url (udsa://serviceId/transportId) * @param url uds url (udsa://serviceId/transportId)
* @returns nothing * @returns nothing
*/ */
private async processUDSUrl(url: string): Promise<void> { private async processUDSUrl(url: string) {
// Extract params from url, serviceId and transportId // Extract params from url, serviceId and transportId
const params = url.split('//')[1].split('/'); const params = url.split('//')[1].split('/');
if (params.length !== 2) { if (params.length !== 2) {
@ -96,7 +76,7 @@ export class Plugin {
// Connect close dialog to "cancel" variable // Connect close dialog to "cancel" variable
toPromise(dialog.afterClosed()).then(() => (cancel = true)); toPromise(dialog.afterClosed()).then(() => (cancel = true));
let readyTime = -1; let readySinceTime = -1;
try { try {
// Enable service // Enable service
const enabledData = await this.api.enabler(serviceId, transportId); const enabledData = await this.api.enabler(serviceId, transportId);
@ -119,7 +99,7 @@ export class Plugin {
while (!cancel) { while (!cancel) {
const data = await this.api.status(serviceId, transportId); const data = await this.api.status(serviceId, transportId);
// Wait 5 times the default delay before notifying that client is not installed // Wait 5 times the default delay before notifying that client is not installed
if (readyTime > 0 && Date.now() - readyTime > this.delay * 5) { if (readySinceTime > 0 && Date.now() - readySinceTime > this.delay * 5) {
dialog.componentInstance.data.title = dialog.componentInstance.data.title =
django.gettext('Service ready') + django.gettext('Service ready') +
' - ' + ' - ' +
@ -137,9 +117,9 @@ export class Plugin {
'</a>'; '</a>';
} }
if (data.status === 'ready') { if (data.status === 'ready') {
if (readyTime === -1) { if (readySinceTime === -1) {
// Service is ready, wait for client, update dialog text // Service is ready, wait for client, update dialog text
readyTime = Date.now(); // Milisecodns readySinceTime = Date.now(); // Milisecodns
dialog.componentInstance.data.title = dialog.componentInstance.data.title =
django.gettext('Service ready'); django.gettext('Service ready');
dialog.componentInstance.data.body = django.gettext( dialog.componentInstance.data.body = django.gettext(
@ -167,7 +147,7 @@ export class Plugin {
} }
} }
private async processExternalUrl(url: string): Promise<void> { private async processExternalUrl(url: string) {
const dialog = await this.showAlert( const dialog = await this.showAlert(
django.gettext('Please wait until the service is launched.'), django.gettext('Please wait until the service is launched.'),
django.gettext( django.gettext(
@ -281,7 +261,7 @@ export class Plugin {
} }
} }
private async notifyError(error?: any): Promise<void> { private async notifyError(error?: any) {
let msg: string = django.gettext( let msg: string = django.gettext(
'Error communicating with your service. Please, retry again.' 'Error communicating with your service. Please, retry again.'
); );

View File

@ -1,8 +1,8 @@
<div class="about"> <div class="about">
<h1>Universal Desktop Services {{ api.config.version }} build {{ api.config.version_stamp }}</h1> <h1>Universal Desktop Services {{ api.config.version }} build {{ api.config.version_stamp }}</h1>
<h3><a href="http://www.udsenterprise.com" target="_blank"> &copy; 2012-{{ year }} Virtual Cable S.L.U.</a></h3> <h3><a rel="noopener noreferrer" href="http://www.udsenterprise.com" target="_blank"> &copy; 2012-{{ year }} Virtual Cable S.L.U.</a></h3>
<h4> <h4>
<uds-translate>You can access UDS Open Source code at</uds-translate>&nbsp;<a href="https://github.com/dkmstr/openuds" <uds-translate>You can access UDS Open Source code at</uds-translate>&nbsp;<a rel="noopener noreferrer" href="https://github.com/dkmstr/openuds"
target="_blank">OpenUDS github repository</a> target="_blank">OpenUDS github repository</a>
</h4> </h4>
<div class="components"> <div class="components">
@ -10,15 +10,15 @@
<uds-translate>UDS has been developed using these components:</uds-translate> <uds-translate>UDS has been developed using these components:</uds-translate>
</h2> </h2>
<ul> <ul>
<li><a href="https://www.python.org/">Python</a></li> <li><a rel="noopener noreferrer" href="https://www.python.org/" target="_blank">Python</a></li>
<li><a href="https://www.typescriptlang.org/" target="_blank">TypeScript</a></li> <li><a rel="noopener noreferrer" href="https://www.typescriptlang.org/" target="_blank">TypeScript</a></li>
<li><a href="https://www.djangoproject.com/" target="_blank">Django</a></li> <li><a rel="noopener noreferrer" href="https://www.djangoproject.com/" target="_blank">Django</a></li>
<li><a href="https://angular.io" target="_blank">Angular</a></li> <li><a rel="noopener noreferrer" href="https://angular.io" target="_blank">Angular</a></li>
<li><a href="https://guac-dev.org/" target="_blank">Guacamole</a></li> <li><a rel="noopener noreferrer" href="https://guac-dev.org/" target="_blank">Guacamole</a></li>
<li><a href="https://weasyprint.org/" target="_blank">weasyprint</a></li> <li><a rel="noopener noreferrer" href="https://weasyprint.org/" target="_blank">weasyprint</a></li>
<li><a href="https://kde-look.org/content/show.php/Crystal+Project?content=60475)" target="_blank">Crystal project <li><a rel="noopener noreferrer" href="https://kde-look.org/content/show.php/Crystal+Project?content=60475)" target="_blank">Crystal project
icons</a></li> icons</a></li>
<li><a href="https://github.com/NitruxSA/flattr-icons" target="_blank">Flattr Icons</a></li> <li><a rel="noopener noreferrer" href="https://github.com/NitruxSA/flattr-icons" target="_blank">Flattr Icons</a></li>
</ul> </ul>
<p><small>* <uds-translate>If you find that we missed any component, please let us know</uds-translate></small></p> <p><small>* <uds-translate>If you find that we missed any component, please let us know</uds-translate></small></p>
</div> </div>

View File

@ -10,7 +10,7 @@
</div> </div>
<div class="plugins"> <div class="plugins">
<div [class]="css(p)" *ngFor="let p of api.plugins" (click)="download(p.url)"> <div [class]="css(p)" *ngFor="let p of api.plugins" (click)="api.download(p.url)">
<div class="image"> <div class="image">
<img [src]="img(p.name)"> <img [src]="img(p.name)">
</div> </div>

View File

@ -15,10 +15,6 @@ export class ClientDownloadComponent implements OnInit {
ngOnInit() { ngOnInit() {
} }
download(url: string) {
window.location.href = url;
}
img(image: string) { img(image: string) {
return this.api.staticURL( 'modern/img/' + image + '.png'); return this.api.staticURL( 'modern/img/' + image + '.png');
} }

View File

@ -9,7 +9,7 @@
</div> </div>
<div class="actors"> <div class="actors">
<div [class]="css(p.name)" *ngFor="let p of actors" (click)="download(p.url)"> <div [class]="css(p.name)" *ngFor="let p of actors" (click)="api.download(p.url)">
<div class="image"> <div class="image">
<img [src]="img(p.name)"> <img [src]="img(p.name)">
</div> </div>

View File

@ -5,31 +5,27 @@ import { Downloadable } from '../../types/config';
@Component({ @Component({
selector: 'uds-downloads', selector: 'uds-downloads',
templateUrl: './downloads.component.html', templateUrl: './downloads.component.html',
styleUrls: ['./downloads.component.scss'] styleUrls: ['./downloads.component.scss'],
}) })
export class DownloadsComponent implements OnInit { export class DownloadsComponent implements OnInit {
actors: Downloadable[] = []; actors: Downloadable[] = [];
constructor(public api: UDSApiService) { } constructor(public api: UDSApiService) {}
ngOnInit() { ngOnInit() {
this.actors = []; // Put legacy at end of downloadables... // Sort legacy actors to the end of the list
this.actors = []; // Put legacy at end of downloadables...
const legacy: Downloadable[] = []; const legacy: Downloadable[] = [];
this.api.actors.forEach(a => { for (const a of this.api.actors) {
if (a.name.includes('legacy')) { if (a.name.includes('legacy')) {
legacy.push(a); legacy.push(a);
} else { } else {
this.actors.push(a); this.actors.push(a);
} }
}); }
legacy.forEach(l => { for (const l of legacy) {
this.actors.push(l); this.actors.push(l);
}); }
}
download(url: string) {
window.location.href = url;
} }
img(filename: string) { img(filename: string) {
@ -52,5 +48,4 @@ export class DownloadsComponent implements OnInit {
} }
return styles; return styles;
} }
} }

View File

@ -17,7 +17,7 @@ export class ErrorComponent implements OnInit {
await this.getError(); await this.getError();
} }
async getError(): Promise<void> { async getError() {
const id = this.route.snapshot.paramMap.get('id') || '-1'; const id = this.route.snapshot.paramMap.get('id') || '-1';
if (id === '19') { // 19 is MFA error, return to MFA if (id === '19') { // 19 is MFA error, return to MFA
this.returnUrl = '/mfa'; this.returnUrl = '/mfa';

View File

@ -42,6 +42,9 @@ export interface UDSApiServiceType {
/* Executes logout */ /* Executes logout */
logout(): void; logout(): void;
/* Download file/launches a custom uro */
download(url: string): Promise<void>;
/* sleep milliseconds */ /* sleep milliseconds */
sleep(ms: number): Promise<void>; sleep(ms: number): Promise<void>;
/** /**

View File

@ -19,7 +19,6 @@ import { UDSApiServiceType } from './uds-api.service-type';
import { environment } from '../environments/environment'; import { environment } from '../environments/environment';
const DARK_THEME = 'dark-theme'; const DARK_THEME = 'dark-theme';
const LIGHT_THEME = 'light-theme'; const LIGHT_THEME = 'light-theme';
const TIMEOUT = 10000; const TIMEOUT = 10000;
@ -32,14 +31,10 @@ const toPromise = <T>(observable: Observable<T>, wait?: number): Promise<T> => {
@Injectable() @Injectable()
export class UDSApiService implements UDSApiServiceType { export class UDSApiService implements UDSApiServiceType {
readonly user: User; readonly user: User;
transportsWindow: Window|null = null; transportsWindow: Window | null = null;
plugin: Plugin; plugin: Plugin;
constructor( constructor(private http: HttpClient, public gui: UDSGuiService, public router: Router) {
private http: HttpClient,
public gui: UDSGuiService,
public router: Router
) {
this.user = new User(udsData.profile); this.user = new User(udsData.profile);
this.plugin = new Plugin(this); this.plugin = new Plugin(this);
} }
@ -91,32 +86,20 @@ export class UDSApiService implements UDSApiServiceType {
} }
/* Client enabler */ /* Client enabler */
async enabler( async enabler(serviceId: string, transportId: string): Promise<JSONEnabledService> {
serviceId: string, const enabler = this.config.urls.enabler.replace('param1', serviceId).replace('param2', transportId);
transportId: string
): Promise<JSONEnabledService> {
const enabler = this.config.urls.enabler
.replace('param1', serviceId)
.replace('param2', transportId);
return toPromise(this.http.get<JSONEnabledService>(enabler)); return toPromise(this.http.get<JSONEnabledService>(enabler));
} }
/* Check userService status */ /* Check userService status */
async status( async status(serviceId: string, transportId: string): Promise<JSONStatusService> {
serviceId: string, const status = this.config.urls.status.replace('param1', serviceId).replace('param2', transportId);
transportId: string
): Promise<JSONStatusService> {
const status = this.config.urls.status
.replace('param1', serviceId)
.replace('param2', transportId);
return toPromise(this.http.get<JSONStatusService>(status)); return toPromise(this.http.get<JSONStatusService>(status));
} }
/* Services resetter */ /* Services resetter */
async action(action: string, serviceId: string): Promise<JSONService> { async action(action: string, serviceId: string): Promise<JSONService> {
const actionURL = this.config.urls.action const actionURL = this.config.urls.action.replace('param1', serviceId).replace('param2', action);
.replace('param1', serviceId)
.replace('param2', action);
return toPromise(this.http.get<JSONService>(actionURL)); return toPromise(this.http.get<JSONService>(actionURL));
} }
@ -131,9 +114,7 @@ export class UDSApiService implements UDSApiServiceType {
password: string, password: string,
domain: string domain: string
): Promise<any> { ): Promise<any> {
const url = this.config.urls.updateTransportTicket const url = this.config.urls.updateTransportTicket.replace('param1', ticketId).replace('param2', scrambler);
.replace('param1', ticketId)
.replace('param2', scrambler);
return toPromise( return toPromise(
this.http.post<any>(url, { this.http.post<any>(url, {
username, username,
@ -164,20 +145,14 @@ export class UDSApiService implements UDSApiServiceType {
* Gets services information * Gets services information
*/ */
async getServicesInformation(): Promise<JSONServicesInformation> { async getServicesInformation(): Promise<JSONServicesInformation> {
return toPromise( return toPromise(this.http.get<JSONServicesInformation>(this.config.urls.services));
this.http.get<JSONServicesInformation>(this.config.urls.services)
);
} }
/** /**
* Gets error string from a code * Gets error string from a code
*/ */
async getErrorInformation(errorCode: string): Promise<JSONErrorInformation> { async getErrorInformation(errorCode: string): Promise<JSONErrorInformation> {
return toPromise( return toPromise(this.http.get<JSONErrorInformation>(this.config.urls.error.replace('9999', errorCode)));
this.http.get<JSONErrorInformation>(
this.config.urls.error.replace('9999', errorCode)
)
);
} }
/** /**
@ -200,6 +175,28 @@ export class UDSApiService implements UDSApiServiceType {
window.location.href = this.config.urls.logout; window.location.href = this.config.urls.logout;
} }
async download(url: string): Promise<void> {
// Launch the download
// Create an iframe and set the src to the url
// This will trigger the download
// First, loof for an existing download iframe
let iframe = document.getElementById('download') as HTMLIFrameElement;
// If not found, create it
if (!iframe) {
iframe = document.createElement('iframe');
iframe.id = 'download';
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
// Set the src to the url
iframe.src = url;
// onload in iframe will only be triggered if an html page is downloaded. If it is loaded, it's not a download
iframe.onload = () => {
alert('Error downloading file. Please try again later.');
};
//window.location.href = url;
}
sleep(ms: number): Promise<void> { sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} }
@ -215,9 +212,7 @@ export class UDSApiService implements UDSApiServiceType {
* @returns Observable * @returns Observable
*/ */
async getAuthCustomJavascript(authId: string): Promise<string> { async getAuthCustomJavascript(authId: string): Promise<string> {
return toPromise( return toPromise(this.http.get(this.config.urls.customAuth + authId, { responseType: 'text' }));
this.http.get(this.config.urls.customAuth + authId, {responseType: 'text'})
);
} }
// Switch dark/light theme // Switch dark/light theme