make shortened urls editable

This commit is contained in:
_Bastler 2021-08-16 19:06:12 +02:00
parent d7fb55a661
commit 323ce0e885
6 changed files with 225 additions and 48 deletions

View File

@ -44,7 +44,7 @@ import {ConfirmDialog} from './ui/confirm/confirm.component'
import {UserComponent} from './pages/user/user.component'
import {JitsiComponent, JitsiShareDialog} from './pages/jitsi/jitsi.component'
import {ParteyTimeslotsComponent, ParteyTimeslotDialog} from './pages/partey/timeslots/timeslots.compontent'
import {UrlShortenerComponent, UrlShortenerPasswordComponent, UrlShortenerShareDialog} from './pages/urlshortener/urlshortener.component'
import {UrlShortenerComponent, UrlShortenerPasswordComponent, UrlShortenerShareDialog, UrlShortenerEditDialog} from './pages/urlshortener/urlshortener.component'
import {I18nService} from './services/i18n.service';
@ -103,7 +103,7 @@ export class XhrInterceptor implements HttpInterceptor {
JitsiComponent, JitsiShareDialog,
ParteyTimeslotsComponent, ParteyTimeslotDialog,
MinetestAccountsComponent,
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerPasswordComponent
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerEditDialog, UrlShortenerPasswordComponent
],
imports: [
BrowserModule,

View File

@ -49,6 +49,14 @@
<td mat-cell *matCellDef="let shortenedUrl"> {{ shortenedUrl.expires | date:datetimeformat}} </td>
</ng-container>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'urlshortener.edit' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl" class="text-right">
<a mat-icon-button>
<mat-icon (click)="edit(shortenedUrl)">edit</mat-icon>
</a>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'urlshortener.delete' | i18n}} </th>

View File

