Client: add basic support to report video abuses

This commit is contained in:
Chocobozzz 2017-01-23 22:16:48 +01:00
parent 4f8c0eb0e9
commit 11ac88de40
21 changed files with 185 additions and 15 deletions

View File

@ -37,10 +37,6 @@ Prototype of a decentralized video streaming platform using P2P (BitTorrent) dir
<img src="https://david-dm.org/Chocobozzz/PeerTube/dev-status.svg" alt="devDependency Status" /> <img src="https://david-dm.org/Chocobozzz/PeerTube/dev-status.svg" alt="devDependency Status" />
</a> </a>
<a href="https://codeclimate.com/github/Chocobozzz/PeerTube">
<img src="https://codeclimate.com/github/Chocobozzz/PeerTube/badges/gpa.svg" alt="Code climate" />
</a>
<a href="http://standardjs.com/"> <a href="http://standardjs.com/">
<img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg" alt="JavaScript Style Guide" /> <img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg" alt="JavaScript Style Guide" />
</a> </a>

View File

@ -5,6 +5,7 @@ import { AdminComponent } from './admin.component';
import { FriendsRoutes } from './friends'; import { FriendsRoutes } from './friends';
import { RequestsRoutes } from './requests'; import { RequestsRoutes } from './requests';
import { UsersRoutes } from './users'; import { UsersRoutes } from './users';
import { VideoAbusesRoutes } from './video-abuses';
const adminRoutes: Routes = [ const adminRoutes: Routes = [
{ {
@ -18,7 +19,8 @@ const adminRoutes: Routes = [
}, },
...FriendsRoutes, ...FriendsRoutes,
...RequestsRoutes, ...RequestsRoutes,
...UsersRoutes ...UsersRoutes,
...VideoAbusesRoutes
] ]
} }
]; ];

View File

@ -5,6 +5,7 @@ import { AdminRoutingModule } from './admin-routing.module';
import { FriendsComponent, FriendAddComponent, FriendListComponent, FriendService } from './friends'; import { FriendsComponent, FriendAddComponent, FriendListComponent, FriendService } from './friends';
import { RequestsComponent, RequestStatsComponent, RequestService } from './requests'; import { RequestsComponent, RequestStatsComponent, RequestService } from './requests';
import { UsersComponent, UserAddComponent, UserListComponent, UserService } from './users'; import { UsersComponent, UserAddComponent, UserListComponent, UserService } from './users';
import { VideoAbusesComponent, VideoAbuseListComponent } from './video-abuses';
import { MenuAdminComponent } from './menu-admin.component'; import { MenuAdminComponent } from './menu-admin.component';
import { SharedModule } from '../shared'; import { SharedModule } from '../shared';
@ -28,6 +29,9 @@ import { SharedModule } from '../shared';
UserAddComponent, UserAddComponent,
UserListComponent, UserListComponent,
VideoAbusesComponent,
VideoAbuseListComponent,
MenuAdminComponent MenuAdminComponent
], ],

View File

@ -15,6 +15,11 @@
<span class="hidden-xs glyphicon glyphicon-stats"></span> <span class="hidden-xs glyphicon glyphicon-stats"></span>
<a [routerLink]="['/admin/requests/stats']">Request stats</a> <a [routerLink]="['/admin/requests/stats']">Request stats</a>
</div> </div>
<div id="panel-video-abuses" class="panel-button">
<span class="hidden-xs glyphicon glyphicon-alert"></span>
<a [routerLink]="['/admin/video-abuses/list']">Video abuses</a>
</div>
</div> </div>
<div class="panel-block"> <div class="panel-block">

View File

@ -0,0 +1,3 @@
export * from './video-abuse-list';
export * from './video-abuses.component';
export * from './video-abuses.routes';

View File

@ -0,0 +1 @@
export * from './video-abuse-list.component';

View File

