url shortener, quotas, small improvements
This commit is contained in:
parent
aef130dff4
commit
651352e4a1
@ -22,6 +22,7 @@ import {JitsiComponent} from './pages/jitsi/jitsi.component'
|
|||||||
import {AliasesComponent} from './pages/account/aliases/aliases.component';
|
import {AliasesComponent} from './pages/account/aliases/aliases.component';
|
||||||
import {DomainsComponent} from './pages/account/domains/domains.component';
|
import {DomainsComponent} from './pages/account/domains/domains.component';
|
||||||
import {InvitesComponent} from './pages/invites/invites.component';
|
import {InvitesComponent} from './pages/invites/invites.component';
|
||||||
|
import {UrlShortenerComponent, UrlShortenerPasswordComponent} from './pages/urlshortener/urlshortener.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', redirectTo: "/services", pathMatch: 'full'},
|
{path: '', redirectTo: "/services", pathMatch: 'full'},
|
||||||
@ -49,6 +50,8 @@ const routes: Routes = [
|
|||||||
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
|
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
|
||||||
{path: 'tokens', component: TokensComponent, canActivate: [AuthGuard]},
|
{path: 'tokens', component: TokensComponent, canActivate: [AuthGuard]},
|
||||||
{path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard]},
|
{path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard]},
|
||||||
|
{path: 'urlshortener', component: UrlShortenerComponent, canActivate: [AuthenticatedGuard]},
|
||||||
|
{path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [AuthUpdateGuard]},
|
||||||
{path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard]},
|
{path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard]},
|
||||||
{path: 'unavailable', component: UnavailableComponent},
|
{path: 'unavailable', component: UnavailableComponent},
|
||||||
{path: 'p/:username', component: UserComponent, canActivate: [AuthUpdateGuard]},
|
{path: 'p/:username', component: UserComponent, canActivate: [AuthUpdateGuard]},
|
||||||
|
@ -9,6 +9,9 @@ import {MaterialModule} from './material/material.module';
|
|||||||
import {QRCodeModule} from 'angularx-qrcode';
|
import {QRCodeModule} from 'angularx-qrcode';
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
|
|
||||||
|
|
||||||
|
import { AutofocusDirective} from './material/autofocus';
|
||||||
|
|
||||||
import {I18nPipe} from './utils/i18n.pipe';
|
import {I18nPipe} from './utils/i18n.pipe';
|
||||||
import {ImprintComponent, PrivacyPolicyComponent, TermsOfServiceComponent} from './pages/general/general.component';
|
import {ImprintComponent, PrivacyPolicyComponent, TermsOfServiceComponent} from './pages/general/general.component';
|
||||||
import {AccountComponent} from './pages/account/account.component';
|
import {AccountComponent} from './pages/account/account.component';
|
||||||
@ -41,6 +44,7 @@ import {HtmlComponent} from './utils/html/html.component';
|
|||||||
import {ConfirmDialog} from './ui/confirm/confirm.component'
|
import {ConfirmDialog} from './ui/confirm/confirm.component'
|
||||||
import {UserComponent} from './pages/user/user.component'
|
import {UserComponent} from './pages/user/user.component'
|
||||||
import {JitsiComponent, JitsiShareDialog} from './pages/jitsi/jitsi.component'
|
import {JitsiComponent, JitsiShareDialog} from './pages/jitsi/jitsi.component'
|
||||||
|
import {UrlShortenerComponent, UrlShortenerPasswordComponent, UrlShortenerShareDialog} from './pages/urlshortener/urlshortener.component'
|
||||||
|
|
||||||
|
|
||||||
import {I18nService} from './services/i18n.service';
|
import {I18nService} from './services/i18n.service';
|
||||||
@ -63,6 +67,7 @@ export class XhrInterceptor implements HttpInterceptor {
|
|||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
AutofocusDirective,
|
||||||
I18nPipe,
|
I18nPipe,
|
||||||
AppComponent,
|
AppComponent,
|
||||||
ImprintComponent, PrivacyPolicyComponent, TermsOfServiceComponent,
|
ImprintComponent, PrivacyPolicyComponent, TermsOfServiceComponent,
|
||||||
@ -95,7 +100,8 @@ export class XhrInterceptor implements HttpInterceptor {
|
|||||||
HtmlComponent,
|
HtmlComponent,
|
||||||
ConfirmDialog,
|
ConfirmDialog,
|
||||||
UserComponent,
|
UserComponent,
|
||||||
JitsiComponent, JitsiShareDialog
|
JitsiComponent, JitsiShareDialog,
|
||||||
|
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerPasswordComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
15
src/app/material/autofocus.ts
Normal file
15
src/app/material/autofocus.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Directive, OnInit } from '@angular/core';
|
||||||
|
import { MatInput } from '@angular/material/input';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[matInputAutofocus]',
|
||||||
|
})
|
||||||
|
export class AutofocusDirective implements OnInit {
|
||||||
|
|
||||||
|
constructor(private matInput: MatInput) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
setTimeout(() => this.matInput.focus());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
{{'security.2fa.totp.invalid' | i18n}}
|
{{'security.2fa.totp.invalid' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input id="code" name="code" matInput placeholder="{{'security.2fa.totp.code' | i18n}}" required>
|
<input id="code" name="code" matInput placeholder="{{'security.2fa.totp.code' | i18n}}" required matInputAutofocus>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{'security.2fa.totp.missing' | i18n}}
|
{{'security.2fa.totp.missing' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
{{'login.invalid' | i18n}}
|
{{'login.invalid' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input id="username" name="username" matInput placeholder="{{'username' | i18n}}" required>
|
<input id="username" name="username" matInput placeholder="{{'username' | i18n}}" required matInputAutofocus>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{'username.missing' | i18n}}
|
{{'username.missing' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
@ -1,76 +1,83 @@
|
|||||||
<h3>{{'jitsi.rooms' | i18n}}</h3>
|
<h3>{{'jitsi.rooms' | i18n}}</h3>
|
||||||
|
|
||||||
<table mat-table matSort [dataSource]="jitsiRooms" (matSortChange)="sortData($event)">
|
<div *ngIf="jitsiRooms">
|
||||||
|
<table mat-table matSort [dataSource]="jitsiRooms.content" (matSortChange)="updateSort($event)">
|
||||||
|
|
||||||
<ng-container matColumnDef="share">
|
<ng-container matColumnDef="share">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'jitsi.share' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef> {{'jitsi.share' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let jitsiRoom">
|
<td mat-cell *matCellDef="let jitsiRoom">
|
||||||
<button mat-icon-button (click)="share(jitsiRoom)">
|
<button mat-icon-button (click)="share(jitsiRoom)">
|
||||||
<mat-icon>share</mat-icon>
|
<mat-icon>share</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="room">
|
<ng-container matColumnDef="room">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="room"> {{'jitsi.rooms.room' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="room"> {{'jitsi.rooms.room' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let jitsiRoom">
|
<td mat-cell *matCellDef="let jitsiRoom">
|
||||||
<a *ngIf="open(jitsiRoom, false)" mat-button color="accent" href="{{ jitsiRoom.url }}" target="_blank">
|
<a *ngIf="open(jitsiRoom, false)" mat-button color="accent" href="{{ jitsiRoom.url }}" target="_blank">
|
||||||
{{ jitsiRoom.room }}
|
{{ jitsiRoom.room }}
|
||||||
<mat-icon style="font-size: 1em;">open_in_new
|
<mat-icon style="font-size: 1em;">open_in_new
|
||||||
</mat-icon>
|
</mat-icon>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a *ngIf="!open(jitsiRoom, false)" mat-button disabled matTooltip="{{'jitsi.rooms.notStarted' | i18n}}"
|
<a *ngIf="!open(jitsiRoom, false)" mat-button disabled matTooltip="{{'jitsi.rooms.notStarted' | i18n}}"
|
||||||
matTooltipPosition="above">
|
matTooltipPosition="above">
|
||||||
{{ jitsiRoom.room }}
|
{{ jitsiRoom.room }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
<ng-container matColumnDef="starts">
|
<ng-container matColumnDef="starts">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'jitsi.rooms.starts' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'jitsi.rooms.starts' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.starts | date:datetimeformat}} </td>
|
<td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.starts | date:datetimeformat}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="moderationStarts">
|
<ng-container matColumnDef="moderationStarts">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="moderationStarts"> {{'jitsi.rooms.moderationStarts' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="moderationStarts"> {{'jitsi.rooms.moderationStarts' |
|
||||||
<td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.moderationStarts | date:datetimeformat}} </td>
|
i18n}} </th>
|
||||||
</ng-container>
|
<td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.moderationStarts | date:datetimeformat}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="expires">
|
<ng-container matColumnDef="expires">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="expires"> {{'jitsi.rooms.expires' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="expires"> {{'jitsi.rooms.expires' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let jitsiRoom"> {{ jitsiRoom.expires | date:datetimeformat}} </td>
|
<td mat-cell *matCellDef="let jitsiRoom"> {{ jitsiRoom.expires | date:datetimeformat}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="moderationUrl">
|
<ng-container matColumnDef="moderationUrl">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="moderationUrl"> {{'jitsi.rooms.moderationUrl' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="moderationUrl"> {{'jitsi.rooms.moderationUrl' | i18n}}
|
||||||
<td mat-cell *matCellDef="let jitsiRoom">
|
</th>
|
||||||
<a *ngIf="open(jitsiRoom, true)" mat-button color="primary" class="url" href="{{ jitsiRoom.moderationUrl}}"
|
<td mat-cell *matCellDef="let jitsiRoom">
|
||||||
target="_blank">
|
<a *ngIf="open(jitsiRoom, true)" mat-button color="primary" class="url" href="{{ jitsiRoom.moderationUrl}}"
|
||||||
{{ jitsiRoom.moderationUrl }}
|
target="_blank">
|
||||||
<mat-icon style="font-size: 1em;">open_in_new</mat-icon>
|
{{ jitsiRoom.moderationUrl }}
|
||||||
</a>
|
<mat-icon style="font-size: 1em;">open_in_new</mat-icon>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a *ngIf="!open(jitsiRoom, true)" mat-button color="primary" class="url" disabled matTooltip="{{'jitsi.rooms.notStarted' | i18n}}"
|
<a *ngIf="!open(jitsiRoom, true)" mat-button color="primary" class="url" disabled
|
||||||
matTooltipPosition="above">
|
matTooltip="{{'jitsi.rooms.notStarted' | i18n}}" matTooltipPosition="above">
|
||||||
{{ jitsiRoom.moderationUrl }}
|
{{ jitsiRoom.moderationUrl }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="delete">
|
<ng-container matColumnDef="delete">
|
||||||
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'jitsi.rooms.delete' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'jitsi.rooms.delete' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let jitsiRoom" class="text-right">
|
<td mat-cell *matCellDef="let jitsiRoom" class="text-right">
|
||||||
<a mat-icon-button>
|
<a mat-icon-button>
|
||||||
<mat-icon (click)="confirmDelete(jitsiRoom)">delete</mat-icon>
|
<mat-icon (click)="confirmDelete(jitsiRoom)">delete</mat-icon>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="jitsiRoomsColumns"></tr>
|
<tr mat-header-row *matHeaderRowDef="jitsiRoomsColumns"></tr>
|
||||||
<tr mat-row *matRowDef="let myRowData; columns: jitsiRoomsColumns"></tr>
|
<tr mat-row *matRowDef="let myRowData; columns: jitsiRoomsColumns"></tr>
|
||||||
</table>
|
</table>
|
||||||
|
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="jitsiRooms.totalElements"
|
||||||
|
[pageSize]="jitsiRooms.size" (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
|
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
|
||||||
<mat-card>
|
<mat-card>
|
||||||
@ -98,8 +105,8 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field *ngIf="jitsiRoom.starts">
|
<mat-form-field *ngIf="jitsiRoom.starts">
|
||||||
<input matInput [ngxMatDatetimePicker]="moderationStartsPicker" [(ngModel)]="jitsiRoom.moderationStarts" formControlName="moderationStarts"
|
<input matInput [ngxMatDatetimePicker]="moderationStartsPicker" [(ngModel)]="jitsiRoom.moderationStarts"
|
||||||
placeholder="{{'jitsi.rooms.moderationStarts' | i18n}}">
|
formControlName="moderationStarts" placeholder="{{'jitsi.rooms.moderationStarts' | i18n}}">
|
||||||
<mat-datepicker-toggle matSuffix [for]="moderationStartsPicker"></mat-datepicker-toggle>
|
<mat-datepicker-toggle matSuffix [for]="moderationStartsPicker"></mat-datepicker-toggle>
|
||||||
<ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker>
|
<ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
|
@ -4,11 +4,13 @@ import {Sort} from '@angular/material/sort';
|
|||||||
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
|
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
|
||||||
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
|
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
|
||||||
import {DatePipe} from '@angular/common';
|
import {DatePipe} from '@angular/common';
|
||||||
|
import {PageEvent} from '@angular/material/paginator';
|
||||||
|
|
||||||
import {QuotaService} from '../../services/quota.service';
|
import {QuotaService} from '../../services/quota.service';
|
||||||
import {JitsiService} from '../../services/jitsi.service';
|
import {JitsiService} from '../../services/jitsi.service';
|
||||||
import {ConfirmDialog} from '../../ui/confirm/confirm.component';
|
import {ConfirmDialog} from '../../ui/confirm/confirm.component';
|
||||||
import {I18nService} from './../../services/i18n.service';
|
import {I18nService} from './../../services/i18n.service';
|
||||||
|
import {ThrowStmt} from '@angular/compiler';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-account-jitsi',
|
selector: 'app-account-jitsi',
|
||||||
@ -20,11 +22,13 @@ export class JitsiComponent implements OnInit {
|
|||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
@ViewChild('formDirective') private formDirective: NgForm;
|
@ViewChild('formDirective') private formDirective: NgForm;
|
||||||
jitsiRoomsQuota: number = 0;
|
jitsiRoomsQuota: number = 0;
|
||||||
jitsiRooms: any[] = [];
|
jitsiRooms: any;
|
||||||
jitsiRoom: any = {};
|
jitsiRoom: any = {};
|
||||||
success: boolean;
|
success: boolean;
|
||||||
working: boolean;
|
working: boolean;
|
||||||
datetimeformat: String;
|
datetimeformat: String;
|
||||||
|
page: any = {page: 0, size: 10, sort: "id", desc: false};
|
||||||
|
pageSizeOptions: number[] = [5, 10, 25, 50];
|
||||||
|
|
||||||
jitsiRoomsColumns = ["share", "room", "starts", "moderationStarts", "expires", "moderationUrl", "delete"];
|
jitsiRoomsColumns = ["share", "room", "starts", "moderationStarts", "expires", "moderationUrl", "delete"];
|
||||||
|
|
||||||
@ -85,11 +89,33 @@ export class JitsiComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.jitsiService.get().subscribe((data: any) => {
|
this.jitsiService.get(this.page.page, this.page.size, this.page.sort, this.page.desc).subscribe((data: any) => {
|
||||||
this.jitsiRooms = data;
|
this.jitsiRooms = data;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePages(event: PageEvent) {
|
||||||
|
this.page.page = event.pageIndex;
|
||||||
|
this.page.size = event.pageSize;
|
||||||
|
this.jitsiService.get(this.page.page, this.page.size, this.page.sort, this.page.desc).subscribe((data: any) => {
|
||||||
|
this.jitsiRooms = data;
|
||||||
|
}, (error) => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSort(sort: Sort) {
|
||||||
|
if(sort.direction == "") {
|
||||||
|
this.page.sort = "id";
|
||||||
|
this.page.desc = false;
|
||||||
|
} else {
|
||||||
|
this.page.sort = sort.active;
|
||||||
|
this.page.desc = sort.direction == "desc";
|
||||||
|
}
|
||||||
|
this.jitsiService.get(this.page.page, this.page.size, this.page.sort, this.page.desc).subscribe((data: any) => {
|
||||||
|
this.jitsiRooms = data;
|
||||||
|
}, (error) => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
confirmDelete(jitsiRoom) {
|
confirmDelete(jitsiRoom) {
|
||||||
const dialogRef = this.dialog.open(ConfirmDialog, {
|
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||||
data: {
|
data: {
|
||||||
@ -107,28 +133,9 @@ export class JitsiComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sortData(sort: Sort) {
|
|
||||||
const data = this.jitsiRooms.slice();
|
|
||||||
if(!sort.active || sort.direction === '') {
|
|
||||||
this.jitsiRooms = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.jitsiRooms = data.sort((a, b) => {
|
|
||||||
const isAsc = sort.direction === 'asc';
|
|
||||||
switch(sort.active) {
|
|
||||||
case 'room': return this.compare(a.room, b.room, isAsc);
|
|
||||||
case 'starts': return this.compare(a.room, b.room, isAsc);
|
|
||||||
case 'moderationStarts': return this.compare(a.room, b.room, isAsc);
|
|
||||||
case 'expires': return this.compare(a.room, b.room, isAsc);
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
compare(a: number | string | String, b: number | string | String, isAsc: boolean) {
|
|
||||||
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
share(jitsiRoom) {
|
share(jitsiRoom) {
|
||||||
const dialogRef = this.dialog.open(JitsiShareDialog, {
|
const dialogRef = this.dialog.open(JitsiShareDialog, {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input id="code" name="code" matInput placeholder="{{'security.2fa.totp.code' | i18n}}" formControlName="code"
|
<input id="code" name="code" matInput placeholder="{{'security.2fa.totp.code' | i18n}}" formControlName="code"
|
||||||
required>
|
required matInputAutofocus>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{'security.2fa.totp.missing' | i18n}}
|
{{'security.2fa.totp.missing' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input id="username" name="username" matInput placeholder="{{'username' | i18n}}"
|
<input id="username" name="username" matInput placeholder="{{'username' | i18n}}"
|
||||||
formControlName="username" required>
|
formControlName="username" required matInputAutofocus>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{'username.missing' | i18n}}
|
{{'username.missing' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput placeholder="{{'username' | i18n}}" formControlName="username"
|
<input matInput placeholder="{{'username' | i18n}}" formControlName="username"
|
||||||
[(ngModel)]="model.username" required>
|
[(ngModel)]="model.username" required matInputAutofocus>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{'username.error' | i18n}}
|
{{'username.error' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{{'tokens.redeemed' | i18n}}
|
{{'tokens.redeemed' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput placeholder="{{'token' | i18n}}" formControlName="token">
|
<input matInput placeholder="{{'token' | i18n}}" formControlName="token" matInputAutofocus>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{'tokens.provide-valid' | i18n}}
|
{{'tokens.provide-valid' | i18n}}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
133
src/app/pages/urlshortener/urlshortener.component.html
Normal file
133
src/app/pages/urlshortener/urlshortener.component.html
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<h3>{{'urlshortener' | i18n}}</h3>
|
||||||
|
|
||||||
|
<div *ngIf="shortenedUrls">
|
||||||
|
<table mat-table matSort [dataSource]="shortenedUrls.content" (matSortChange)="updateSort($event)">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="share">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'urlshortener.share' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let shortenedUrl">
|
||||||
|
<button mat-icon-button (click)="share(shortenedUrl)">
|
||||||
|
<mat-icon>share</mat-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="link">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="link"> {{'urlshortener.link' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let shortenedUrl">
|
||||||
|
<a mat-button color="accent" href="{{ shortenedUrl.link }}" target="_blank">
|
||||||
|
<mat-icon *ngIf="shortenedUrl.password">vpn_key</mat-icon> {{ shortenedUrl.link }}
|
||||||
|
<mat-icon style="font-size: 1em;">open_in_new
|
||||||
|
</mat-icon>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="note">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'urlshortener.note' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let shortenedUrl">
|
||||||
|
{{shortenedUrl.note}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="url">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="url"> {{'urlshortener.url' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let shortenedUrl">
|
||||||
|
<a mat-button color="accent" href="{{ shortenedUrl.url }}" target="_blank">
|
||||||
|
{{ shortenedUrl.url }}
|
||||||
|
<mat-icon style="font-size: 1em;">open_in_new
|
||||||
|
</mat-icon>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="expires">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="expires"> {{'urlshortener.expires' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let shortenedUrl"> {{ shortenedUrl.expires | date:datetimeformat}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-container matColumnDef="delete">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'urlshortener.delete' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let shortenedUrl" class="text-right">
|
||||||
|
<a mat-icon-button>
|
||||||
|
<mat-icon (click)="confirmDelete(shortenedUrl)">delete</mat-icon>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="shortenedUrlColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let myRowData; columns: shortenedUrlColumns"></tr>
|
||||||
|
</table>
|
||||||
|
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="shortenedUrls.totalElements"
|
||||||
|
[pageSize]="shortenedUrls.size" (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>{{'urlshortener.info' | i18n}}</p>
|
||||||
|
<p *ngIf="!shortenedUrlQuota">{{'urlshortener.noQuota' | i18n}}</p>
|
||||||
|
<div *ngIf="shortenedUrlQuota">
|
||||||
|
<p>{{'urlshortener.left' | i18n:shortenedUrlQuota}}</p>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="{{'urlshortener.url' | i18n}}" formControlName="url"
|
||||||
|
[(ngModel)]="shortenedUrl.url" type="url">
|
||||||
|
<mat-error>
|
||||||
|
{{'urlshortener.error.url' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-expansion-panel>
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>
|
||||||
|
{{'urlshortener.advanced' | i18n}}
|
||||||
|
</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="password" placeholder="{{'password' | i18n}}" formControlName="password"
|
||||||
|
[(ngModel)]="shortenedUrl.password">
|
||||||
|
<mat-error>
|
||||||
|
<div *ngFor="let error of form.get('password').errors | keyvalue">
|
||||||
|
{{'password.error.' + error.key | i18n}}<br>
|
||||||
|
</div>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="password" placeholder="{{'password.confirm' | i18n}}" formControlName="password2"
|
||||||
|
[(ngModel)]="shortenedUrl.password2">
|
||||||
|
<mat-error>
|
||||||
|
{{'password.not-match' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="shortenedUrl.expires"
|
||||||
|
formControlName="expires" placeholder="{{'urlshortener.expires' | i18n}}">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle>
|
||||||
|
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
|
||||||
|
<mat-error>
|
||||||
|
{{'urlshortener.error.expires' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="{{'urlshortener.code' | i18n}}" formControlName="code"
|
||||||
|
[(ngModel)]="shortenedUrl.code" pattern="[a-zA-Z0-9]+">
|
||||||
|
<mat-error>
|
||||||
|
{{'urlshortener.error.code' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button *ngIf="shortenedUrlQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||||
|
{{'urlshortener.create' | i18n}}
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
</form>
|
29
src/app/pages/urlshortener/urlshortener.component.scss
Normal file
29
src/app/pages/urlshortener/urlshortener.component.scss
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
mat-form-field {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-header-cell,
|
||||||
|
.mat-cell {
|
||||||
|
&.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-cell .mat-button {
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right{
|
||||||
|
display: flex;
|
||||||
|
padding: 21px 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url {
|
||||||
|
display: block;
|
||||||
|
width: 200px;
|
||||||
|
max-width: 200px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
211
src/app/pages/urlshortener/urlshortener.component.ts
Normal file
211
src/app/pages/urlshortener/urlshortener.component.ts
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
import {Component, OnInit, ViewChild, Inject} from '@angular/core';
|
||||||
|
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||||
|
import {Sort} from '@angular/material/sort';
|
||||||
|
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
|
||||||
|
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
|
||||||
|
import {PageEvent} from '@angular/material/paginator';
|
||||||
|
import {ActivatedRoute} from '@angular/router';
|
||||||
|
|
||||||
|
import {MatchingValidator} from './../../utils/matching.validator';
|
||||||
|
import {QuotaService} from '../../services/quota.service';
|
||||||
|
import {UrlShortenerService} from '../../services/urlshortener.service';
|
||||||
|
import {ConfirmDialog} from '../../ui/confirm/confirm.component';
|
||||||
|
import {I18nService} from '../../services/i18n.service';
|
||||||
|
import {environment} from '../../../environments/environment';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-urlshortener',
|
||||||
|
templateUrl: './urlshortener.component.html',
|
||||||
|
styleUrls: ['./urlshortener.component.scss']
|
||||||
|
})
|
||||||
|
export class UrlShortenerComponent implements OnInit {
|
||||||
|
|
||||||
|
form: FormGroup;
|
||||||
|
@ViewChild('formDirective') private formDirective: NgForm;
|
||||||
|
shortenedUrlQuota: number = 0;
|
||||||
|
shortenedUrls: any;
|
||||||
|
shortenedUrl: any = {};
|
||||||
|
success: boolean;
|
||||||
|
working: boolean;
|
||||||
|
datetimeformat: String;
|
||||||
|
page: any = {page: 0, size: 10, sort: "code", desc: false};
|
||||||
|
pageSizeOptions: number[] = [5, 10, 25, 50];
|
||||||
|
|
||||||
|
shortenedUrlColumns = ["share", "link", "note", "url", "expires", "delete"];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private quotaService: QuotaService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private urlShortenerService: UrlShortenerService,
|
||||||
|
private i18n: I18nService,
|
||||||
|
public dialog: MatDialog) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.datetimeformat = this.i18n.get('format.datetime', []);
|
||||||
|
|
||||||
|
this.form = this.formBuilder.group({
|
||||||
|
url: ['', Validators.required],
|
||||||
|
code: ['', Validators.nullValidator],
|
||||||
|
password: ['', Validators.nullValidator],
|
||||||
|
password2: ['', Validators.nullValidator],
|
||||||
|
expires: ['', Validators.nullValidator],
|
||||||
|
}, {
|
||||||
|
validator: MatchingValidator('password', 'password2')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
create(): void {
|
||||||
|
this.working = true;
|
||||||
|
|
||||||
|
this.urlShortenerService.create(this.shortenedUrl).subscribe(response => {
|
||||||
|
this.update();
|
||||||
|
this.formDirective.resetForm();
|
||||||
|
this.shortenedUrl = {};
|
||||||
|
this.working = false;
|
||||||
|
}, (error) => {
|
||||||
|
this.working = false;
|
||||||
|
if(error.status == 409) {
|
||||||
|
let errors = {};
|
||||||
|
for(let code of error.error) {
|
||||||
|
errors[code.field] = errors[code.field] || {};
|
||||||
|
errors[code.field][code.code] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let code in errors) {
|
||||||
|
this.form.get(code).setErrors(errors[code]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.shortenedUrlQuota = 0;
|
||||||
|
this.quotaService.quotas().subscribe((data: any) => {
|
||||||
|
for(let quota of data) {
|
||||||
|
if(quota.name == "url_shortener") {
|
||||||
|
this.shortenedUrlQuota = quota.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.urlShortenerService.get(this.page.page, this.page.size, this.page.sort, this.page.desc).subscribe((data: any) => {
|
||||||
|
this.shortenedUrls = data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePages(event: PageEvent) {
|
||||||
|
this.page.page = event.pageIndex;
|
||||||
|
this.page.size = event.pageSize;
|
||||||
|
this.urlShortenerService.get(this.page.page, this.page.size, this.page.sort, this.page.desc).subscribe((data: any) => {
|
||||||
|
this.shortenedUrls = data;
|
||||||
|
}, (error) => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSort(sort: Sort) {
|
||||||
|
if(sort.direction == "") {
|
||||||
|
this.page.sort = "code";
|
||||||
|
this.page.desc = false;
|
||||||
|
} else {
|
||||||
|
this.page.sort = sort.active;
|
||||||
|
this.page.desc = sort.direction == "desc";
|
||||||
|
}
|
||||||
|
this.urlShortenerService.get(this.page.page, this.page.size, this.page.sort, this.page.desc).subscribe((data: any) => {
|
||||||
|
this.shortenedUrls = data;
|
||||||
|
}, (error) => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDelete(shortenedUrl) {
|
||||||
|
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||||
|
data: {
|
||||||
|
'label': 'urlshortener.confirmDelete',
|
||||||
|
'args': [shortenedUrl.url]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if(result) {
|
||||||
|
this.urlShortenerService.delete(shortenedUrl.code).subscribe((result: any) => {
|
||||||
|
this.update();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
share(shortenedUrl) {
|
||||||
|
const dialogRef = this.dialog.open(UrlShortenerShareDialog, {
|
||||||
|
data: shortenedUrl,
|
||||||
|
minWidth: '300px',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
open(jitsiRoom: any, moderation: boolean) {
|
||||||
|
return (moderation && jitsiRoom.moderationStarts != null || !jitsiRoom.starts || Date.parse(jitsiRoom.starts) < new Date().getTime()) && (!moderation || jitsiRoom.moderationStarts == null || Date.parse(jitsiRoom.moderationStarts) < new Date().getTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-urlshortener-share-dialog',
|
||||||
|
templateUrl: 'urlshortener.share.html',
|
||||||
|
styleUrls: ['./urlshortener.share.scss']
|
||||||
|
})
|
||||||
|
export class UrlShortenerShareDialog {
|
||||||
|
|
||||||
|
shortenedUrl: any;
|
||||||
|
datetimeformat: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private i18n: I18nService,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
public dialogRef: MatDialogRef<UrlShortenerShareDialog>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||||
|
this.shortenedUrl = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.datetimeformat = this.i18n.get('format.datetime', []);
|
||||||
|
this.shortenedUrl.shareText = this.i18n.get('urlshortener.share.text', [this.shortenedUrl.link]);
|
||||||
|
}
|
||||||
|
|
||||||
|
copyToClipboard(text) {
|
||||||
|
const selBox = document.createElement('textarea');
|
||||||
|
selBox.value = text;
|
||||||
|
document.body.appendChild(selBox);
|
||||||
|
selBox.focus();
|
||||||
|
selBox.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(selBox);
|
||||||
|
this.snackBar.open(this.i18n.get("urlshortener.share.clipboard.copied", []), this.i18n.get("close", []), {
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-urlshortener-password',
|
||||||
|
templateUrl: './urlshortener.password.html',
|
||||||
|
styleUrls: ['./urlshortener.password.scss']
|
||||||
|
})
|
||||||
|
export class UrlShortenerPasswordComponent implements OnInit {
|
||||||
|
|
||||||
|
code : string;
|
||||||
|
apiUrl = environment.apiUrl;
|
||||||
|
invalidPassword: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.code = this.route.snapshot.paramMap.get('code');
|
||||||
|
|
||||||
|
this.route.queryParams.subscribe(params => {
|
||||||
|
if(params['error'] || params['error'] == '') {
|
||||||
|
this.invalidPassword = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
src/app/pages/urlshortener/urlshortener.password.html
Normal file
20
src/app/pages/urlshortener/urlshortener.password.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<form action="{{apiUrl}}/url/shortener/{{code}}" method="POST" #loginForm>
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-content>
|
||||||
|
<h2>{{'urlshortener.password' | i18n}}
|
||||||
|
</h2>
|
||||||
|
<mat-form-field>
|
||||||
|
<input id="password" name="password" matInput type="password" placeholder="{{'password' | i18n}}" required matInputAutofocus>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-error *ngIf="invalidPassword">
|
||||||
|
{{'urlshortener.password.invalid' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button type="submit" (click)="loginForm.submit()" mat-raised-button color="primary"
|
||||||
|
[disabled]="loginForm.invalid">{{'urlshortener.password.submit' |
|
||||||
|
i18n}}<mat-icon style="font-size: 1em;">open_in_new
|
||||||
|
</mat-icon></button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
</form>
|
3
src/app/pages/urlshortener/urlshortener.password.scss
Normal file
3
src/app/pages/urlshortener/urlshortener.password.scss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mat-form-field {
|
||||||
|
display: block;
|
||||||
|
}
|
37
src/app/pages/urlshortener/urlshortener.share.html
Normal file
37
src/app/pages/urlshortener/urlshortener.share.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<h1 mat-dialog-title>
|
||||||
|
<mat-icon inline="true">share</mat-icon> {{'urlshortener.share' | i18n}}
|
||||||
|
</h1>
|
||||||
|
<div mat-dialog-content>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<qrcode [qrdata]="shortenedUrl.link" [width]="400" [errorCorrectionLevel]="'M'"></qrcode>
|
||||||
|
<textarea matInput [(ngModel)]="shortenedUrl.shareText"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-list>
|
||||||
|
<mat-list-item>
|
||||||
|
<a mat-raised-button color="accent" [disabled]="!shortenedUrl.shareText"
|
||||||
|
href="mailto:?&subject={{'urlshortener.share.email.subject' | i18n}}&body={{shortenedUrl.shareText}}"
|
||||||
|
mat-dialog-close>
|
||||||
|
<mat-icon>mail</mat-icon> {{'urlshortener.share.email' | i18n}}
|
||||||
|
</a>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<a mat-raised-button color="accent" (click)="copyToClipboard(shortenedUrl.link)"
|
||||||
|
[disabled]="!shortenedUrl.shareText" mat-dialog-close>
|
||||||
|
<mat-icon>content_paste</mat-icon> {{'urlshortener.share.clipboard.url' | i18n}}
|
||||||
|
</a>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<a mat-raised-button color="accent" (click)="copyToClipboard(shortenedUrl.shareText)"
|
||||||
|
[disabled]="!shortenedUrl.shareText" mat-dialog-close>
|
||||||
|
<mat-icon>content_paste</mat-icon> {{'urlshortener.share.clipboard.text' | i18n}}
|
||||||
|
</a>
|
||||||
|
</mat-list-item>
|
||||||
|
</mat-list>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<button mat-button mat-dialog-close>{{'close' | i18n}}</button>
|
||||||
|
</div>
|
12
src/app/pages/urlshortener/urlshortener.share.scss
Normal file
12
src/app/pages/urlshortener/urlshortener.share.scss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.mat-raised-button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
import {ActivatedRoute, Router, ParamMap} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
|
|
||||||
import {ProfileService} from '../../services/profile.service';
|
import {ProfileService} from '../../services/profile.service';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {HttpClient} from '@angular/common/http';
|
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||||
|
|
||||||
import {environment} from '../../environments/environment';
|
import {environment} from '../../environments/environment';
|
||||||
|
|
||||||
@ -11,8 +11,9 @@ export class JitsiService {
|
|||||||
constructor(private http: HttpClient) {
|
constructor(private http: HttpClient) {
|
||||||
}
|
}
|
||||||
|
|
||||||
get() {
|
get(page: number, size: number, sort: string, desc: boolean) {
|
||||||
return this.http.get(environment.apiUrl + "/jitsi/rooms");
|
const httpParams = new HttpParams().set("page", "" + page).set("size", "" + size).set("sort", sort).set("desc", "" + desc);
|
||||||
|
return this.http.get(environment.apiUrl + "/jitsi/rooms", {params: httpParams});
|
||||||
}
|
}
|
||||||
|
|
||||||
create(jitsiRoom) {
|
create(jitsiRoom) {
|
||||||
|
27
src/app/services/urlshortener.service.ts
Normal file
27
src/app/services/urlshortener.service.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||||
|
|
||||||
|
import {environment} from '../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class UrlShortenerService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get(page: number, size: number, sort: string, desc: boolean) {
|
||||||
|
const httpParams = new HttpParams().set("page", "" + page).set("size", "" + size).set("sort", sort).set("desc", "" + desc);
|
||||||
|
return this.http.get(environment.apiUrl + "/url/shortener", {params: httpParams});
|
||||||
|
}
|
||||||
|
|
||||||
|
create(shortendUrlModel) {
|
||||||
|
return this.http.post(environment.apiUrl + "/url/shortener", shortendUrlModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(code) {
|
||||||
|
return this.http.delete(environment.apiUrl + "/url/shortener/" + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,9 +8,16 @@
|
|||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="quota">
|
<ng-container matColumnDef="value">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="value"> {{'quotas.value' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="value"> {{'quotas.value' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let quota"> {{quota.value}} </td>
|
<td mat-cell *matCellDef="let quota"><span *ngIf="quota.disposable">{{quota.value}}</span> </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="fixed_value">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'quotas.fixed_value' | i18n}} <mat-icon
|
||||||
|
#primaryHint="matTooltip" inline matTooltip="{{'quotas.fixed_value.hint' | i18n}}">info</mat-icon>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let quota"><span *ngIf="!quota.disposable">{{quota.value}}</span> </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="quotaUnit">
|
<ng-container matColumnDef="quotaUnit">
|
||||||
|
@ -11,7 +11,7 @@ export class QuotasComponent implements OnInit {
|
|||||||
|
|
||||||
|
|
||||||
@Input() quotas;
|
@Input() quotas;
|
||||||
quotaColumns = ["name", "quota", "quotaUnit"];
|
quotaColumns = ["name", "value", "fixed_value", "quotaUnit"];
|
||||||
|
|
||||||
constructor(private i18n: I18nService) {}
|
constructor(private i18n: I18nService) {}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user