From f595d3947708114deeed4312cc5ffd285745b090 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 8 Dec 2017 17:31:21 +0100 Subject: [PATCH] Finish admin design --- .../following-list.component.html | 2 +- .../jobs/jobs-list/jobs-list.component.html | 2 +- .../video-abuse-list.component.html | 41 ++++++++--------- .../video-abuse-list.component.scss | 6 +++ .../video-abuse-list.component.ts | 3 +- .../video-blacklist-list.component.html | 2 +- client/src/app/core/auth/auth.service.ts | 22 ++++----- .../app/shared/video/abstract-video-list.ts | 6 ++- .../shared/video-description.component.html | 2 +- .../shared/video-description.component.scss | 17 +++++++ .../shared/video-description.component.ts | 2 + client/src/sass/application.scss | 21 +++------ server/controllers/api/videos/index.ts | 8 ++-- .../custom-validators/activitypub/videos.ts | 6 +-- server/helpers/custom-validators/videos.ts | 9 ++-- server/lib/activitypub/process/misc.ts | 21 +++++++-- server/models/video/video.ts | 26 +++++++---- server/tests/api/multiple-servers.ts | 46 +++++++++++++++++++ server/tests/api/single-server.ts | 37 --------------- 19 files changed, 164 insertions(+), 115 deletions(-) create mode 100644 client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html index 3e70b418c..2b6cc9113 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.html +++ b/client/src/app/+admin/follows/following-list/following-list.component.html @@ -6,7 +6,7 @@ - + diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html index 29103c06b..7aa5f4254 100644 --- a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html +++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html @@ -8,7 +8,7 @@ > - +
{{ job.handlerInputData }}
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html index ab0a9d99f..d655a5e9b 100644 --- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html +++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html @@ -1,24 +1,19 @@ -
-
- -

Video abuses list

