download handout

This commit is contained in:
2020-05-28 21:59:10 +02:00
parent 1878e4f0d9
commit 2023f2b561
7 changed files with 74 additions and 15 deletions

View File

@@ -12,7 +12,13 @@ import {Show} from './show';
import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component'; import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component';
import {UserService} from '../../../services/user/user.service'; import {UserService} from '../../../services/user/user.service';
import {User} from '../../../services/user/user'; import {User} from '../../../services/user/user';
import {ConfigService} from '../../../services/config.service';
import {Config} from '../../../services/config';
export interface DownloadOptions {
copyright?: boolean;
chordMode?: ChordMode;
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -25,20 +31,21 @@ export class DocxService {
private songService: SongService, private songService: SongService,
private textRenderingService: TextRenderingService, private textRenderingService: TextRenderingService,
private userService: UserService, private userService: UserService,
private configService: ConfigService,
) { ) {
} }
public async create(showId: string): Promise<any> { public async create(showId: string, options: DownloadOptions = {}): Promise<any> {
const {show, songs, user} = await this.prepareData(showId); const {show, songs, user, config} = await this.prepareData(showId);
const type = new ShowTypePipe().transform(show.showType); const type = new ShowTypePipe().transform(show.showType);
const title = `${type} ${show.date.toDate().toLocaleDateString()}`; const title = `${type} ${show.date.toDate().toLocaleDateString()}`;
const paragraphs = [ const paragraphs = [
...this.renderTitle(title), ...this.renderTitle(title),
...this.renderSongs(songs), ...this.renderSongs(songs, options, config),
]; ];
const document = this.prepareNewDocument(type, user.name); const document = this.prepareNewDocument(type, user.name, options);
document.addSection({ document.addSection({
properties: {top: 400, bottom: 400, left: 400, right: 400}, properties: {top: 400, bottom: 400, left: 400, right: 400},
children: paragraphs, children: paragraphs,
@@ -50,7 +57,7 @@ export class DocxService {
this.saveAs(blob, `${title}.docx`); this.saveAs(blob, `${title}.docx`);
} }
private prepareNewDocument(type: string, name: string): Document { private prepareNewDocument(type: string, name: string, options: DownloadOptions): Document {
return new Document({ return new Document({
creator: name, creator: name,
title: type, title: type,
@@ -63,7 +70,15 @@ export class DocxService {
basedOn: 'Normal', basedOn: 'Normal',
next: 'Normal', next: 'Normal',
quickFormat: true, quickFormat: true,
run: {font: 'courier new'}, run: options?.chordMode === 'hide' ? {} : {font: 'courier new'},
paragraph: {indent: {left: 0}},
}, {
id: 'licence',
name: 'Lizenz',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {size: 15, color: 'grey'},
paragraph: {indent: {left: 0}}, paragraph: {indent: {left: 0}},
}, },
] ]
@@ -71,18 +86,20 @@ export class DocxService {
}) })
} }
private renderSongs(songs: { showSong: ShowSong; song: Song; sections: Section[] }[]): Paragraph[] { private renderSongs(songs: { showSong: ShowSong; song: Song; sections: Section[] }[], options: DownloadOptions, config: Config): Paragraph[] {
return songs.reduce((p, song) => [...p, ...this.renderSong(song.showSong, song.song, song.sections)], []) return songs.reduce((p, song) => [...p, ...this.renderSong(song.showSong, song.song, song.sections, options, config)], [])
} }
private renderSong(showSong: ShowSong, song: Song, sections: Section[]): Paragraph[] { private renderSong(showSong: ShowSong, song: Song, sections: Section[], options: DownloadOptions, config: Config): Paragraph[] {
const songTitle = this.renderSongTitle(song); const songTitle = this.renderSongTitle(song);
const songText = this.renderSongText(sections, showSong.chordMode); const copyright = this.renderCopyright(song, options, config);
const songText = this.renderSongText(sections, options?.chordMode ?? showSong.chordMode);
return [ return [
songTitle, songTitle,
copyright,
...songText ...songText
] ].filter(_ => _);
} }
private renderSongText(sections: Section[], chordMode: ChordMode) { private renderSongText(sections: Section[], chordMode: ChordMode) {
@@ -100,6 +117,23 @@ export class DocxService {
}); });
} }
private renderCopyright(song: Song, options: DownloadOptions, config: Config): Paragraph {
if (!options?.copyright) return null;
const label = song.label ? song.label + ', ' : '';
const artist = song.artist ? song.artist + ', ' : '';
const termsOfUse = song.termsOfUse ? song.termsOfUse + ', ' : '';
const origin = song.origin ? song.origin + ', ' : '';
const licence = song.legalOwner === 'CCLI'
? 'CCLI-Liednummer: ' + song.legalOwnerId + ', CCLI-Lizenz: ' + config.ccliLicenseId
: 'CCLI-Liednummer: ' + song.legalOwnerId;
return new Paragraph({
text: artist + label + termsOfUse + origin + licence,
style: 'licence',
});
}
private renderSection(section: Section, chordMode: ChordMode): Paragraph[] { private renderSection(section: Section, chordMode: ChordMode): Paragraph[] {
return section.lines return section.lines
@@ -138,9 +172,10 @@ export class DocxService {
return [songTitle] return [songTitle]
} }
private async prepareData(showId: string): Promise<{ songs: ({ showSong: ShowSong, song: Song, sections: Section[] })[]; show: Show, user: User }> { private async prepareData(showId: string): Promise<{ songs: ({ showSong: ShowSong, song: Song, sections: Section[] })[]; show: Show, user: User, config: Config }> {
const show = await this.showService.read$(showId).pipe(first()).toPromise(); const show = await this.showService.read$(showId).pipe(first()).toPromise();
const user = await this.userService.getUserbyId(show.owner); const user = await this.userService.getUserbyId(show.owner);
const config = await this.configService.get();
const showSongs = await this.showSongService.list(showId); const showSongs = await this.showSongService.list(showId);
const songsAsync = await showSongs.map(async showSong => { const songsAsync = await showSongs.map(async showSong => {
@@ -153,7 +188,7 @@ export class DocxService {
} }
}) })
const songs = await Promise.all(songsAsync); const songs = await Promise.all(songsAsync);
return {songs, show, user}; return {songs, show, user, config};
} }
private saveAs(blob, fileName) { private saveAs(blob, fileName) {

View File

@@ -40,7 +40,11 @@
Veröffentlichung zurückziehen Veröffentlichung zurückziehen
</app-button> </app-button>
</ng-container> </ng-container>
<app-button (click)="onDownload()" [icon]="faDownload">Herunterladen</app-button> <app-button [icon]="faDownload" [matMenuTriggerFor]="menu">Herunterladen</app-button>
<mat-menu #menu="matMenu">
<app-button (click)="onDownload()" [icon]="faUser">Ablauf für Lobpreisleiter</app-button>
<app-button (click)="onDownloadHandout()" [icon]="faUsers">Handout mit Copyright Infos</app-button>
</mat-menu>
</app-button-row> </app-button-row>
</app-card> </app-card>
</div> </div>

View File

@@ -14,6 +14,8 @@ import {faBoxOpen} from '@fortawesome/free-solid-svg-icons/faBoxOpen';
import {faExternalLinkAlt} from '@fortawesome/free-solid-svg-icons/faExternalLinkAlt'; import {faExternalLinkAlt} from '@fortawesome/free-solid-svg-icons/faExternalLinkAlt';
import {faLock} from '@fortawesome/free-solid-svg-icons/faLock'; import {faLock} from '@fortawesome/free-solid-svg-icons/faLock';
import {faFileDownload} from '@fortawesome/free-solid-svg-icons/faFileDownload'; import {faFileDownload} from '@fortawesome/free-solid-svg-icons/faFileDownload';
import {faUser} from '@fortawesome/free-solid-svg-icons/faUser';
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers';
@Component({ @Component({
selector: 'app-show', selector: 'app-show',
@@ -32,6 +34,8 @@ export class ShowComponent implements OnInit {
public faPublish = faExternalLinkAlt; public faPublish = faExternalLinkAlt;
public faUnpublish = faLock; public faUnpublish = faLock;
public faDownload = faFileDownload; public faDownload = faFileDownload;
public faUser = faUser;
public faUsers = faUsers;
constructor( constructor(
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
@@ -80,4 +84,8 @@ export class ShowComponent implements OnInit {
public async onDownload(): Promise<void> { public async onDownload(): Promise<void> {
await this.docxService.create(this.showId); await this.docxService.create(this.showId);
} }
public async onDownloadHandout(): Promise<void> {
await this.docxService.create(this.showId, {chordMode: 'hide', copyright: true});
}
} }

View File

@@ -27,6 +27,7 @@ import {AddSongModule} from '../../widget-modules/components/add-song/add-song.m
import {ButtonModule} from '../../widget-modules/components/button/button.module'; import {ButtonModule} from '../../widget-modules/components/button/button.module';
import {OwnerModule} from '../../services/user/owner.module'; import {OwnerModule} from '../../services/user/owner.module';
import {UserNameModule} from '../../services/user/user-name/user-name.module'; import {UserNameModule} from '../../services/user/user-name/user-name.module';
import {MatMenuModule} from '@angular/material/menu';
@NgModule({ @NgModule({
@@ -55,6 +56,7 @@ import {UserNameModule} from '../../services/user/user-name/user-name.module';
ButtonModule, ButtonModule,
OwnerModule, OwnerModule,
UserNameModule, UserNameModule,
MatMenuModule,
] ]
}) })
export class ShowsModule { export class ShowsModule {

View File

@@ -2,6 +2,7 @@ import {Injectable} from '@angular/core';
import {DbService} from './db.service'; import {DbService} from './db.service';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {Config} from './config'; import {Config} from './config';
import {first} from 'rxjs/operators';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -13,4 +14,8 @@ export class ConfigService {
public get get$(): Observable<Config> { public get get$(): Observable<Config> {
return this.db.doc$<Config>('global/config'); return this.db.doc$<Config>('global/config');
} }
public async get(): Promise<Config> {
return await this.db.doc$<Config>('global/config').pipe(first()).toPromise();
}
} }

View File

@@ -32,7 +32,7 @@ export class UserService {
} }
public getUserbyId(userId: string): Promise<User> { public getUserbyId(userId: string): Promise<User> {
return this.getUserbyId$('users/' + userId).pipe(first()).toPromise(); return this.getUserbyId$(userId).pipe(first()).toPromise();
} }
public getUserbyId$(userId: string): Observable<User> { public getUserbyId$(userId: string): Observable<User> {

View File

@@ -10,3 +10,8 @@ button {
display: none ; display: none ;
} }
} }
fa-icon {
width: 20px;
display: inline-block;
}