1
0
Fork 0

Merge branch 'release/v1.3.0' into develop

This commit is contained in:
Chocobozzz 2019-06-07 15:04:41 +02:00
commit b91bc1d1f3
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
15 changed files with 122 additions and 65 deletions

View File

@ -75,22 +75,22 @@ menu {
.logged-in-more { .logged-in-more {
margin-right: 20px; margin-right: 20px;
my-global-icon { my-global-icon.dropdown-toggle {
@include apply-svg-color(var(--mainBackgroundColor));
cursor: pointer; cursor: pointer;
&::after { &::after {
border: none; border: none;
} }
/deep/ {
@include apply-svg-color(var(--menuForegroundColor));
}
} }
.dropdown-item { .dropdown-item {
@include dropdown-with-icon-item; @include dropdown-with-icon-item;
my-global-icon { my-global-icon {
@include apply-svg-color(var(--mainForegroundColor));
width: 22px; width: 22px;
height: 22px; height: 22px;

View File

@ -32,6 +32,10 @@
.more-icon { .more-icon {
width: 21px; width: 21px;
/deep/ {
@include apply-svg-color(var(--mainForegroundColor));
}
} }
&.small { &.small {

View File

@ -41,10 +41,4 @@ export class ActionDropdownComponent<T> {
areActionsDisplayed (actions: DropdownAction<T>[], entry: T) { areActionsDisplayed (actions: DropdownAction<T>[], entry: T) {
return actions.some(a => a.isDisplayed === undefined || a.isDisplayed(entry)) return actions.some(a => a.isDisplayed === undefined || a.isDisplayed(entry))
} }
handleClick (event: Event, action: DropdownAction<T>) {
event.preventDefault()
// action.handler(entry)
}
} }

View File

@ -57,7 +57,7 @@ export abstract class FormReactive {
// Don't care if dirty on force check // Don't care if dirty on force check
const isDirty = control.dirty || forceCheck === true const isDirty = control.dirty || forceCheck === true
if (control && isDirty && !control.valid) { if (control && isDirty && control.enabled && !control.valid) {
const messages = validationMessages[ field ] const messages = validationMessages[ field ]
for (const key of Object.keys(control.errors)) { for (const key of Object.keys(control.errors)) {
formErrors[ field ] += messages[ key ] + ' ' formErrors[ field ] += messages[ key ] + ' '

View File

@ -3,6 +3,7 @@
table { table {
font-size: 14px; font-size: 14px;
color: var(--mainForegroundColor);
.label { .label {
font-weight: $font-semibold; font-weight: $font-semibold;
@ -16,4 +17,4 @@ table {
.glyphicon-remove { .glyphicon-remove {
color: $red; color: $red;
} }
} }

View File

@ -11,7 +11,7 @@ import {
styleUrls: ['./remote-subscribe.component.scss'] styleUrls: ['./remote-subscribe.component.scss']
}) })
export class RemoteSubscribeComponent extends FormReactive implements OnInit { export class RemoteSubscribeComponent extends FormReactive implements OnInit {
@Input() account: string @Input() uri: string
@Input() interact = false @Input() interact = false
@Input() showHelp = false @Input() showHelp = false
@ -42,19 +42,20 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit {
fetch(`https://${hostname}/.well-known/webfinger?resource=acct:${username}@${hostname}`) fetch(`https://${hostname}/.well-known/webfinger?resource=acct:${username}@${hostname}`)
.then(response => response.json()) .then(response => response.json())
.then(data => new Promise((resolve, reject) => { .then(data => new Promise((resolve, reject) => {
console.log(data)
if (data && Array.isArray(data.links)) { if (data && Array.isArray(data.links)) {
const link: { const link: { template: string } = data.links.find((link: any) => {
template: string return link && typeof link.template === 'string' && link.rel === 'http://ostatus.org/schema/1.0/subscribe'
} = data.links.find((link: any) => })
link && typeof link.template === 'string' && link.rel === 'http://ostatus.org/schema/1.0/subscribe')
if (link && link.template.includes('{uri}')) { if (link && link.template.includes('{uri}')) {
resolve(link.template.replace('{uri}', `acct:${this.account}`)) resolve(link.template.replace('{uri}', encodeURIComponent(this.uri)))
} }
} }
reject() reject()
})) }))
.then(window.open) .then(window.open)
.catch(() => window.open(`https://${hostname}/authorize_interaction?acct=${this.account}`)) .catch(err => console.error(err))
} }
} }

View File

@ -41,7 +41,7 @@
</button> </button>
<button class="dropdown-item" i18n>Subscribe with a Mastodon account:</button> <button class="dropdown-item" i18n>Subscribe with a Mastodon account:</button>
<my-remote-subscribe showHelp="true" account="{{ uriAccount }}"></my-remote-subscribe> <my-remote-subscribe showHelp="true" [uri]="channelUri"></my-remote-subscribe>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@ -50,4 +50,4 @@
</div> </div>
</div> </div>
</div> </div>

View File

@ -28,19 +28,19 @@ export class SubscribeButtonComponent implements OnInit {
private videoService: VideoService private videoService: VideoService
) { } ) { }
get uri () { get channelHandle () {
return this.videoChannel.name + '@' + this.videoChannel.host return this.videoChannel.name + '@' + this.videoChannel.host
} }
get uriAccount () { get channelUri () {
return this.videoChannel.ownerAccount.name + '@' + this.videoChannel.host return this.videoChannel.url
} }
ngOnInit () { ngOnInit () {
if (this.isUserLoggedIn()) { if (this.isUserLoggedIn()) {
this.userSubscriptionService.doesSubscriptionExist(this.uri) this.userSubscriptionService.doesSubscriptionExist(this.channelHandle)
.subscribe( .subscribe(
res => this.subscribed = res[this.uri], res => this.subscribed = res[this.channelHandle],
err => this.notifier.error(err.message) err => this.notifier.error(err.message)
) )
@ -56,7 +56,7 @@ export class SubscribeButtonComponent implements OnInit {
} }
localSubscribe () { localSubscribe () {
this.userSubscriptionService.addSubscription(this.uri) this.userSubscriptionService.addSubscription(this.channelHandle)
.subscribe( .subscribe(
() => { () => {
this.subscribed = true this.subscribed = true
@ -78,7 +78,7 @@ export class SubscribeButtonComponent implements OnInit {
} }
localUnsubscribe () { localUnsubscribe () {
this.userSubscriptionService.deleteSubscription(this.uri) this.userSubscriptionService.deleteSubscription(this.channelHandle)
.subscribe( .subscribe(
() => { () => {
this.subscribed = false this.subscribed = false

View File

@ -188,19 +188,16 @@ export class VideoActionsDropdownComponent implements OnChanges {
} }
private buildActions () { private buildActions () {
this.videoActions = [] this.videoActions = [
[
if (this.authService.isLoggedIn()) {
this.videoActions.push([
{ {
label: this.i18n('Save to playlist'), label: this.i18n('Save to playlist'),
handler: () => this.playlistDropdown.toggle(), handler: () => this.playlistDropdown.toggle(),
isDisplayed: () => this.displayOptions.playlist, isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.playlist,
iconName: 'playlist-add' iconName: 'playlist-add'
} }
]) ],
[
this.videoActions.push([
{ {
label: this.i18n('Download'), label: this.i18n('Download'),
handler: () => this.showDownloadModal(), handler: () => this.showDownloadModal(),
@ -211,36 +208,35 @@ export class VideoActionsDropdownComponent implements OnChanges {
label: this.i18n('Update'), label: this.i18n('Update'),
linkBuilder: ({ video }) => [ '/videos/update', video.uuid ], linkBuilder: ({ video }) => [ '/videos/update', video.uuid ],
iconName: 'edit', iconName: 'edit',
isDisplayed: () => this.displayOptions.update && this.isVideoUpdatable() isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.update && this.isVideoUpdatable()
}, },
{ {
label: this.i18n('Blacklist'), label: this.i18n('Blacklist'),
handler: () => this.showBlacklistModal(), handler: () => this.showBlacklistModal(),
iconName: 'no', iconName: 'no',
isDisplayed: () => this.displayOptions.blacklist && this.isVideoBlacklistable() isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoBlacklistable()
}, },
{ {
label: this.i18n('Unblacklist'), label: this.i18n('Unblacklist'),
handler: () => this.unblacklistVideo(), handler: () => this.unblacklistVideo(),
iconName: 'undo', iconName: 'undo',
isDisplayed: () => this.displayOptions.blacklist && this.isVideoUnblacklistable() isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoUnblacklistable()
}, },
{ {
label: this.i18n('Delete'), label: this.i18n('Delete'),
handler: () => this.removeVideo(), handler: () => this.removeVideo(),
isDisplayed: () => this.displayOptions.delete && this.isVideoRemovable(), isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.delete && this.isVideoRemovable(),
iconName: 'delete' iconName: 'delete'
} }
]) ],
[
this.videoActions.push([
{ {
label: this.i18n('Report'), label: this.i18n('Report'),
handler: () => this.showReportModal(), handler: () => this.showReportModal(),
isDisplayed: () => this.displayOptions.report, isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.report,
iconName: 'alert' iconName: 'alert'
} }
]) ]
} ]
} }
} }

View File

@ -193,7 +193,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
} }
private trackPrivacyChange () { private trackPrivacyChange () {
// We will update the "support" field depending on the channel // We will update the schedule input and the wait transcoding checkbox validators
this.form.controls[ 'privacy' ] this.form.controls[ 'privacy' ]
.valueChanges .valueChanges
.pipe(map(res => parseInt(res.toString(), 10))) .pipe(map(res => parseInt(res.toString(), 10)))

View File

@ -41,7 +41,7 @@
<span i18n> <span i18n>
If you have an account on Mastodon or Pleroma, you can open it directly in their interface: If you have an account on Mastodon or Pleroma, you can open it directly in their interface:
</span> </span>
<my-remote-subscribe [interact]="true" [account]="getUrl()"></my-remote-subscribe> <my-remote-subscribe [interact]="true" [uri]="getUri()"></my-remote-subscribe>
</div> </div>
<div class="modal-footer inputs"> <div class="modal-footer inputs">
<span i18n class="action-button action-button-cancel" role="button" (click)="hideVisitorModal()"> <span i18n class="action-button action-button-cancel" role="button" (click)="hideVisitorModal()">

View File

@ -8,7 +8,6 @@ import { User } from '../../../shared/users'
import { Video } from '../../../shared/video/video.model' import { Video } from '../../../shared/video/video.model'
import { VideoComment } from './video-comment.model' import { VideoComment } from './video-comment.model'
import { VideoCommentService } from './video-comment.service' import { VideoCommentService } from './video-comment.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
import { VideoCommentValidatorsService } from '@app/shared/forms/form-validators/video-comment-validators.service' import { VideoCommentValidatorsService } from '@app/shared/forms/form-validators/video-comment-validators.service'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@ -40,8 +39,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
private videoCommentService: VideoCommentService, private videoCommentService: VideoCommentService,
private authService: AuthService, private authService: AuthService,
private modalService: NgbModal, private modalService: NgbModal,
private router: Router, private router: Router
private i18n: I18n
) { ) {
super() super()
} }
@ -124,7 +122,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
return this.form.value['text'] return this.form.value['text']
} }
getUrl () { getUri () {
return window.location.href return window.location.href
} }

View File

@ -26,6 +26,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail' import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail'
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
import { ThumbnailModel } from '../../../models/video/thumbnail' import { ThumbnailModel } from '../../../models/video/thumbnail'
import { UserModel } from '../../../models/account/user'
const auditLogger = auditLoggerFactory('video-imports') const auditLogger = auditLoggerFactory('video-imports')
const videoImportsRouter = express.Router() const videoImportsRouter = express.Router()
@ -107,7 +108,8 @@ async function addTorrentImport (req: express.Request, res: express.Response, to
previewModel, previewModel,
videoChannel: res.locals.videoChannel, videoChannel: res.locals.videoChannel,
tags, tags,
videoImportAttributes videoImportAttributes,
user
}) })
// Create job to import the video // Create job to import the video
@ -151,12 +153,13 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
userId: user.id userId: user.id
} }
const videoImport = await insertIntoDB({ const videoImport = await insertIntoDB({
video: video, video,
thumbnailModel, thumbnailModel,
previewModel, previewModel,
videoChannel: res.locals.videoChannel, videoChannel: res.locals.videoChannel,
tags, tags,
videoImportAttributes videoImportAttributes,
user
}) })
// Create job to import the video // Create job to import the video
@ -227,9 +230,10 @@ function insertIntoDB (parameters: {
previewModel: ThumbnailModel, previewModel: ThumbnailModel,
videoChannel: VideoChannelModel, videoChannel: VideoChannelModel,
tags: string[], tags: string[],
videoImportAttributes: Partial<VideoImportModel> videoImportAttributes: Partial<VideoImportModel>,
user: UserModel
}): Bluebird<VideoImportModel> { }): Bluebird<VideoImportModel> {
let { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes } = parameters const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters
return sequelizeTypescript.transaction(async t => { return sequelizeTypescript.transaction(async t => {
const sequelizeOptions = { transaction: t } const sequelizeOptions = { transaction: t }
@ -241,7 +245,7 @@ function insertIntoDB (parameters: {
if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t) if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t)
await autoBlacklistVideoIfNeeded(video, videoChannel.Account.User, t) await autoBlacklistVideoIfNeeded(video, user, t)
// Set tags to the video // Set tags to the video
if (tags) { if (tags) {

View File

@ -156,6 +156,19 @@ staticRouter.use('/.well-known/change-password',
} }
) )
staticRouter.use('/.well-known/host-meta',
(_, res: express.Response) => {
res.type('application/xml')
const xml = '<?xml version="1.0" encoding="UTF-8"?>\n' +
'<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">\n' +
` <Link rel="lrdd" type="application/xrd+xml" template="${WEBSERVER.URL}/.well-known/webfinger?resource={uri}"/>\n` +
'</XRD>'
res.send(xml).end()
}
)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { export {

View File

@ -4,10 +4,11 @@ import * as chai from 'chai'
import { orderBy } from 'lodash' import { orderBy } from 'lodash'
import 'mocha' import 'mocha'
import { import {
addVideoToBlacklist, cleanupTests, addVideoToBlacklist,
cleanupTests,
createUser, createUser,
flushAndRunMultipleServers, flushAndRunMultipleServers,
getBlacklistedVideosList, getBlacklistedVideosList, getMyUserInformation,
getMyVideos, getMyVideos,
getVideosList, getVideosList,
killallServers, killallServers,
@ -16,6 +17,7 @@ import {
searchVideo, searchVideo,
ServerInfo, ServerInfo,
setAccessTokensToServers, setAccessTokensToServers,
setDefaultVideoChannel,
updateVideo, updateVideo,
updateVideoBlacklist, updateVideoBlacklist,
uploadVideo, uploadVideo,
@ -25,7 +27,8 @@ import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
import { waitJobs } from '../../../../shared/extra-utils/server/jobs' import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { VideoBlacklist, VideoBlacklistType } from '../../../../shared/models/videos' import { VideoBlacklist, VideoBlacklistType } from '../../../../shared/models/videos'
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
import { UserRole } from '../../../../shared/models/users' import { User, UserRole, UserUpdateMe } from '../../../../shared/models/users'
import { getMagnetURI, getYoutubeVideoUrl, importVideo } from '../../../../shared/extra-utils/videos/video-imports'
const expect = chai.expect const expect = chai.expect
@ -351,6 +354,7 @@ describe('Test video blacklist', function () {
describe('When auto blacklist videos', function () { describe('When auto blacklist videos', function () {
let userWithoutFlag: string let userWithoutFlag: string
let userWithFlag: string let userWithFlag: string
let channelOfUserWithoutFlag: number
before(async function () { before(async function () {
this.timeout(20000) this.timeout(20000)
@ -380,6 +384,10 @@ describe('Test video blacklist', function () {
}) })
userWithoutFlag = await userLogin(servers[0], user) userWithoutFlag = await userLogin(servers[0], user)
const res = await getMyUserInformation(servers[0].url, userWithoutFlag)
const body: User = res.body
channelOfUserWithoutFlag = body.videoChannels[0].id
} }
{ {
@ -399,7 +407,7 @@ describe('Test video blacklist', function () {
await waitJobs(servers) await waitJobs(servers)
}) })
it('Should auto blacklist a video', async function () { it('Should auto blacklist a video on upload', async function () {
await uploadVideo(servers[0].url, userWithoutFlag, { name: 'blacklisted' }) await uploadVideo(servers[0].url, userWithoutFlag, { name: 'blacklisted' })
const res = await getBlacklistedVideosList({ const res = await getBlacklistedVideosList({
@ -412,7 +420,45 @@ describe('Test video blacklist', function () {
expect(res.body.data[0].video.name).to.equal('blacklisted') expect(res.body.data[0].video.name).to.equal('blacklisted')
}) })
it('Should not auto blacklist a video', async function () { it('Should auto blacklist a video on URL import', async function () {
const attributes = {
targetUrl: getYoutubeVideoUrl(),
name: 'URL import',
channelId: channelOfUserWithoutFlag
}
await importVideo(servers[ 0 ].url, userWithoutFlag, attributes)
const res = await getBlacklistedVideosList({
url: servers[ 0 ].url,
token: servers[ 0 ].accessToken,
sort: 'createdAt',
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
})
expect(res.body.total).to.equal(2)
expect(res.body.data[1].video.name).to.equal('URL import')
})
it('Should auto blacklist a video on torrent import', async function () {
const attributes = {
magnetUri: getMagnetURI(),
name: 'Torrent import',
channelId: channelOfUserWithoutFlag
}
await importVideo(servers[ 0 ].url, userWithoutFlag, attributes)
const res = await getBlacklistedVideosList({
url: servers[ 0 ].url,
token: servers[ 0 ].accessToken,
sort: 'createdAt',
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
})
expect(res.body.total).to.equal(3)
expect(res.body.data[2].video.name).to.equal('Torrent import')
})
it('Should not auto blacklist a video on upload if the user has the bypass blacklist flag', async function () {
await uploadVideo(servers[0].url, userWithFlag, { name: 'not blacklisted' }) await uploadVideo(servers[0].url, userWithFlag, { name: 'not blacklisted' })
const res = await getBlacklistedVideosList({ const res = await getBlacklistedVideosList({
@ -421,7 +467,7 @@ describe('Test video blacklist', function () {
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
}) })
expect(res.body.total).to.equal(1) expect(res.body.total).to.equal(3)
}) })
}) })