@ -0,0 +1,27 @@
<h3>Video abuses list</h3>
<table class="table table-hover">
<thead>
<tr>
<th class="cell-id">ID</th>
<th class="cell-reason">Reason</th>
<th>Reporter pod host</th>
<th>Reporter username</th>
<th>Video</th>
<th>Created at</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let videoAbuse of videoAbuses">
<td>{{ videoAbuse.id }}</td>
<td>{{ videoAbuse.reason }}</td>
<td>{{ videoAbuse.reporterPodHost }}</td>
<td>{{ videoAbuse.reporterUsername }}</td>
<td>
<a [routerLink]="buildVideoLink(videoAbuse)" title="Go to video">{{ videoAbuse.videoId }}</a>
</td>
<td>{{ videoAbuse.createdAt | date: 'medium' }}</td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,7 @@
.cell-id {
width: 40px;
}
.cell-reason {
width: 200px;
}

View File

@ -0,0 +1,31 @@
import { setInterval } from 'timers'
import { Component, OnInit } from '@angular/core';
import { VideoAbuseService, VideoAbuse} from '../../../shared';
@Component({
selector: 'my-video-abuse-list',
templateUrl: './video-abuse-list.component.html',
styleUrls: [ './video-abuse-list.component.scss' ]
})
export class VideoAbuseListComponent implements OnInit {
videoAbuses: VideoAbuse[];
constructor(private videoAbuseService: VideoAbuseService) { }
ngOnInit() {
this.getVideoAbuses();
}
buildVideoLink(videoAbuse: VideoAbuse) {
return `/videos/${videoAbuse.videoId}`;
}
private getVideoAbuses() {
this.videoAbuseService.getVideoAbuses().subscribe(
res => this.videoAbuses = res.videoAbuses,
err => alert(err.text)
);
}
}

View File

@ -0,0 +1,8 @@
import { Component } from '@angular/core';
@Component({
template: '<router-outlet></router-outlet>'
})
export class VideoAbusesComponent {
}

View File

@ -0,0 +1,28 @@
import { Routes } from '@angular/router';
import { VideoAbusesComponent } from './video-abuses.component';
import { VideoAbuseListComponent } from './video-abuse-list';
export const VideoAbusesRoutes: Routes = [
{
path: 'video-abuses',
component: VideoAbusesComponent
,
children: [
{
path: '',
redirectTo: 'list',
pathMatch: 'full'
},
{
path: 'list',
component: VideoAbuseListComponent,
data: {
meta: {
titleSuffix: ' - Video abuses list'
}
}
}
]
}
];

View File

@ -1,4 +1,4 @@
export * from './host.validator'; export * from './host.validator';
export * from './user'; export * from './user';
export * from './video-report'; export * from './video-abuse';
export * from './video'; export * from './video';

View File