@ -1,23 +1,23 @@
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, FormControl} 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 { 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, FormControl } 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';
import {debounceTime} from 'rxjs/operators';
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';
import { debounceTime } from 'rxjs/operators';
@Component({
selector: 'app-urlshortener',
templateUrl: './urlshortener.component.html',
styleUrls: ['./urlshortener.component.scss']
styleUrls: [ './urlshortener.component.scss' ]
})
export class UrlShortenerComponent implements OnInit {
@ -30,28 +30,28 @@ export class UrlShortenerComponent implements OnInit {
success: boolean;
working: boolean;
datetimeformat: String;
page: any = {page: 0, size: 10, sort: "code", desc: false, search: ""};
pageSizeOptions: number[] = [5, 10, 25, 50];
page: any = { page: 0, size: 10, sort: "code", desc: false, search: "" };
pageSizeOptions: number[] = [ 5, 10, 25, 50 ];
shortenedUrlColumns = ["share", "link", "note", "url", "expires", "delete"];
shortenedUrlColumns = [ "share", "link", "note", "url", "expires", "edit", "delete" ];
constructor(
private quotaService: QuotaService,
private formBuilder: FormBuilder,
private urlShortenerService: UrlShortenerService,
private i18n: I18nService,
public dialog: MatDialog) {}
public dialog: MatDialog) { }
ngOnInit(): void {
this.datetimeformat = this.i18n.get('format.datetime', []);
this.form = this.formBuilder.group({
url: ['', Validators.required],
note: ['', Validators.nullValidator],
code: ['', Validators.nullValidator],
password: ['', Validators.nullValidator],
password2: ['', Validators.nullValidator],
expires: ['', Validators.nullValidator],
url: [ '', Validators.required ],
note: [ '', Validators.nullValidator ],
code: [ '', Validators.nullValidator ],
password: [ '', Validators.nullValidator ],
password2: [ '', Validators.nullValidator ],
expires: [ '', Validators.nullValidator ],
}, {
validator: MatchingValidator('password', 'password2')
});
@ -77,15 +77,15 @@ export class UrlShortenerComponent implements OnInit {
this.working = false;
}, (error) => {
this.working = false;
if(error.status == 409) {
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 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]);
for (let code in errors) {
this.form.get(code).setErrors(errors[ code ]);
}
}
})
@ -94,8 +94,8 @@ export class UrlShortenerComponent implements OnInit {
update() {
this.shortenedUrlQuota = 0;
this.quotaService.quotas().subscribe((data: any) => {
for(let quota of data) {
if(quota.name == "url_shortener") {
for (let quota of data) {
if (quota.name == "url_shortener") {
this.shortenedUrlQuota = quota.value;
}
}
@ -112,7 +112,7 @@ export class UrlShortenerComponent implements OnInit {
this.page.search = this.searchFormControl.value ? this.searchFormControl.value : "";
this.urlShortenerService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.page.search).subscribe((data: any) => {
this.shortenedUrls = data;
}, (error) => {})
}, (error) => { })
}
updatePages(event: PageEvent) {
@ -121,11 +121,11 @@ export class UrlShortenerComponent implements OnInit {
this.page.search = this.searchFormControl.value ? this.searchFormControl.value : "";
this.urlShortenerService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.page.search).subscribe((data: any) => {
this.shortenedUrls = data;
}, (error) => {})
}, (error) => { })
}
updateSort(sort: Sort) {
if(sort.direction == "") {
if (sort.direction == "") {
this.page.sort = "code";
this.page.desc = false;
} else {
@ -135,19 +135,19 @@ export class UrlShortenerComponent implements OnInit {
this.page.search = this.searchFormControl.value ? this.searchFormControl.value : "";
this.urlShortenerService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.page.search).subscribe((data: any) => {
this.shortenedUrls = data;
}, (error) => {})
}, (error) => { })
}
confirmDelete(shortenedUrl) {
const dialogRef = this.dialog.open(ConfirmDialog, {
data: {
'label': 'urlshortener.confirmDelete',
'args': [shortenedUrl.url]
'args': [ shortenedUrl.url ]
}
})
dialogRef.afterClosed().subscribe(result => {
if(result) {
if (result) {
this.urlShortenerService.delete(shortenedUrl.code).subscribe((result: any) => {
this.update();
})
@ -162,15 +162,96 @@ export class UrlShortenerComponent implements OnInit {
});
}
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());
edit(shortenedUrl) {
const dialogRef = this.dialog.open(UrlShortenerEditDialog, {
data: shortenedUrl,
minWidth: '400px',
});
dialogRef.afterClosed().subscribe(result => {
console.log(result);
if (result) {
this.update();
}
});
}
}
@Component({
selector: 'app-urlshortener-edit-dialog',
templateUrl: 'urlshortener.edit.html',
styleUrls: [ './urlshortener.edit.scss' ]
})
export class UrlShortenerEditDialog {
form: FormGroup;
shortenedUrlModel: any;
datetimeformat: string;
constructor(
private i18n: I18nService,
private urlShortenerService: UrlShortenerService,
private snackBar: MatSnackBar,
public dialogRef: MatDialogRef<UrlShortenerShareDialog>,
private formBuilder: FormBuilder,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.shortenedUrlModel = {};
this.shortenedUrlModel.code = data.code;
this.shortenedUrlModel.url = data.url;
this.shortenedUrlModel.newCode = data.code;
this.shortenedUrlModel.note = data.note;
this.shortenedUrlModel.expires = data.expires;
}
ngOnInit(): void {
this.datetimeformat = this.i18n.get('format.datetime', []);
this.form = this.formBuilder.group({
url: [ '', Validators.required ],
note: [ '', Validators.nullValidator ],
code: [ '', Validators.nullValidator ],
newPassword: [ '', Validators.nullValidator ],
password: [ '', Validators.nullValidator ],
password2: [ '', Validators.nullValidator ],
expires: [ '', Validators.nullValidator ],
});
}
newPassword(event) {
this.shortenedUrlModel.newPassword = event.checked;
if (!this.shortenedUrlModel.newPassword) {
this.shortenedUrlModel.password = "";
this.shortenedUrlModel.password2 = "";
this.form.get('password').setErrors(null);
this.form.get('password2').setErrors(null);
}
}
save() {
this.urlShortenerService.update(this.shortenedUrlModel).subscribe((result: any) => {
this.dialogRef.close(result);
}, (error) => {
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 ]);
}
}
});
}
}
@Component({
selector: 'app-urlshortener-share-dialog',
templateUrl: 'urlshortener.share.html',
styleUrls: ['./urlshortener.share.scss']
styleUrls: [ './urlshortener.share.scss' ]
})
export class UrlShortenerShareDialog {
@ -187,7 +268,7 @@ export class UrlShortenerShareDialog {
ngOnInit(): void {
this.datetimeformat = this.i18n.get('format.datetime', []);
this.shortenedUrl.shareText = this.i18n.get('urlshortener.share.text', [this.shortenedUrl.link]);
this.shortenedUrl.shareText = this.i18n.get('urlshortener.share.text', [ this.shortenedUrl.link ]);
}
copyToClipboard(text) {
@ -206,10 +287,11 @@ export class UrlShortenerShareDialog {
}
@Component({
selector: 'app-urlshortener-password',
templateUrl: './urlshortener.password.html',
styleUrls: ['./urlshortener.password.scss']
styleUrls: [ './urlshortener.password.scss' ]
})
export class UrlShortenerPasswordComponent implements OnInit {
@ -218,13 +300,13 @@ export class UrlShortenerPasswordComponent implements OnInit {
invalidPassword: boolean = false;
constructor(
private route: ActivatedRoute) {}
private route: ActivatedRoute) { }
async ngOnInit() {
this.code = this.route.snapshot.paramMap.get('code');
this.route.queryParams.subscribe(params => {
if(params['error'] || params['error'] == '') {
if (params[ 'error' ] || params[ 'error' ] == '') {
this.invalidPassword = true;
}
});

View File

@ -0,0 +1,76 @@
<h1 mat-dialog-title>
<mat-icon inline="true">edit</mat-icon> {{'urlshortener.edit' | i18n}}
</h1>
<mat-dialog-content>
<form [formGroup]="form">
<mat-form-field>
<input matInput placeholder="{{'urlshortener.url' | i18n}}" formControlName="url"
[(ngModel)]="shortenedUrlModel.url" type="url">
<mat-error>
{{'urlshortener.error.url' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<textarea matInput type="note" placeholder="{{'urlshortener.note' | i18n}}" formControlName="note"
[(ngModel)]="shortenedUrlModel.note"></textarea>
<mat-error>
{{'urlshortener.error.note' | 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-slide-toggle (change)="newPassword($event)" formControlName="newPassword">
{{'urlshortener.newPassword' | i18n}}
</mat-slide-toggle>
<mat-form-field *ngIf="shortenedUrlModel.newPassword">
<input matInput type="password" placeholder="{{'password' | i18n}}" formControlName="password"
[(ngModel)]="shortenedUrlModel.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 *ngIf="shortenedUrlModel.newPassword">
<input matInput type="password" placeholder="{{'password.confirm' | i18n}}" formControlName="password2"
[(ngModel)]="shortenedUrlModel.password2">
<mat-error>
{{'password.not-match' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="shortenedUrlModel.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)]="shortenedUrlModel.newCode">
<mat-error>
{{'urlshortener.error.code' | i18n}}
</mat-error>
</mat-form-field>
</mat-expansion-panel>
</form>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button>
<button [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent">{{
'urlshortener.save'
| i18n}}</button>
</mat-dialog-actions>

View File

@ -0,0 +1,7 @@
mat-form-field {
display: block;
}
mat-slide-toggle {
margin-bottom: 24px;
}

View File

@ -24,6 +24,10 @@ export class UrlShortenerService {
return this.http.post(environment.apiUrl + "/url/shortener", shortendUrlModel);
}
update(shortendUrlModel) {
return this.http.patch(environment.apiUrl + "/url/shortener", shortendUrlModel);
}
delete(code) {
return this.http.delete(environment.apiUrl + "/url/shortener/" + code);
}