sortField="createdAt" (onLazyLoad)="loadLazy($event)"
>
<p-column field="id" header="ID"></p-column>
- <p-column field="host" header="Host"></p-column>
+ <p-column field="follower.host" header="Host"></p-column>
<p-column field="email" header="Email"></p-column>
- <p-column field="score" header="Score"></p-column>
+ <p-column field="follower.score" header="Score"></p-column>
<p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
</p-dataTable>
</div>
sortField="createdAt" (onLazyLoad)="loadLazy($event)"
>
<p-column field="id" header="ID"></p-column>
- <p-column field="host" header="Host"></p-column>
+ <p-column field="following.host" header="Host"></p-column>
<p-column field="email" header="Email"></p-column>
- <p-column field="score" header="Score"></p-column>
+ <p-column field="following.score" header="Score"></p-column>
<p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
+ <p-column header="Unfollow" styleClass="action-cell">
+ <ng-template pTemplate="body" let-following="rowData">
+ <span (click)="removeFollowing(following)" class="glyphicon glyphicon-remove glyphicon-black" title="Unfollow"></span>
+ </ng-template>
+ </p-column>
</p-dataTable>
</div>
</div>
import { NotificationsService } from 'angular2-notifications'
import { SortMeta } from 'primeng/primeng'
import { AccountFollow } from '../../../../../../shared/models/accounts/follow.model'
+import { ConfirmService } from '../../../core/confirm/confirm.service'
import { RestPagination, RestTable } from '../../../shared'
import { FollowService } from '../shared'
constructor (
private notificationsService: NotificationsService,
+ private confirmService: ConfirmService,
private followService: FollowService
) {
super()
}
+ removeFollowing (follow: AccountFollow) {
+ this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow').subscribe(
+ res => {
+ if (res === false) return
+
+ this.followService.unfollow(follow).subscribe(
+ () => {
+ this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`)
+ this.loadData()
+ },
+
+ err => this.notificationsService.error('Error', err.message)
+ )
+ }
+ )
+ }
+
protected loadData () {
this.followService.getFollowing(this.pagination, this.sort)
.subscribe(
.catch(res => this.restExtractor.handleError(res))
}
- follow (notEmptyHosts: String[]) {
+ follow (notEmptyHosts: string[]) {
const body = {
hosts: notEmptyHosts
}
.map(this.restExtractor.extractDataBool)
.catch(res => this.restExtractor.handleError(res))
}
+
+ unfollow (follow: AccountFollow) {
+ return this.authHttp.delete(FollowService.BASE_APPLICATION_URL + '/following/' + follow.following.id)
+ .map(this.restExtractor.extractDataBool)
+ .catch(res => this.restExtractor.handleError(res))
+ }
}
-import { Component, OnInit } from '@angular/core'
+import { Component } from '@angular/core'
import { SortMeta } from 'primeng/components/common/sortmeta'
import { NotificationsService } from 'angular2-notifications'
templateUrl: './user-list.component.html',
styleUrls: [ './user-list.component.scss' ]
})
-export class UserListComponent extends RestTable implements OnInit {
+export class UserListComponent extends RestTable {
users: User[] = []
totalRecords = 0
rowsPerPage = 10
super()
}
- ngOnInit () {
- this.loadData()
- }
-
removeUser (user: User) {
if (user.username === 'root') {
this.notificationsService.error('Error', 'You cannot delete root.')
VIEWS: { min: 0 },
LIKES: { min: 0 },
DISLIKES: { min: 0 },
- FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 3 /* 3Go */ },
+ FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 10 /* 10Go */ },
URL: { min: 3, max: 2000 } // Length
},
ACCOUNTS: {
const following = await db.Account.loadByUrl(activityToUndo.object)
const accountFollow = await db.AccountFollow.loadByAccountAndTarget(follower.id, following.id)
- if (!accountFollow) throw new Error(`'Unknown account follow (${follower.id} -> ${following.id}.`)
+ if (!accountFollow) throw new Error(`'Unknown account follow ${follower.id} -> ${following.id}.`)
await accountFollow.destroy()
import * as express from 'express'
-import { body } from 'express-validator/check'
+import { body, param } from 'express-validator/check'
import { isTestInstance } from '../../helpers/core-utils'
-import { isAccountIdValid } from '../../helpers/custom-validators/activitypub/account'
import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
import { logger } from '../../helpers/logger'
import { CONFIG, database as db } from '../../initializers'
import { checkErrors } from './utils'
import { getServerAccount } from '../../helpers/utils'
+import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
const followValidator = [
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
]
const removeFollowingValidator = [
- body('accountId').custom(isAccountIdValid).withMessage('Should have a valid account id'),
+ param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking follow parameters', { parameters: req.body })
-import * as Sequelize from 'sequelize'
import * as Bluebird from 'bluebird'
-import { FollowState } from '../../../shared/models/accounts/follow.model'
+import * as Sequelize from 'sequelize'
+import { AccountFollow, FollowState } from '../../../shared/models/accounts/follow.model'
import { ResultList } from '../../../shared/models/result-list.model'
import { AccountInstance } from './account-interface'
export namespace AccountFollowMethods {
export type LoadByAccountAndTarget = (accountId: number, targetAccountId: number) => Bluebird<AccountFollowInstance>
- export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
- export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
+ export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountFollowInstance>>
+ export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountFollowInstance>>
export type ListAcceptedFollowerUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
export type ListAcceptedFollowingUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
export type ListAcceptedFollowerSharedInboxUrls = (accountId: number[]) => Promise< ResultList<string> >
+ export type ToFormattedJSON = (this: AccountFollowInstance) => AccountFollow
}
export interface AccountFollowClass {
AccountFollower?: AccountInstance
AccountFollowing?: AccountInstance
+
+ toFormattedJSON: AccountFollowMethods.ToFormattedJSON
}
export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> {}
let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
+let toFormattedJSON: AccountFollowMethods.ToFormattedJSON
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
listAcceptedFollowingUrlsForApi,
listAcceptedFollowerSharedInboxUrls
]
- addMethodsToModel(AccountFollow, classMethods)
+ const instanceMethods = [
+ toFormattedJSON
+ ]
+ addMethodsToModel(AccountFollow, classMethods, instanceMethods)
return AccountFollow
}
})
}
+toFormattedJSON = function (this: AccountFollowInstance) {
+ const follower = this.AccountFollower.toFormattedJSON()
+ const following = this.AccountFollowing.toFormattedJSON()
+
+ const json = {
+ id: this.id,
+ follower,
+ following,
+ state: this.state,
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt
+ }
+
+ return json
+}
+
loadByAccountAndTarget = function (accountId: number, targetAccountId: number) {
const query = {
where: {
return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
return {
- data: rows.map(r => r.AccountFollowing),
+ data: rows,
total: count
}
})
return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
return {
- data: rows.map(r => r.AccountFollower),
+ data: rows,
total: count
}
})
+import { Account } from './account.model'
+
export type FollowState = 'pending' | 'accepted'
export interface AccountFollow {
id: number
- name: string
- score?: number // Used for followers
- host: string
+ follower: Account
+ following: Account
+ state: FollowState
+ createdAt: Date
+ updatedAt: Date
}
# For the video upload
client_max_body_size 2G;
+ proxy_connect_timeout 600;
+ proxy_send_timeout 600;
+ proxy_read_timeout 600;
}
# Bypass PeerTube webseed route for better performances
# For the video upload
client_max_body_size 2G;
+ proxy_connect_timeout 600;
+ proxy_send_timeout 600;
+ proxy_read_timeout 600;
}
# Bypass PeerTube webseed route for better performances