@ -1,6 +1,6 @@
import { Validators } from '@angular/forms'; import { Validators } from '@angular/forms';
export const VIDEO_REPORT_REASON = { export const VIDEO_ABUSE_REASON = {
VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(300) ], VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(300) ],
MESSAGES: { MESSAGES: {
'required': 'Report reason name is required.', 'required': 'Report reason name is required.',

View File

@ -3,4 +3,5 @@ export * from './forms';
export * from './rest'; export * from './rest';
export * from './search'; export * from './search';
export * from './users'; export * from './users';
export * from './video-abuse';
export * from './shared.module'; export * from './shared.module';

View File

@ -14,6 +14,7 @@ import { FileUploadModule } from 'ng2-file-upload/ng2-file-upload';
import { AUTH_HTTP_PROVIDERS } from './auth'; import { AUTH_HTTP_PROVIDERS } from './auth';
import { RestExtractor, RestService } from './rest'; import { RestExtractor, RestService } from './rest';
import { SearchComponent, SearchService } from './search'; import { SearchComponent, SearchService } from './search';
import { VideoAbuseService } from './video-abuse';
@NgModule({ @NgModule({
imports: [ imports: [
@ -57,7 +58,8 @@ import { SearchComponent, SearchService } from './search';
AUTH_HTTP_PROVIDERS, AUTH_HTTP_PROVIDERS,
RestExtractor, RestExtractor,
RestService, RestService,
SearchService SearchService,
VideoAbuseService
] ]
}) })
export class SharedModule { } export class SharedModule { }

View File

@ -0,0 +1,2 @@
export * from './video-abuse.service';
export * from './video-abuse.model';

View File

@ -0,0 +1,8 @@
export interface VideoAbuse {
id: string;
reason: string;
reporterPodHost: string;
reporterUsername: string;
videoId: string;
createdAt: Date;
}

View File

@ -0,0 +1,44 @@
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { AuthService } from '../core';
import { AuthHttp } from '../auth';
import { RestExtractor, ResultList } from '../rest';
import { VideoAbuse } from './video-abuse.model';
@Injectable()
export class VideoAbuseService {
private static BASE_VIDEO_ABUSE_URL = '/api/v1/videos/';
constructor(
private authHttp: AuthHttp,
private restExtractor: RestExtractor
) {}
getVideoAbuses() {
return this.authHttp.get(VideoAbuseService.BASE_VIDEO_ABUSE_URL + 'abuse')
.map(this.restExtractor.extractDataList)
.map(this.extractVideoAbuses)
}
reportVideo(id: string, reason: string) {
const body = {
reason
};
const url = VideoAbuseService.BASE_VIDEO_ABUSE_URL + id + '/abuse';
return this.authHttp.post(url, body)
.map(this.restExtractor.extractDataBool)
.catch((res) => this.restExtractor.handleError(res));
}
private extractVideoAbuses(result: ResultList) {
const videoAbuses: VideoAbuse[] = result.data;
const totalVideoAbuses = result.total;
return { videoAbuses, totalVideoAbuses };
}
}

View File

@ -3,7 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { ModalDirective } from 'ng2-bootstrap/modal'; import { ModalDirective } from 'ng2-bootstrap/modal';
import { FormReactive, VIDEO_REPORT_REASON } from '../../shared'; import { FormReactive, VideoAbuseService, VIDEO_ABUSE_REASON } from '../../shared';
import { Video, VideoService } from '../shared'; import { Video, VideoService } from '../shared';
@Component({ @Component({
@ -21,12 +21,12 @@ export class VideoReportComponent extends FormReactive implements OnInit {
reason: '' reason: ''
}; };
validationMessages = { validationMessages = {
reason: VIDEO_REPORT_REASON.MESSAGES reason: VIDEO_ABUSE_REASON.MESSAGES
}; };
constructor( constructor(
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private videoService: VideoService private videoAbuseService: VideoAbuseService
) { ) {
super(); super();
} }
@ -37,7 +37,7 @@ export class VideoReportComponent extends FormReactive implements OnInit {
buildForm() { buildForm() {
this.form = this.formBuilder.group({ this.form = this.formBuilder.group({
reason: [ '', VIDEO_REPORT_REASON.VALIDATORS ] reason: [ '', VIDEO_ABUSE_REASON.VALIDATORS ]
}); });
this.form.valueChanges.subscribe(data => this.onValueChanged(data)); this.form.valueChanges.subscribe(data => this.onValueChanged(data));
@ -54,7 +54,7 @@ export class VideoReportComponent extends FormReactive implements OnInit {
report() { report() {
const reason = this.form.value['reason'] const reason = this.form.value['reason']
this.videoService.reportVideo(this.video.id, reason) this.videoAbuseService.reportVideo(this.video.id, reason)
.subscribe( .subscribe(
// TODO: move alert to beautiful notifications // TODO: move alert to beautiful notifications
ok => { ok => {

View File

@ -70,7 +70,7 @@ function init (silent, callback) {
} }
}) })
if (!silent) logger.info('Database is ready.') if (!silent) logger.info('Database %s is ready.', dbname)
return callback(null) return callback(null)
}) })

View File

@ -106,7 +106,8 @@ function toFormatedJSON () {
reporterPodHost, reporterPodHost,
reason: this.reason, reason: this.reason,
reporterUsername: this.reporterUsername, reporterUsername: this.reporterUsername,
videoId: this.videoId videoId: this.videoId,
createdAt: this.createdAt
} }
return json return json