export * from './shared';
-export * from './video-add';
+export * from './video-edit';
export * from './video-list';
export * from './video-watch';
export * from './videos-routing.module';
by: string;
createdAt: Date;
categoryLabel: string;
+ category: string;
licenceLabel: string;
+ licence: string;
languageLabel: string;
+ language: string;
description: string;
duration: string;
id: string;
author: string,
createdAt: string,
categoryLabel: string,
+ category: string,
licenceLabel: string,
+ licence: string,
languageLabel: string;
+ language: string;
description: string,
duration: number;
id: string,
this.author = hash.author;
this.createdAt = new Date(hash.createdAt);
this.categoryLabel = hash.categoryLabel;
+ this.category = hash.category;
this.licenceLabel = hash.licenceLabel;
+ this.licence = hash.licence;
this.languageLabel = hash.languageLabel;
+ this.language = hash.language;
this.description = hash.description;
this.duration = Video.createDurationString(hash.duration);
this.id = hash.id;
// If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos...
return (this.nsfw && (!user || user.displayNSFW === false));
}
+
+ patch(values: Object) {
+ Object.keys(values).forEach((key) => {
+ this[key] = values[key];
+ });
+ }
+
+ toJSON() {
+ return {
+ author: this.author,
+ createdAt: this.createdAt,
+ category: this.category,
+ licence: this.licence,
+ language: this.language,
+ description: this.description,
+ duration: this.duration,
+ id: this.id,
+ isLocal: this.isLocal,
+ magnetUri: this.magnetUri,
+ name: this.name,
+ podHost: this.podHost,
+ tags: this.tags,
+ thumbnailPath: this.thumbnailPath,
+ views: this.views,
+ likes: this.likes,
+ dislikes: this.dislikes,
+ nsfw: this.nsfw
+ };
+ }
}
import { Injectable } from '@angular/core';
-import { Http } from '@angular/http';
+import { Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
.catch((res) => this.restExtractor.handleError(res));
}
+ updateVideo(video: Video) {
+ const body = {
+ name: video.name,
+ category: video.category,
+ licence: video.licence,
+ language: video.language,
+ description: video.description,
+ tags: video.tags
+ };
+ const headers = new Headers({ 'Content-Type': 'application/json' });
+ const options = new RequestOptions({ headers: headers });
+
+ return this.authHttp.put(`${VideoService.BASE_VIDEO_URL}/${video.id}`, body, options)
+ .map(this.restExtractor.extractDataBool)
+ .catch(this.restExtractor.handleError);
+ }
+
getVideos(pagination: RestPagination, sort: SortField) {
const params = this.restService.buildRestGetParams(pagination, sort);
+++ /dev/null
-export * from './video-add.component';
--- /dev/null
+export * from './video-add.component';
+export * from './video-update.component';
@Component({
selector: 'my-videos-add',
- styleUrls: [ './video-add.component.scss' ],
+ styleUrls: [ './video-edit.component.scss' ],
templateUrl: './video-add.component.html'
})
--- /dev/null
+<h3>Update {{ video.name }}</h3>
+
+<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
+
+<form novalidate [formGroup]="form">
+ <div class="form-group">
+ <label for="name">Name</label>
+ <input
+ type="text" class="form-control" id="name"
+ formControlName="name"
+ >
+ <div *ngIf="formErrors.name" class="alert alert-danger">
+ {{ formErrors.name }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="nsfw">NSFW</label>
+ <input
+ type="checkbox" id="nsfw"
+ formControlName="nsfw"
+ >
+ </div>
+
+ <div class="form-group">
+ <label for="category">Category</label>
+ <select class="form-control" id="category" formControlName="category">
+ <option></option>
+ <option *ngFor="let category of videoCategories" [value]="category.id">{{ category.label }}</option>
+ </select>
+
+ <div *ngIf="formErrors.category" class="alert alert-danger">
+ {{ formErrors.category }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="licence">Licence</label>
+ <select class="form-control" id="licence" formControlName="licence">
+ <option></option>
+ <option *ngFor="let licence of videoLicences" [value]="licence.id">{{ licence.label }}</option>
+ </select>
+
+ <div *ngIf="formErrors.licence" class="alert alert-danger">
+ {{ formErrors.licence }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="language">Language</label>
+ <select class="form-control" id="language" formControlName="language">
+ <option></option>
+ <option *ngFor="let language of videoLanguages" [value]="language.id">{{ language.label }}</option>
+ </select>
+
+ <div *ngIf="formErrors.language" class="alert alert-danger">
+ {{ formErrors.language }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="tags">Tags</label> <span class="little-information">(press enter to add the tag)</span>
+ <input
+ type="text" class="form-control" id="currentTag"
+ formControlName="currentTag" (keyup)="onTagKeyPress($event)"
+ >
+ <div *ngIf="formErrors.currentTag" class="alert alert-danger">
+ {{ formErrors.currentTag }}
+ </div>
+ </div>
+
+ <div class="tags">
+ <div class="label label-primary tag" *ngFor="let tag of tags">
+ {{ tag }}
+ <span class="remove" (click)="removeTag(tag)">x</span>
+ </div>
+ </div>
+
+ <div *ngIf="tagsError" class="alert alert-danger">
+ {{ tagsError }}
+ </div>
+
+ <div class="form-group">
+ <label for="description">Description</label>
+ <textarea
+ id="description" class="form-control" placeholder="Description..."
+ formControlName="description"
+ >
+ </textarea>
+ <div *ngIf="formErrors.description" class="alert alert-danger">
+ {{ formErrors.description }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <input
+ type="button" value="Update" class="btn btn-default form-control"
+ (click)="update()"
+ >
+ </div>
+</form>
--- /dev/null
+import { Component, ElementRef, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { FileUploader } from 'ng2-file-upload/ng2-file-upload';
+import { NotificationsService } from 'angular2-notifications';
+
+import { AuthService } from '../../core';
+import {
+ FormReactive,
+ VIDEO_NAME,
+ VIDEO_CATEGORY,
+ VIDEO_LICENCE,
+ VIDEO_LANGUAGE,
+ VIDEO_DESCRIPTION,
+ VIDEO_TAGS
+} from '../../shared';
+import { Video, VideoService } from '../shared';
+
+@Component({
+ selector: 'my-videos-update',
+ styleUrls: [ './video-edit.component.scss' ],
+ templateUrl: './video-update.component.html'
+})
+
+export class VideoUpdateComponent extends FormReactive implements OnInit {
+ tags: string[] = [];
+ videoCategories = [];
+ videoLicences = [];
+ videoLanguages = [];
+ video: Video;
+
+ error: string = null;
+ form: FormGroup;
+ formErrors = {
+ name: '',
+ category: '',
+ licence: '',
+ language: '',
+ description: '',
+ currentTag: ''
+ };
+ validationMessages = {
+ name: VIDEO_NAME.MESSAGES,
+ category: VIDEO_CATEGORY.MESSAGES,
+ licence: VIDEO_LICENCE.MESSAGES,
+ language: VIDEO_LANGUAGE.MESSAGES,
+ description: VIDEO_DESCRIPTION.MESSAGES,
+ currentTag: VIDEO_TAGS.MESSAGES
+ };
+
+ // Special error messages
+ tagsError = '';
+ fileError = '';
+
+ constructor(
+ private authService: AuthService,
+ private elementRef: ElementRef,
+ private formBuilder: FormBuilder,
+ private route: ActivatedRoute,
+ private router: Router,
+ private notificationsService: NotificationsService,
+ private videoService: VideoService
+ ) {
+ super();
+ }
+
+ buildForm() {
+ this.form = this.formBuilder.group({
+ name: [ '', VIDEO_NAME.VALIDATORS ],
+ nsfw: [ false ],
+ category: [ '', VIDEO_CATEGORY.VALIDATORS ],
+ licence: [ '', VIDEO_LICENCE.VALIDATORS ],
+ language: [ '', VIDEO_LANGUAGE.VALIDATORS ],
+ description: [ '', VIDEO_DESCRIPTION.VALIDATORS ],
+ currentTag: [ '', VIDEO_TAGS.VALIDATORS ]
+ });
+
+ this.form.valueChanges.subscribe(data => this.onValueChanged(data));
+ }
+
+ ngOnInit() {
+ this.buildForm();
+
+ this.videoCategories = this.videoService.videoCategories;
+ this.videoLicences = this.videoService.videoLicences;
+ this.videoLanguages = this.videoService.videoLanguages;
+
+ const id = this.route.snapshot.params['id'];
+ this.videoService.getVideo(id)
+ .subscribe(
+ video => {
+ this.video = video;
+
+ this.hydrateFormFromVideo();
+ },
+
+ err => this.error = 'Cannot fetch video.'
+ );
+ }
+
+ checkForm() {
+ this.forceCheck();
+
+ return this.form.valid === true && this.tagsError === '' && this.fileError === '';
+ }
+
+
+ onTagKeyPress(event: KeyboardEvent) {
+ // Enter press
+ if (event.keyCode === 13) {
+ this.addTagIfPossible();
+ }
+ }
+
+ removeTag(tag: string) {
+ this.tags.splice(this.tags.indexOf(tag), 1);
+ this.form.get('currentTag').enable();
+ }
+
+ update() {
+ // Maybe the user forgot to press "enter" when he filled the field
+ this.addTagIfPossible();
+
+ if (this.checkForm() === false) {
+ return;
+ }
+
+ this.video.patch(this.form.value);
+
+ this.videoService.updateVideo(this.video)
+ .subscribe(
+ () => {
+ this.notificationsService.success('Success', 'Video updated.');
+ this.router.navigate([ '/videos/watch', this.video.id ]);
+ },
+
+ err => {
+ this.error = 'Cannot update the video.';
+ console.error(err);
+ }
+ );
+
+ }
+
+ private addTagIfPossible() {
+ const currentTag = this.form.value['currentTag'];
+ if (currentTag === undefined) return;
+
+ // Check if the tag is valid and does not already exist
+ if (
+ currentTag.length >= 2 &&
+ this.form.controls['currentTag'].valid &&
+ this.tags.indexOf(currentTag) === -1
+ ) {
+ this.tags.push(currentTag);
+ this.form.patchValue({ currentTag: '' });
+
+ if (this.tags.length >= 3) {
+ this.form.get('currentTag').disable();
+ }
+
+ this.tagsError = '';
+ }
+ }
+
+ private hydrateFormFromVideo() {
+ this.form.patchValue(this.video.toJSON());
+ }
+}
</button>
<ul dropdownMenu id="more-menu" role="menu" aria-labelledby="single-button">
+ <li *ngIf="canUserUpdateVideo()" role="menuitem">
+ <a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/edit', video.id ]">
+ <span class="glyphicon glyphicon-pencil"></span> Update
+ </a>
+ </li>
+
<li role="menuitem">
<a class="dropdown-item" title="Get magnet URI" href="#" (click)="showMagnetUriModal($event)">
<span class="glyphicon glyphicon-magnet"></span> Magnet
return this.authService.isLoggedIn();
}
+ canUserUpdateVideo() {
+ return this.authService.getUser() !== null &&
+ this.authService.getUser().username === this.video.author;
+ }
+
private checkUserRating() {
// Unlogged users do not have ratings
if (this.isUserLoggedIn() === false) return;
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
-import { VideoAddComponent } from './video-add';
+import { VideoAddComponent, VideoUpdateComponent } from './video-edit';
import { VideoListComponent } from './video-list';
import { VideosComponent } from './videos.component';
import { VideoWatchComponent } from './video-watch';
}
}
},
+ {
+ path: 'edit/:id',
+ component: VideoUpdateComponent,
+ data: {
+ meta: {
+ title: 'Edit a video'
+ }
+ }
+ },
{
path: ':id',
redirectTo: 'watch/:id'
import { VideosRoutingModule } from './videos-routing.module';
import { VideosComponent } from './videos.component';
-import { VideoAddComponent } from './video-add';
+import { VideoAddComponent, VideoUpdateComponent } from './video-edit';
import { VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list';
import {
VideoWatchComponent,
VideosComponent,
VideoAddComponent,
+ VideoUpdateComponent,
VideoListComponent,
VideoMiniatureComponent,