From 2295ce6c4e7ba805cc100ff961527bebc2cd89e5 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 4 Dec 2017 10:34:40 +0100 Subject: Add account avatar --- server/models/account/account-interface.ts | 3 +++ server/models/account/account.ts | 27 ++++++++++++++++++++++++++- server/models/account/user.ts | 5 +---- server/models/avatar/avatar-interface.ts | 16 ++++++++++++++++ server/models/avatar/avatar.ts | 24 ++++++++++++++++++++++++ server/models/avatar/index.ts | 1 + server/models/index.ts | 1 + 7 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 server/models/avatar/avatar-interface.ts create mode 100644 server/models/avatar/avatar.ts create mode 100644 server/models/avatar/index.ts (limited to 'server/models') diff --git a/server/models/account/account-interface.ts b/server/models/account/account-interface.ts index b369766dc..46fe068e3 100644 --- a/server/models/account/account-interface.ts +++ b/server/models/account/account-interface.ts @@ -1,6 +1,7 @@ import * as Bluebird from 'bluebird' import * as Sequelize from 'sequelize' import { Account as FormattedAccount, ActivityPubActor } from '../../../shared' +import { AvatarInstance } from '../avatar' import { ServerInstance } from '../server/server-interface' import { VideoChannelInstance } from '../video/video-channel-interface' @@ -51,6 +52,7 @@ export interface AccountAttributes { serverId?: number userId?: number applicationId?: number + avatarId?: number } export interface AccountInstance extends AccountClass, AccountAttributes, Sequelize.Instance { @@ -68,6 +70,7 @@ export interface AccountInstance extends AccountClass, AccountAttributes, Sequel Server: ServerInstance VideoChannels: VideoChannelInstance[] + Avatar: AvatarInstance } export interface AccountModel extends AccountClass, Sequelize.Model {} diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 61a88524c..15be1126b 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts @@ -1,4 +1,6 @@ +import { join } from 'path' import * as Sequelize from 'sequelize' +import { Avatar } from '../../../shared/models/avatars/avatar.model' import { activityPubContextify, isAccountFollowersCountValid, @@ -8,8 +10,10 @@ import { isUserUsernameValid } from '../../helpers' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' +import { AVATARS_DIR } from '../../initializers' import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' +import { AvatarModel } from '../avatar' import { addMethodsToModel } from '../utils' import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface' @@ -252,6 +256,14 @@ function associate (models) { as: 'followers', onDelete: 'cascade' }) + + Account.hasOne(models.Avatar, { + foreignKey: { + name: 'avatarId', + allowNull: true + }, + onDelete: 'cascade' + }) } function afterDestroy (account: AccountInstance) { @@ -265,6 +277,15 @@ function afterDestroy (account: AccountInstance) { toFormattedJSON = function (this: AccountInstance) { let host = CONFIG.WEBSERVER.HOST let score: number + let avatar: Avatar = null + + if (this.Avatar) { + avatar = { + path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename), + createdAt: this.Avatar.createdAt, + updatedAt: this.Avatar.updatedAt + } + } if (this.Server) { host = this.Server.host @@ -273,11 +294,15 @@ toFormattedJSON = function (this: AccountInstance) { const json = { id: this.id, + uuid: this.uuid, host, score, name: this.name, + followingCount: this.followingCount, + followersCount: this.followersCount, createdAt: this.createdAt, - updatedAt: this.updatedAt + updatedAt: this.updatedAt, + avatar } return json diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 8f7c9b013..3705947c0 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts @@ -157,10 +157,7 @@ toFormattedJSON = function (this: UserInstance) { roleLabel: USER_ROLE_LABELS[this.role], videoQuota: this.videoQuota, createdAt: this.createdAt, - account: { - id: this.Account.id, - uuid: this.Account.uuid - } + account: this.Account.toFormattedJSON() } if (Array.isArray(this.Account.VideoChannels) === true) { diff --git a/server/models/avatar/avatar-interface.ts b/server/models/avatar/avatar-interface.ts new file mode 100644 index 000000000..4af2b87b7 --- /dev/null +++ b/server/models/avatar/avatar-interface.ts @@ -0,0 +1,16 @@ +import * as Sequelize from 'sequelize' + +export namespace AvatarMethods {} + +export interface AvatarClass {} + +export interface AvatarAttributes { + filename: string +} + +export interface AvatarInstance extends AvatarClass, AvatarAttributes, Sequelize.Instance { + createdAt: Date + updatedAt: Date +} + +export interface AvatarModel extends AvatarClass, Sequelize.Model {} diff --git a/server/models/avatar/avatar.ts b/server/models/avatar/avatar.ts new file mode 100644 index 000000000..3d329d888 --- /dev/null +++ b/server/models/avatar/avatar.ts @@ -0,0 +1,24 @@ +import * as Sequelize from 'sequelize' +import { addMethodsToModel } from '../utils' +import { AvatarAttributes, AvatarInstance, AvatarMethods } from './avatar-interface' + +let Avatar: Sequelize.Model + +export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { + Avatar = sequelize.define('Avatar', + { + filename: { + type: DataTypes.STRING, + allowNull: false + } + }, + {} + ) + + const classMethods = [] + addMethodsToModel(Avatar, classMethods) + + return Avatar +} + +// ------------------------------ Statics ------------------------------ diff --git a/server/models/avatar/index.ts b/server/models/avatar/index.ts new file mode 100644 index 000000000..877aed1ce --- /dev/null +++ b/server/models/avatar/index.ts @@ -0,0 +1 @@ +export * from './avatar-interface' diff --git a/server/models/index.ts b/server/models/index.ts index 65faa5294..fedd97dd1 100644 --- a/server/models/index.ts +++ b/server/models/index.ts @@ -1,4 +1,5 @@ export * from './application' +export * from './avatar' export * from './job' export * from './oauth' export * from './server' -- cgit v1.2.3 From be44767854709dbf7da4ba37fe4f16ac4e297f08 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 4 Dec 2017 11:17:08 +0100 Subject: Fix lint --- server/models/account/account.ts | 1 - server/models/avatar/avatar.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'server/models') diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 15be1126b..8b0819f39 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts @@ -13,7 +13,6 @@ import { isActivityPubUrlValid } from '../../helpers/custom-validators/activityp import { AVATARS_DIR } from '../../initializers' import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' -import { AvatarModel } from '../avatar' import { addMethodsToModel } from '../utils' import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface' diff --git a/server/models/avatar/avatar.ts b/server/models/avatar/avatar.ts index 3d329d888..96308fd5f 100644 --- a/server/models/avatar/avatar.ts +++ b/server/models/avatar/avatar.ts @@ -1,6 +1,6 @@ import * as Sequelize from 'sequelize' import { addMethodsToModel } from '../utils' -import { AvatarAttributes, AvatarInstance, AvatarMethods } from './avatar-interface' +import { AvatarAttributes, AvatarInstance } from './avatar-interface' let Avatar: Sequelize.Model -- cgit v1.2.3 From f3aaa9a95cc2b61f1f255472d7014d08faa66561 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 5 Dec 2017 17:46:33 +0100 Subject: Fix client search --- server/models/video/video-interface.ts | 1 - server/models/video/video.ts | 47 ++++++++++++++-------------------- 2 files changed, 19 insertions(+), 29 deletions(-) (limited to 'server/models') diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts index be140de86..2a63350af 100644 --- a/server/models/video/video-interface.ts +++ b/server/models/video/video-interface.ts @@ -50,7 +50,6 @@ export namespace VideoMethods { export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Bluebird< ResultList > export type SearchAndPopulateAccountAndServerAndTags = ( value: string, - field: string, start: number, count: number, sort: string diff --git a/server/models/video/video.ts b/server/models/video/video.ts index f3469c1de..4dce8e2fc 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -1070,7 +1070,7 @@ loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) { return Video.findOne(options) } -searchAndPopulateAccountAndServerAndTags = function (value: string, field: string, start: number, count: number, sort: string) { +searchAndPopulateAccountAndServerAndTags = function (value: string, start: number, count: number, sort: string) { const serverInclude: Sequelize.IncludeOptions = { model: Video['sequelize'].models.Server, required: false @@ -1099,33 +1099,24 @@ searchAndPopulateAccountAndServerAndTags = function (value: string, field: strin order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ] } - if (field === 'tags') { - const escapedValue = Video['sequelize'].escape('%' + value + '%') - query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal( - `(SELECT "VideoTags"."videoId" - FROM "Tags" - INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" - WHERE name ILIKE ${escapedValue} - )` - ) - } else if (field === 'host') { - // FIXME: Include our server? (not stored in the database) - serverInclude.where = { - host: { - [Sequelize.Op.iLike]: '%' + value + '%' - } - } - serverInclude.required = true - } else if (field === 'account') { - accountInclude.where = { - name: { - [Sequelize.Op.iLike]: '%' + value + '%' - } - } - } else { - query.where[field] = { - [Sequelize.Op.iLike]: '%' + value + '%' - } + // TODO: search on tags too + // const escapedValue = Video['sequelize'].escape('%' + value + '%') + // query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal( + // `(SELECT "VideoTags"."videoId" + // FROM "Tags" + // INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" + // WHERE name ILIKE ${escapedValue} + // )` + // ) + + // TODO: search on account too + // accountInclude.where = { + // name: { + // [Sequelize.Op.iLike]: '%' + value + '%' + // } + // } + query.where['name'] = { + [Sequelize.Op.iLike]: '%' + value + '%' } query.include = [ -- cgit v1.2.3 From b1fa3eba70dbd7d9e5b795ad251e293c88ebeee2 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Dec 2017 17:15:59 +0100 Subject: Begin video watch design --- server/models/video/video.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'server/models') diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 4dce8e2fc..60023bc8c 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -486,7 +486,7 @@ toFormattedJSON = function (this: VideoInstance) { description: this.getTruncatedDescription(), serverHost, isLocal: this.isOwned(), - account: this.VideoChannel.Account.name, + accountName: this.VideoChannel.Account.name, duration: this.duration, views: this.views, likes: this.likes, @@ -514,6 +514,7 @@ toFormattedDetailsJSON = function (this: VideoInstance) { privacy: this.privacy, descriptionPath: this.getDescriptionPath(), channel: this.VideoChannel.toFormattedJSON(), + account: this.VideoChannel.Account.toFormattedJSON(), files: [] } -- cgit v1.2.3 From 8e7f08b5a5e65195ad6dd3d7850fda57021421f3 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 Dec 2017 17:03:56 +0100 Subject: Make some fields optional when uploading a video --- server/models/video/video.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'server/models') diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 60023bc8c..8b1eb1f96 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -104,7 +104,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da }, category: { type: DataTypes.INTEGER, - allowNull: false, + allowNull: true, + defaultValue: null, validate: { categoryValid: value => { const res = isVideoCategoryValid(value) @@ -114,7 +115,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da }, licence: { type: DataTypes.INTEGER, - allowNull: false, + allowNull: true, defaultValue: null, validate: { licenceValid: value => { @@ -126,6 +127,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da language: { type: DataTypes.INTEGER, allowNull: true, + defaultValue: null, validate: { languageValid: value => { const res = isVideoLanguageValid(value) @@ -155,7 +157,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da }, description: { type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max), - allowNull: false, + allowNull: true, + defaultValue: null, validate: { descriptionValid: value => { const res = isVideoDescriptionValid(value) @@ -664,6 +667,8 @@ toActivityPubObject = function (this: VideoInstance) { } getTruncatedDescription = function (this: VideoInstance) { + if (!this.description) return null + const options = { length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max } @@ -754,8 +759,6 @@ getDescriptionPath = function (this: VideoInstance) { getCategoryLabel = function (this: VideoInstance) { let categoryLabel = VIDEO_CATEGORIES[this.category] - - // Maybe our server is not up to date and there are new categories since our version if (!categoryLabel) categoryLabel = 'Misc' return categoryLabel @@ -763,15 +766,12 @@ getCategoryLabel = function (this: VideoInstance) { getLicenceLabel = function (this: VideoInstance) { let licenceLabel = VIDEO_LICENCES[this.licence] - - // Maybe our server is not up to date and there are new licences since our version if (!licenceLabel) licenceLabel = 'Unknown' return licenceLabel } getLanguageLabel = function (this: VideoInstance) { - // Language is an optional attribute let languageLabel = VIDEO_LANGUAGES[this.language] if (!languageLabel) languageLabel = 'Unknown' -- cgit v1.2.3 From f595d3947708114deeed4312cc5ffd285745b090 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 8 Dec 2017 17:31:21 +0100 Subject: Finish admin design --- server/models/video/video.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'server/models') 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, -- cgit v1.2.3