- - - - - - - - - - {{ videoAbuse.videoId }} - - - - - -
+
+
Video abuses list
+ + + + + + + + + + {{ videoAbuse.videoName }} + + + diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss new file mode 100644 index 000000000..6a4762650 --- /dev/null +++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss @@ -0,0 +1,6 @@ +/deep/ a { + + &, &:hover, &:active, &:focus { + color: #000; + } +} diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts index 654603d01..b4d3bbd24 100644 --- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts +++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts @@ -8,7 +8,8 @@ import { VideoAbuse } from '../../../../../../shared' @Component({ selector: 'my-video-abuse-list', - templateUrl: './video-abuse-list.component.html' + templateUrl: './video-abuse-list.component.html', + styleUrls: [ './video-abuse-list.component.scss'] }) export class VideoAbuseListComponent extends RestTable implements OnInit { videoAbuses: VideoAbuse[] = [] diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html index 05d116798..1d813fa07 100644 --- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html +++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html @@ -18,7 +18,7 @@ - + diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 0db197f02..e887dde1f 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -169,19 +169,15 @@ export class AuthService { return this.http.post(AuthService.BASE_TOKEN_URL, body, { headers }) .map(res => this.handleRefreshToken(res)) - .catch(res => { - // The refresh token is invalid? - if (res.status === 400 && res.error.error === 'invalid_grant') { - console.error('Cannot refresh token -> logout...') - this.logout() - this.router.navigate(['/login']) - - return Observable.throw({ - error: 'You need to reconnect.' - }) - } - - return this.restExtractor.handleError(res) + .catch(err => { + console.error(err) + console.log('Cannot refresh token -> logout...') + this.logout() + this.router.navigate(['/login']) + + return Observable.throw({ + error: 'You need to reconnect.' + }) }) } diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts index ee1ed2cb2..ba1635a18 100644 --- a/client/src/app/shared/video/abstract-video-list.ts +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -62,7 +62,7 @@ export abstract class AbstractVideoList implements OnInit { observable.subscribe( ({ videos, totalVideos }) => { // Paging is too high, return to the first one - if (totalVideos <= ((this.pagination.currentPage - 1) * this.pagination.itemsPerPage)) { + if (this.pagination.currentPage > 1 && totalVideos <= ((this.pagination.currentPage - 1) * this.pagination.itemsPerPage)) { this.pagination.currentPage = 1 this.setNewRouteParams() return this.reloadVideos() @@ -82,6 +82,10 @@ export abstract class AbstractVideoList implements OnInit { } protected hasMoreVideos () { + // No results + if (this.pagination.totalItems === 0) return false + + // Not loaded yet if (!this.pagination.totalItems) return true const maxPage = this.pagination.totalItems / this.pagination.itemsPerPage diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.html b/client/src/app/videos/+video-edit/shared/video-description.component.html index da66a9753..5d05467be 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.html +++ b/client/src/app/videos/+video-edit/shared/video-description.component.html @@ -1,6 +1,6 @@ diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.scss b/client/src/app/videos/+video-edit/shared/video-description.component.scss index 8155cbca7..2a4c8d189 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-description.component.scss @@ -4,4 +4,21 @@ textarea { padding: 5px 15px; font-size: 15px; height: 150px; + margin-bottom: 15px; } + +/deep/ { + .nav-link { + display: flex !important; + align-items: center; + height: 30px !important; + padding: 0 15px !important; + } + + .tab-content { + min-height: 75px; + padding: 15px; + font-size: 15px; + } +} + diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.ts b/client/src/app/videos/+video-edit/shared/video-description.component.ts index 8dfb74b2a..9b77a27e6 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-description.component.ts @@ -60,6 +60,8 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { } private updateDescriptionPreviews () { + if (!this.description) return + this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 })) this.descriptionHTML = this.markdownService.markdownToHTML(this.description) } diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index ecbb8dac5..5a4aa4cd9 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -165,7 +165,7 @@ p-datatable { td { border: 1px solid #E5E5E5 !important; - padding: 15px; + padding-left: 15px !important; } tr { @@ -185,6 +185,10 @@ p-datatable { &:first-child td { border-top: none !important; } + + &:last-child td { + border-bottom: none !important; + } } th { @@ -198,6 +202,7 @@ p-datatable { &.ui-state-active, &.ui-sortable-column:hover { background-color: #f0f0f0 !important; border: 1px solid #f0f0f0 !important; + border-width: 0 1px !important; } } @@ -208,17 +213,10 @@ p-datatable { } p-paginator { - overflow: hidden; - display: block; - padding-top: 2px; - border: 1px solid #f0f0f0 !important; - border-top: none !important; - .ui-paginator-bottom { position: relative; border: none !important; - border-top: 1px solid #f0f0f0 !important; - box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.16); + border: 1px solid #f0f0f0 !important; height: 40px; display: flex; justify-content: center; @@ -298,11 +296,6 @@ p-datatable { font-weight: $font-semibold !important; } } - - .tab-content { - min-height: 75px; - padding: 15px; - } } diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 0f71a7f7f..63de662a7 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -280,7 +280,7 @@ async function updateVideo (req: express.Request, res: express.Response) { if (videoInfoToUpdate.licence !== undefined) videoInstance.set('licence', videoInfoToUpdate.licence) if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language) if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw) - if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', videoInfoToUpdate.privacy) + if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', parseInt(videoInfoToUpdate.privacy.toString(), 10)) if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description) const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) @@ -298,9 +298,9 @@ async function updateVideo (req: express.Request, res: express.Response) { } // Video is not private anymore, send a create action to remote servers - if (wasPrivateVideo === true && videoInstance.privacy !== VideoPrivacy.PRIVATE) { - await sendAddVideo(videoInstance, t) - await shareVideoByServer(videoInstance, t) + if (wasPrivateVideo === true && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE) { + await sendAddVideo(videoInstanceUpdated, t) + await shareVideoByServer(videoInstanceUpdated, t) } }) diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts index 12c672fd2..2ed2988f5 100644 --- a/server/helpers/custom-validators/activitypub/videos.ts +++ b/server/helpers/custom-validators/activitypub/videos.ts @@ -49,14 +49,14 @@ function isVideoTorrentObjectValid (video: any) { isActivityPubVideoDurationValid(video.duration) && isUUIDValid(video.uuid) && setValidRemoteTags(video) && - isRemoteIdentifierValid(video.category) && - isRemoteIdentifierValid(video.licence) && + (!video.category || isRemoteIdentifierValid(video.category)) && + (!video.licence || isRemoteIdentifierValid(video.licence)) && (!video.language || isRemoteIdentifierValid(video.language)) && isVideoViewsValid(video.views) && isVideoNSFWValid(video.nsfw) && isDateValid(video.published) && isDateValid(video.updated) && - isRemoteVideoContentValid(video.mediaType, video.content) && + (!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) && isRemoteVideoIconValid(video.icon) && setValidRemoteVideoUrls(video) && video.url.length !== 0 diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts index f13178c54..4fc460699 100644 --- a/server/helpers/custom-validators/videos.ts +++ b/server/helpers/custom-validators/videos.ts @@ -9,16 +9,17 @@ import { VIDEO_PRIVACIES } from '../../initializers/constants' import { database as db } from '../../initializers/database' import { VideoInstance } from '../../models/video/video-interface' import { exists, isArray } from './misc' +import isInt = require('validator/lib/isInt') const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES function isVideoCategoryValid (value: number) { - return VIDEO_CATEGORIES[value] !== undefined + return value === null || VIDEO_CATEGORIES[value] !== undefined } function isVideoLicenceValid (value: number) { - return VIDEO_LICENCES[value] !== undefined + return value === null || VIDEO_LICENCES[value] !== undefined } function isVideoLanguageValid (value: number) { @@ -38,7 +39,7 @@ function isVideoTruncatedDescriptionValid (value: string) { } function isVideoDescriptionValid (value: string) { - return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION) + return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)) } function isVideoNameValid (value: string) { @@ -84,7 +85,7 @@ function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | } function isVideoPrivacyValid (value: string) { - return VIDEO_PRIVACIES[value] !== undefined + return validator.isInt(value + '') && VIDEO_PRIVACIES[value] !== undefined } function isVideoFileInfoHashValid (value: string) { diff --git a/server/lib/activitypub/process/misc.ts b/server/lib/activitypub/process/misc.ts index f20e588ab..0baa22c26 100644 --- a/server/lib/activitypub/process/misc.ts +++ b/server/lib/activitypub/process/misc.ts @@ -41,15 +41,30 @@ async function videoActivityObjectToDBAttributes ( language = parseInt(videoObject.language.identifier, 10) } + let category = null + if (videoObject.category) { + category = parseInt(videoObject.category.identifier, 10) + } + + let licence = null + if (videoObject.licence) { + licence = parseInt(videoObject.licence.identifier, 10) + } + + let description = null + if (videoObject.content) { + description = videoObject.content + } + const videoData: VideoAttributes = { name: videoObject.name, uuid: videoObject.uuid, url: videoObject.id, - category: parseInt(videoObject.category.identifier, 10), - licence: parseInt(videoObject.licence.identifier, 10), + category, + licence, language, + description, nsfw: videoObject.nsfw, - description: videoObject.content, channelId: videoChannel.id, duration: parseInt(duration, 10), createdAt: new Date(videoObject.published), diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 8b1eb1f96..d46fdeebe 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -564,6 +564,22 @@ toActivityPubObject = function (this: VideoInstance) { } } + let category + if (this.category) { + category = { + identifier: this.category + '', + name: this.getCategoryLabel() + } + } + + let licence + if (this.licence) { + licence = { + identifier: this.licence + '', + name: this.getLicenceLabel() + } + } + let likesObject let dislikesObject @@ -635,14 +651,8 @@ toActivityPubObject = function (this: VideoInstance) { duration: 'PT' + this.duration + 'S', uuid: this.uuid, tag, - category: { - identifier: this.category + '', - name: this.getCategoryLabel() - }, - licence: { - identifier: this.licence + '', - name: this.getLicenceLabel() - }, + category, + licence, language, views: this.views, nsfw: this.nsfw, diff --git a/server/tests/api/multiple-servers.ts b/server/tests/api/multiple-servers.ts index c7f19f261..c9f74cc8c 100644 --- a/server/tests/api/multiple-servers.ts +++ b/server/tests/api/multiple-servers.ts @@ -2,6 +2,8 @@ import 'mocha' import * as chai from 'chai' +import { join } from "path" +import * as request from 'supertest' import { dateIsValid, @@ -707,6 +709,50 @@ describe('Test multiple servers', function () { }) }) + describe('With minimum parameters', function () { + it('Should upload and propagate the video', async function () { + this.timeout(50000) + + const path = '/api/v1/videos/upload' + + const req = request(servers[1].url) + .post(path) + .set('Accept', 'application/json') + .set('Authorization', 'Bearer ' + servers[1].accessToken) + .field('name', 'minimum parameters') + .field('privacy', '1') + .field('nsfw', 'false') + .field('channelId', '1') + + const filePath = join(__dirname, '..', 'api', 'fixtures', 'video_short.webm') + + await req.attach('videofile', filePath) + .expect(200) + + await wait(25000) + + for (const server of servers) { + const res = await getVideosList(server.url) + const video = res.body.data.find(v => v.name === 'minimum parameters') + + expect(video.name).to.equal('minimum parameters') + expect(video.category).to.equal(null) + expect(video.categoryLabel).to.equal('Misc') + expect(video.licence).to.equal(null) + expect(video.licenceLabel).to.equal('Unknown') + expect(video.language).to.equal(null) + expect(video.languageLabel).to.equal('Unknown') + expect(video.nsfw).to.not.be.ok + expect(video.description).to.equal(null) + expect(video.serverHost).to.equal('localhost:9002') + expect(video.accountName).to.equal('root') + expect(video.tags).to.deep.equal([ ]) + expect(dateIsValid(video.createdAt)).to.be.true + expect(dateIsValid(video.updatedAt)).to.be.true + } + }) + }) + after(async function () { killallServers(servers) diff --git a/server/tests/api/single-server.ts b/server/tests/api/single-server.ts index e99955ef4..91460c7ae 100644 --- a/server/tests/api/single-server.ts +++ b/server/tests/api/single-server.ts @@ -694,43 +694,6 @@ describe('Test a single server', function () { expect(video.dislikes).to.equal(1) }) - it('Should upload a video with minimum parameters', async function () { - const path = '/api/v1/videos/upload' - - const req = request(server.url) - .post(path) - .set('Accept', 'application/json') - .set('Authorization', 'Bearer ' + server.accessToken) - .field('name', 'minimum parameters') - .field('privacy', '1') - .field('nsfw', 'false') - .field('channelId', '1') - - const filePath = join(__dirname, '..', 'api', 'fixtures', 'video_short.webm') - - await req.attach('videofile', filePath) - .expect(200) - - const res = await getVideosList(server.url) - const video = res.body.data.find(v => v.name === 'minimum parameters') - - expect(video.name).to.equal('minimum parameters') - expect(video.category).to.equal(null) - expect(video.categoryLabel).to.equal('Misc') - expect(video.licence).to.equal(null) - expect(video.licenceLabel).to.equal('Unknown') - expect(video.language).to.equal(null) - expect(video.languageLabel).to.equal('Unknown') - expect(video.nsfw).to.not.be.ok - expect(video.description).to.equal(null) - expect(video.serverHost).to.equal('localhost:9001') - expect(video.accountName).to.equal('root') - expect(video.isLocal).to.be.true - expect(video.tags).to.deep.equal([ ]) - expect(dateIsValid(video.createdAt)).to.be.true - expect(dateIsValid(video.updatedAt)).to.be.true - }) - after(async function () { killallServers([ server ]) -- 2.25.1