From 8e4ad4b9195f168d66d3ce09a12afe736ceb481c Mon Sep 17 00:00:00 2001
From: Satsuki Yanagi <17376330+u1-liquid@users.noreply.github.com>
Date: Wed, 17 Jul 2019 18:59:10 +0900
Subject: Improve usability of users view (#5176)
* Improve usability of users view
Resolve #5173
* Fix query
* Follow review and fix
* Follow review
---
src/server/api/endpoints/admin/show-users.ts | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
(limited to 'src/server/api/endpoints')
diff --git a/src/server/api/endpoints/admin/show-users.ts b/src/server/api/endpoints/admin/show-users.ts
index 8733d87a38..89e0cf1e2a 100644
--- a/src/server/api/endpoints/admin/show-users.ts
+++ b/src/server/api/endpoints/admin/show-users.ts
@@ -49,6 +49,16 @@ export const meta = {
'remote',
]),
default: 'local'
+ },
+
+ username: {
+ validator: $.optional.str,
+ default: null
+ },
+
+ hostname: {
+ validator: $.optional.str,
+ default: null
}
}
};
@@ -70,6 +80,14 @@ export default define(meta, async (ps, me) => {
case 'remote': query.andWhere('user.host IS NOT NULL'); break;
}
+ if (ps.username) {
+ query.andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' });
+ }
+
+ if (ps.hostname) {
+ query.andWhere('user.host like :hostname', { hostname: '%' + ps.hostname.toLowerCase() + '%' });
+ }
+
switch (ps.sort) {
case '+follower': query.orderBy('user.followersCount', 'DESC'); break;
case '-follower': query.orderBy('user.followersCount', 'ASC'); break;
--
cgit v1.2.3-freya
From ef44eda69eefbdeeb1efee1c8351be081938cae5 Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Thu, 18 Jul 2019 00:11:39 +0900
Subject: Mastodonのリンクの所有者認証に対応 (#5161)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Profile metadata を設定できるように
* API desc
---
locales/ja-JP.yml | 3 ++
.../common/views/components/settings/profile.vue | 46 ++++++++++++++++++++++
src/models/repositories/user.ts | 1 +
src/remote/activitypub/renderer/person.ts | 15 ++++++-
src/server/api/endpoints/i/update.ts | 15 +++++++
src/server/web/index.ts | 10 ++++-
src/server/web/views/base.pug | 1 +
src/server/web/views/user.pug | 5 +++
8 files changed, 92 insertions(+), 4 deletions(-)
(limited to 'src/server/api/endpoints')
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 76fc26381f..b6bbb7e963 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -804,6 +804,9 @@ common/views/components/profile-editor.vue:
danger-zone: "危険な設定"
delete-account: "アカウントを削除"
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
+ profile-metadata: "プロフィール補足情報"
+ metadata-label: "ラベル"
+ metadata-content: "内容"
common/views/components/user-list-editor.vue:
users: "ユーザー"
diff --git a/src/client/app/common/views/components/settings/profile.vue b/src/client/app/common/views/components/settings/profile.vue
index 52ec8ceda3..edfc5a9edf 100644
--- a/src/client/app/common/views/components/settings/profile.vue
+++ b/src/client/app/common/views/components/settings/profile.vue
@@ -51,6 +51,26 @@
{{ $t('uploading') }}
+
+ {{ $t('profile-metadata') }}
+
+ {{ $t('metadata-label') }}
+ {{ $t('metadata-content') }}
+
+
+ {{ $t('metadata-label') }}
+ {{ $t('metadata-content') }}
+
+
+ {{ $t('metadata-label') }}
+ {{ $t('metadata-content') }}
+
+
+ {{ $t('metadata-label') }}
+ {{ $t('metadata-content') }}
+
+
+
{{ $t('save') }}
@@ -189,6 +209,17 @@ export default Vue.extend({
this.isLocked = this.$store.state.i.isLocked;
this.carefulBot = this.$store.state.i.carefulBot;
this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed;
+
+ if (this.$store.state.i.fields) {
+ this.fieldName0 = this.$store.state.i.fields[0].name;
+ this.fieldValue0 = this.$store.state.i.fields[0].value;
+ this.fieldName1 = this.$store.state.i.fields[1].name;
+ this.fieldValue1 = this.$store.state.i.fields[1].value;
+ this.fieldName2 = this.$store.state.i.fields[2].name;
+ this.fieldValue2 = this.$store.state.i.fields[2].value;
+ this.fieldName3 = this.$store.state.i.fields[3].name;
+ this.fieldValue3 = this.$store.state.i.fields[3].value;
+ }
},
methods: {
@@ -237,6 +268,13 @@ export default Vue.extend({
},
save(notify) {
+ const fields = [
+ { name: this.fieldName0, value: this.fieldValue0 },
+ { name: this.fieldName1, value: this.fieldValue1 },
+ { name: this.fieldName2, value: this.fieldValue2 },
+ { name: this.fieldName3, value: this.fieldValue3 },
+ ];
+
this.saving = true;
this.$root.api('i/update', {
@@ -247,6 +285,7 @@ export default Vue.extend({
birthday: this.birthday || null,
avatarId: this.avatarId || undefined,
bannerId: this.bannerId || undefined,
+ fields,
isCat: !!this.isCat,
isBot: !!this.isBot,
isLocked: !!this.isLocked,
@@ -389,4 +428,11 @@ export default Vue.extend({
height 72px
margin auto
+.fields
+ > header
+ padding 8px 0px
+ font-weight bold
+ > div
+ padding-left 16px
+
diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index 4e85fd7b93..a04b87f77c 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -148,6 +148,7 @@ export class UserRepository extends Repository {
description: profile!.description,
location: profile!.location,
birthday: profile!.birthday,
+ fields: profile!.fields,
followersCount: user.followersCount,
followingCount: user.followingCount,
notesCount: user.notesCount,
diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts
index efe52cdefb..d4c018fb78 100644
--- a/src/remote/activitypub/renderer/person.ts
+++ b/src/remote/activitypub/renderer/person.ts
@@ -21,13 +21,24 @@ export async function renderPerson(user: ILocalUser) {
]);
const attachment: {
- type: string,
+ type: 'PropertyValue',
name: string,
value: string,
- verified_at?: string,
identifier?: IIdentifier
}[] = [];
+ if (profile.fields) {
+ for (const field of profile.fields) {
+ attachment.push({
+ type: 'PropertyValue',
+ name: field.name,
+ value: (field.value != null && field.value.match(/^https?:/))
+ ? `${new URL(field.value).href}`
+ : field.value
+ });
+ }
+ }
+
if (profile.twitter) {
attachment.push({
type: 'PropertyValue',
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index a454cdb940..149081e50b 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -77,6 +77,13 @@ export const meta = {
}
},
+ fields: {
+ validator: $.optional.arr($.object()).range(1, 4),
+ desc: {
+ 'ja-JP': 'プロフィール補足情報'
+ }
+ },
+
isLocked: {
validator: $.optional.bool,
desc: {
@@ -226,6 +233,14 @@ export default define(meta, async (ps, user, app) => {
profileUpdates.pinnedPageId = null;
}
+ if (ps.fields) {
+ profileUpdates.fields = ps.fields
+ .filter(x => typeof x.name === 'string' && x.name !== '' && typeof x.value === 'string' && x.value !== '')
+ .map(x => {
+ return { name: x.name, value: x.value };
+ });
+ }
+
//#region emojis/tags
let emojis = [] as string[];
diff --git a/src/server/web/index.ts b/src/server/web/index.ts
index 8cf6a75208..6c41bbde46 100644
--- a/src/server/web/index.ts
+++ b/src/server/web/index.ts
@@ -156,11 +156,17 @@ router.get('/@:user', async (ctx, next) => {
if (user != null) {
const profile = await UserProfiles.findOne(user.id).then(ensure);
const meta = await fetchMeta();
+ const me = profile.fields
+ ? profile.fields
+ .filter(filed => filed.value != null && filed.value.match(/^https?:/))
+ .map(field => field.value)
+ : [];
+
await ctx.render('user', {
- user, profile,
+ user, profile, me,
instanceName: meta.name || 'Misskey'
});
- ctx.set('Cache-Control', 'public, max-age=180');
+ ctx.set('Cache-Control', 'public, max-age=30');
} else {
// リモートユーザーなので
await next();
diff --git a/src/server/web/views/base.pug b/src/server/web/views/base.pug
index 733a306d56..16bea853e7 100644
--- a/src/server/web/views/base.pug
+++ b/src/server/web/views/base.pug
@@ -44,3 +44,4 @@ html
+ block content
diff --git a/src/server/web/views/user.pug b/src/server/web/views/user.pug
index 9b257afb7b..6ff86b09be 100644
--- a/src/server/web/views/user.pug
+++ b/src/server/web/views/user.pug
@@ -36,3 +36,8 @@ block meta
link(rel='alternate' href=user.uri type='application/activity+json')
if profile.url
link(rel='alternate' href=profile.url type='text/html')
+
+block content
+ div#me
+ each m in me
+ a(rel='me' href=`${m}`) #{m}
--
cgit v1.2.3-freya
From 9c4e64b7b54886f97a9770b9d4904be29ba68eec Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Thu, 18 Jul 2019 02:03:28 +0900
Subject: Send Delete activity on suspend (#5165)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Send Delete Person activity
* Delete activityの後にフォロー解除する
* アカウント削除でもDelete activity
---
src/server/api/endpoints/admin/suspend-user.ts | 6 +++-
src/server/api/endpoints/admin/unsuspend-user.ts | 3 ++
src/server/api/endpoints/i/delete-account.ts | 4 +++
src/services/suspend-user.ts | 34 +++++++++++++++++++++++
src/services/unsuspend-user.ts | 35 ++++++++++++++++++++++++
5 files changed, 81 insertions(+), 1 deletion(-)
create mode 100644 src/services/suspend-user.ts
create mode 100644 src/services/unsuspend-user.ts
(limited to 'src/server/api/endpoints')
diff --git a/src/server/api/endpoints/admin/suspend-user.ts b/src/server/api/endpoints/admin/suspend-user.ts
index 09fdbb070e..6ba0d91505 100644
--- a/src/server/api/endpoints/admin/suspend-user.ts
+++ b/src/server/api/endpoints/admin/suspend-user.ts
@@ -5,6 +5,7 @@ import deleteFollowing from '../../../../services/following/delete';
import { Users, Followings } from '../../../../models';
import { User } from '../../../../models/entities/user';
import { insertModerationLog } from '../../../../services/insert-moderation-log';
+import { doPostSuspend } from '../../../../services/suspend-user';
export const meta = {
desc: {
@@ -51,7 +52,10 @@ export default define(meta, async (ps, me) => {
targetId: user.id,
});
- unFollowAll(user);
+ (async () => {
+ await doPostSuspend(user).catch(e => {});
+ await unFollowAll(user).catch(e => {});
+ })();
});
async function unFollowAll(follower: User) {
diff --git a/src/server/api/endpoints/admin/unsuspend-user.ts b/src/server/api/endpoints/admin/unsuspend-user.ts
index a1c80d3121..237585e276 100644
--- a/src/server/api/endpoints/admin/unsuspend-user.ts
+++ b/src/server/api/endpoints/admin/unsuspend-user.ts
@@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { Users } from '../../../../models';
import { insertModerationLog } from '../../../../services/insert-moderation-log';
+import { doPostUnsuspend } from '../../../../services/unsuspend-user';
export const meta = {
desc: {
@@ -40,4 +41,6 @@ export default define(meta, async (ps, me) => {
insertModerationLog(me, 'unsuspend', {
targetId: user.id,
});
+
+ doPostUnsuspend(user);
});
diff --git a/src/server/api/endpoints/i/delete-account.ts b/src/server/api/endpoints/i/delete-account.ts
index 8ec85c9f41..b4950cb1fb 100644
--- a/src/server/api/endpoints/i/delete-account.ts
+++ b/src/server/api/endpoints/i/delete-account.ts
@@ -3,6 +3,7 @@ import * as bcrypt from 'bcryptjs';
import define from '../../define';
import { Users, UserProfiles } from '../../../../models';
import { ensure } from '../../../../prelude/ensure';
+import { doPostSuspend } from '../../../../services/suspend-user';
export const meta = {
requireCredential: true,
@@ -26,5 +27,8 @@ export default define(meta, async (ps, user) => {
throw new Error('incorrect password');
}
+ // 物理削除する前にDelete activityを送信する
+ await doPostSuspend(user).catch(e => {});
+
await Users.delete(user.id);
});
diff --git a/src/services/suspend-user.ts b/src/services/suspend-user.ts
new file mode 100644
index 0000000000..a85188acbe
--- /dev/null
+++ b/src/services/suspend-user.ts
@@ -0,0 +1,34 @@
+import renderDelete from '../remote/activitypub/renderer/delete';
+import { renderActivity } from '../remote/activitypub/renderer';
+import { deliver } from '../queue';
+import config from '../config';
+import { User } from '../models/entities/user';
+import { Users, Followings } from '../models';
+import { Not, IsNull } from 'typeorm';
+
+export async function doPostSuspend(user: User) {
+ if (Users.isLocalUser(user)) {
+ // 知り得る全SharedInboxにDelete配信
+ const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user));
+
+ const queue: string[] = [];
+
+ const followings = await Followings.find({
+ where: [
+ { followerSharedInbox: Not(IsNull()) },
+ { followeeSharedInbox: Not(IsNull()) }
+ ],
+ select: ['followerSharedInbox', 'followeeSharedInbox']
+ });
+
+ const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox);
+
+ for (const inbox of inboxes) {
+ if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
+ }
+
+ for (const inbox of queue) {
+ deliver(user as any, content, inbox);
+ }
+ }
+}
diff --git a/src/services/unsuspend-user.ts b/src/services/unsuspend-user.ts
new file mode 100644
index 0000000000..6cab375821
--- /dev/null
+++ b/src/services/unsuspend-user.ts
@@ -0,0 +1,35 @@
+import renderDelete from '../remote/activitypub/renderer/delete';
+import renderUndo from '../remote/activitypub/renderer/undo';
+import { renderActivity } from '../remote/activitypub/renderer';
+import { deliver } from '../queue';
+import config from '../config';
+import { User } from '../models/entities/user';
+import { Users, Followings } from '../models';
+import { Not, IsNull } from 'typeorm';
+
+export async function doPostUnsuspend(user: User) {
+ if (Users.isLocalUser(user)) {
+ // 知り得る全SharedInboxにUndo Delete配信
+ const content = renderActivity(renderUndo(renderDelete(`${config.url}/users/${user.id}`, user), user));
+
+ const queue: string[] = [];
+
+ const followings = await Followings.find({
+ where: [
+ { followerSharedInbox: Not(IsNull()) },
+ { followeeSharedInbox: Not(IsNull()) }
+ ],
+ select: ['followerSharedInbox', 'followeeSharedInbox']
+ });
+
+ const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox);
+
+ for (const inbox of inboxes) {
+ if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
+ }
+
+ for (const inbox of queue) {
+ deliver(user as any, content, inbox);
+ }
+ }
+}
--
cgit v1.2.3-freya