Compare commits

...

52 Commits

Author SHA1 Message Date
syuilo 14b16b4733 10.77.0 2019-01-16 14:58:05 +09:00
syuilo 256c216dfb インスタンス情報ページに各種タイムラインの有効/無効を表示 2019-01-16 14:57:01 +09:00
syuilo 13cee2b4f5 Resolve #3896 2019-01-16 14:54:14 +09:00
syuilo eb45eeb1ae [Client] Resolve #3895 2019-01-16 14:46:54 +09:00
syuilo 3154350b64 10.76.0 2019-01-16 11:41:40 +09:00
syuilo 519c9c4499 インスタンス情報ページにユーザー数と投稿数を追加 2019-01-16 03:36:25 +09:00
syuilo c2f6b09969 disableLocalTimeline機能を強化
* ストリームだけではなくAPIも無効に
* モデレーターは無効の場合でも見れるように
* グローバルタイムラインも無効に(連合数が少ないインスタンスではグローバルタイムラインは実質的にローカルタイムラインと同一なので)
2019-01-16 02:30:55 +09:00
syuilo c8e2b22942 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-01-15 18:28:31 +09:00
syuilo f433182c4c Revert "Resolve #3813 (#3814)"
This reverts commit 96515c4544.
2019-01-15 18:28:11 +09:00
dependabot[bot] 957392aaae Update minio requirement from 7.0.2 to 7.0.3 (#3888)
Updates the requirements on [minio](https://github.com/minio/minio-js) to permit the latest version.
- [Release notes](https://github.com/minio/minio-js/releases)
- [Commits](https://github.com/minio/minio-js/commits/7.0.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-15 10:41:16 +09:00
dependabot[bot] bd8d06e133 Update vue-i18n requirement from 8.6.0 to 8.7.0 (#3889)
Updates the requirements on [vue-i18n](https://github.com/kazupon/vue-i18n) to permit the latest version.
- [Release notes](https://github.com/kazupon/vue-i18n/releases)
- [Changelog](https://github.com/kazupon/vue-i18n/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/kazupon/vue-i18n/commits/v8.7.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-15 10:40:04 +09:00
Skid c617b60f70 Correct some weird french translations in the docs. (#3884)
* Correct some weird french translations

* Update docker.fr.md

* Update setup.fr.md

* Update setup.fr.md

* Update docker.fr.md

* Update setup.fr.md
2019-01-14 06:08:33 +09:00
Acid Chicken (硫酸鶏) 018837db0b Support SVG (#3883)
* Update add-file.ts

* Update package.json

* Revert "Update add-file.ts"

This reverts commit ba0fe83b54.

* Update add-file.ts

* Update package.json

* Update add-file.ts

* Update add-file.ts

* Create image.svg

* Update api.ts
2019-01-13 18:21:10 +09:00
syuilo 00d5fdfc13 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-01-12 19:31:03 +09:00
syuilo 2debb0c2ca Provide instance description in info page 2019-01-12 19:30:56 +09:00
Nikhil Jha 148d6737cc Grammatical Fixes (#3844)
Updated the README to read more naturally for English speakers.
2019-01-12 14:34:00 +09:00
dependabot[bot] c15cb8d28c Update chalk requirement from 2.4.1 to 2.4.2 (#3855)
Updates the requirements on [chalk](https://github.com/chalk/chalk) to permit the latest version.
- [Release notes](https://github.com/chalk/chalk/releases)
- [Commits](https://github.com/chalk/chalk/commits/v2.4.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-12 14:33:23 +09:00
dependabot[bot] 1d5471db70 Update webpack-cli requirement from 3.1.2 to 3.2.1 (#3850)
Updates the requirements on [webpack-cli](https://github.com/webpack/webpack-cli) to permit the latest version.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-12 14:32:42 +09:00
Aya Morisawa 086b83c1fe Merge pull request #3851 from syuilo/dependabot/npm_and_yarn/eslint-5.12.0
Update eslint requirement from 5.11.1 to 5.12.0
2019-01-12 14:32:29 +09:00
Aya Morisawa c509e0b86c Update ts-loader requirement from 5.3.1 to 5.3.3 (#3854)
Updates the requirements on [ts-loader](https://github.com/TypeStrong/ts-loader) to permit the latest version.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/commits/v5.3.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-12 14:32:20 +09:00
dependabot[bot] e8a1ad9823 Update chai-http requirement from 4.2.0 to 4.2.1 (#3856)
Updates the requirements on [chai-http](https://github.com/chaijs/chai-http) to permit the latest version.
- [Release notes](https://github.com/chaijs/chai-http/releases)
- [Changelog](https://github.com/chaijs/chai-http/blob/master/History.md)
- [Commits](https://github.com/chaijs/chai-http/commits/4.2.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-12 14:32:11 +09:00
dependabot[bot] 6446b1cfb1 Update cssnano requirement from 4.1.7 to 4.1.8 (#3880)
Updates the requirements on [cssnano](https://github.com/cssnano/cssnano) to permit the latest version.
- [Release notes](https://github.com/cssnano/cssnano/releases)
- [Commits](https://github.com/cssnano/cssnano/commits/v4.1.8)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-12 14:32:03 +09:00
Aya Morisawa 2b9216c441 Merge pull request #3866 from syuilo/dependabot/npm_and_yarn/@types/mongodb-3.1.18
Update @types/mongodb requirement from 3.1.14 to 3.1.18
2019-01-12 14:31:52 +09:00
Aya Morisawa 28656a701f Merge pull request #3857 from syuilo/dependabot/npm_and_yarn/@types/showdown-1.9.1
Update @types/showdown requirement from 1.9.0 to 1.9.1
2019-01-12 14:31:43 +09:00
syuilo cd628eaf54 [MFM] Better hashtag parsing: Ignore trailing colon 2019-01-12 14:10:16 +09:00
syuilo 3d62bd8007 10.75.0 2019-01-12 13:27:14 +09:00
syuilo 7ad9560f53 Implement instance info page 2019-01-12 11:27:23 +09:00
syuilo c135d02895 Update CHANGELOG.md 2019-01-12 09:42:49 +09:00
syuilo 0757f67bde [Client] specifiedのアイコンを戻した 2019-01-12 09:40:53 +09:00
syuilo c5dc25cb9e Revert "[Client] Renote数の表示を廃止"
This reverts commit d7af18efbd.
2019-01-12 09:38:44 +09:00
syuilo 0fcf422dec [Client] Improve UI 2019-01-12 09:34:02 +09:00
syuilo 21e801a325 🎨 2019-01-12 09:17:23 +09:00
syuilo 183faf3b2a [Client] Resolve #3881 2019-01-12 09:13:35 +09:00
dependabot[bot] 9fc66a2d1e Update terser-webpack-plugin requirement from 1.1.0 to 1.2.1 (#3867)
Updates the requirements on [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin) to permit the latest version.
- [Release notes](https://github.com/webpack-contrib/terser-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/terser-webpack-plugin/commits/v1.2.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-11 19:58:16 +09:00
MeiMei 12cf598e6b Fix bug (for return) (#3878) 2019-01-11 17:53:53 +09:00
syuilo dd6cd6332c Update CHANGELOG.md 2019-01-11 16:00:09 +09:00
MeiMei 20ee2118ee モデレーターを凍結できないようにする (#3877) 2019-01-11 08:12:10 +09:00
MeiMei 1546160f6a モデレーター登録を解除できるように (#3876) 2019-01-11 08:11:24 +09:00
MeiMei dd77a6194e Hide NSFW in Photos (#3875) 2019-01-11 08:10:39 +09:00
MeiMei 203fba0216 管理画面でユーザーを状態でフィルタできるように (#3873) 2019-01-11 08:09:44 +09:00
MeiMei 826a2466b7 管理者がサインイン履歴を参照できるツール (#3870)
* 管理者がサインイン履歴を参照できるツール

* remove debug code
2019-01-11 08:07:09 +09:00
MeiMei 273bc6507a ダイレクトを非公開のように使えるようにする (#3869) 2019-01-11 08:06:05 +09:00
MeiMei 2b13969b26 Supports toggle password in SMTP settings (#3868) 2019-01-11 08:05:36 +09:00
Acid Chicken (硫酸鶏) 2f10ff1a2e Fix style 2019-01-10 19:23:22 +09:00
syuilo 1f52aea193 Update CHANGELOG.md 2019-01-10 17:53:56 +09:00
Stéphane Burdin 0a0e76679e Add manage and docker docs in french and correct mistake in english (#3863) 2019-01-10 10:24:04 +09:00
ibrokemypie 845f3436cc Change SMTP password field to have password type (#3861) 2019-01-10 10:23:13 +09:00
MeiMei f6a8c8cf76 Toggle show/hide password (#3865)
* Toggle show/hide password

* hoverでアンダーラインが中途半端に出てダサいので出さないように

* Update signin.vue
2019-01-10 10:22:45 +09:00
dependabot[bot] 53298933e4 Update @types/mongodb requirement from 3.1.14 to 3.1.18
Updates the requirements on [@types/mongodb](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-09 20:16:29 +00:00
dependabot[bot] e7cfae3ccb Update @types/showdown requirement from 1.9.0 to 1.9.1
Updates the requirements on [@types/showdown](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-08 20:29:07 +00:00
dependabot[bot] ebb03113ab Update ts-loader requirement from 5.3.1 to 5.3.3
Updates the requirements on [ts-loader](https://github.com/TypeStrong/ts-loader) to permit the latest version.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/commits/v5.3.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-08 20:19:39 +00:00
dependabot[bot] b3a0fe823e Update eslint requirement from 5.11.1 to 5.12.0
Updates the requirements on [eslint](https://github.com/eslint/eslint) to permit the latest version.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v5.12.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-08 10:15:52 +00:00
53 changed files with 743 additions and 98 deletions
+27
View File
@@ -1,6 +1,33 @@
ChangeLog
=========
10.77.0
----------
* ローカルタイムライン無効オプションをグローバルタイムライン無効オプションと分離
* モデレータはLTL無効時でもUIからLTLを消さない
* インスタンス情報ページに各種タイムラインの有効/無効を表示
10.76.0
----------
* disableLocalTimeline機能を強化
* インスタンス情報ページの強化
* ハッシュタグ判定の強化
* SVGサムネイルを表示するように
* CWの引き継ぎ機能を無効化
10.75.0
----------
* ダイレクトを非公開のように使えるように
* モデレーターを凍結できないように
* モデレーター登録を解除できるように
* NSFWなメディアをユーザーページなどで表示しないように
* 管理画面でユーザーを状態でフィルタできるように
* 管理者がサインイン履歴を参照できるツール
* Renote数を再度表示するように
* インスタンス情報ページの追加
* テーマの調整
* UIの改善
10.74.0
----------
* Pleromaとのフェデレーションを修正
+10 -10
View File
@@ -7,7 +7,7 @@
[![Dependencies](https://img.shields.io/david/syuilo/misskey.svg?style=for-the-badge)](https://david-dm.org/syuilo/misskey)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge)](http://makeapullrequest.com)
**Sophisticated microblogging platform, evolving forever.**
**A forever evolving, sophisticated microblogging platform.**
<p align="justify">
<a href="https://misskey.xyz">Misskey</a> is a decentralized microblogging platform born on Earth.
@@ -27,7 +27,7 @@ Why don't you take a short break from the hustle and bustle of the city, and div
<h3 align="left">Posting</h3>
<p align="justify">
Just post your idea, hot topics and anything you want to share. You may decorate your words, attach your favorite pictures or movies, and create a poll - those are all supported in Misskey!
Post your ideas, discussion topics, fun moments, or anything else you want to share! Misskey supports text, emoji, pictures, videos, and polls!
</p>
---
@@ -36,7 +36,7 @@ Just post your idea, hot topics and anything you want to share. You may decorate
<h3 align="right">Reactions</h3>
<p align="justify">
The simplest way to tell your emotions to the posts. You can choose the best reaction from various reactions. Reactions on Misskey has much more expressive than other social media which only allows pushing “likes”.
Reactions are the simplest way to respond to others' posts. Simply pick a reaction emote from the list! Reactions on Misskey are much more expressive than other social media services which only allow “liking”.
</p>
---
@@ -45,7 +45,7 @@ The simplest way to tell your emotions to the posts. You can choose the best rea
<h3 align="left">Interface</h3>
<p align="justify">
Highly customizable UI for your taste. We understand no UI fits for everyone. Make your graceful home by editing, adjusting layouts of timeline, and placing widgets.
Customize the UI to your own tastes! No UI will work for everyone, so Misskey is completely customizable. Make Misskey *yours* by editing the style, adjusting timeline layouts, and placing widgets.
</p>
---
@@ -54,20 +54,20 @@ Highly customizable UI for your taste. We understand no UI fits for everyone. Ma
<h3 align="right">Misskey Drive</h3>
<p align="justify">
Organized uploaded files. Wanna post a picture you have already uploaded? Wish to create a folder for your files? Misskey Drive is the best solution for you.
Organize and store your files! Want to post a picture you have already uploaded? Wish you could organize your files into folders? Misskey Drive is a solution!
</p>
---
and more! Now it's time to experience the world with your own eyes at [misskey.xyz](https://misskey.xyz) or [other instances](https://joinmisskey.github.io/).
...and more! Experience Misskey with your own eyes at [misskey.xyz](https://misskey.xyz) or join one of the [other instances](https://joinmisskey.github.io/) that are available.
:package: Create your own instance
:package: Create Your Own Instance
----------------------------------------------------------------
Please see [Setup and installation guide](./docs/setup.en.md).
Please see the [Setup and Installation Guide](./docs/setup.en.md).
:wrench: Contribution
----------------------------------------------------------------
Please see [Contribution guide](./CONTRIBUTING.md).
Please see the [Contribution Guide](./CONTRIBUTING.md).
:heart: Backers & Sponsors
----------------------------------------------------------------
@@ -123,7 +123,7 @@ Please see [Contribution guide](./CONTRIBUTING.md).
----------------------------------------------------------------
> Copyright (c) 2014-2019 syuilo
Misskey is an open-source software licensed under the [GNU AGPLv3](LICENSE).
Misskey is open-source software licensed under the [GNU AGPLv3](LICENSE).
[![][agpl-3.0-badge]][AGPL-3.0]
+1 -1
View File
@@ -17,7 +17,7 @@ This guide describes how to install and setup Misskey with Docker.
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` Copy the `.config/mongo_initdb_example.js` and rename it to `mongo_initdb.js`.
2. Edit `default.yml` and `mongo_initdb.js`.
3. Edit `default.yml` and `mongo_initdb.js`.
*3.* Configure Docker
----------------------------------------------------------------
+67
View File
@@ -0,0 +1,67 @@
Guide Docker
================================================================
Ce guide explique comment installer et configurer Misskey avec Docker.
[Version japonaise également disponible - Japanese version also available - 日本語版もあります](./docker.ja.md)
[Version anglaise également disponible - English version also available - 英語版もあります](./docker.en.md)
----------------------------------------------------------------
*1.* Télécharger Misskey
----------------------------------------------------------------
1. `git clone -b master git://github.com/syuilo/misskey.git` Clone le dépôt de Misskey sur la branche master.
2. `cd misskey` Naviguez dans le dossier du dépôt.
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout sur le tag de la [dernière version](https://github.com/syuilo/misskey/releases/latest).
*2.* Configuration de Misskey
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le `default.yml`.
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` Copie le fichier `.config/mongo_initdb_example.js` et le renomme en `mongo_initdb.js`.
3. Editez `default.yml` et `mongo_initdb.js`.
*3.* Configurer Docker
----------------------------------------------------------------
Editez `docker-compose.yml`.
*4.* Contruire Misskey
----------------------------------------------------------------
Contruire l'image Docker avec:
`docker-compose build`
*5.* C'est tout !
----------------------------------------------------------------
Parfait, Vous avez un environnement prêt pour démarrer Misskey.
### Lancer normalement
Utilisez la commande `docker-compose up -d`. GLHF!
### How to update your Misskey server to the latest version
1. `git fetch`
2. `git stash`
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
4. `git stash pop`
5. `docker-compose build`
6. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration
7. `docker-compose stop && docker-compose up -d`
### Comment exécuter des [commandes](manage.fr.md)
`docker-compose run --rm web node cli/mark-admin @example`
### Configuration d'ElasticSearch (pour la fonction de recherche)
*1.* Préparation de l'environnement
----------------------------------------------------------------
1. `mkdir elasticsearch && chown 1000:1000 elasticsearch` Permet de créer le dossier d'accueil de la base ElasticSearch aves les bons droits
2. `sysctl -w vm.max_map_count=262144` Augmente la valeur max du paramètre map_count du système (valeur minimum pour pouvoir lancer ES)
*2.* Après lancement du docker-compose, initialisation de la base ElasticSearch
----------------------------------------------------------------
1. `docker-compose -it web /bin/sh` Connexion dans le conteneur web
2. `apk add curl` Ajout du paquet curl
3. `curl -X PUT "es:9200/misskey" -H 'Content-Type: application/json' -d'{ "settings" : { "index" : { } }}'` Création de la base ES
4. `exit`
----------------------------------------------------------------
Si vous avez des questions ou des problèmes, n'hésitez pas à nous contacter !
+18
View File
@@ -0,0 +1,18 @@
# Guide d'administration
## Vérifier le status de la file d'attente des taches
coming soon
## Marquer un utilisateur en tant que 'admin'
``` shell
node cli/mark-admin (ID utilisateur ou nom d'utilisateur)
```
Exemple :
``` shell
# Par id
node cli/mark-admin 57d01a501fdf2d07be417afe
# Par nom d'utilisateur
node cli/suspend @syuilo
```
+9 -9
View File
@@ -10,8 +10,8 @@ Ce guide décrit les étapes à suivre afin d'installer et de configurer une ins
*1.* Création de l'utilisateur Misskey
----------------------------------------------------------------
Lancer misskey en tant qu'utilisateur est une mauvaise idée, nous avons besoin de créer un utilisateur dédié.
Sur Debian, à titre d'exemple :
Executer misskey en tant que super-utilisateur étant une mauvaise idée, nous allons créer un utilisateur dédié.
Sous Debian, par exemple :
```
adduser --disabled-password --disabled-login misskey
@@ -32,10 +32,10 @@ Installez les paquets suivants :
*3.* Paramètrage de MongoDB
----------------------------------------------------------------
En mode root :
1. `mongo` Accédez au shell de mango
En root :
1. `mongo` Ouvrez le shell mongo
2. `use misskey` Utilisez la base de données misskey
3. `db.users.save( {dummy:"dummy"} )` Write dummy data to initialize the db.
3. `db.users.save( {dummy:"dummy"} )` Écrivez une donnée factice pour initialiser la base de données.
4. `db.createUser( { user: "misskey", pwd: "<password>", roles: [ { role: "readWrite", db: "misskey" } ] } )` Créez l'utilisateur misskey.
5. `exit` Vous avez terminé !
@@ -44,12 +44,12 @@ En mode root :
1. `su - misskey` Basculez vers l'utilisateur misskey.
2. `git clone -b master git://github.com/syuilo/misskey.git` Clonez la branche master du dépôt misskey.
3. `cd misskey` Accédez au dossier misskey.
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Télécharge la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout sur le tag de la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
5. `npm install` Installez les dépendances de misskey.
*5.* Création du fichier de configuration
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le `default.yml`.
1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le`default.yml`.
2. Editez le fichier `default.yml`
*6.* Construction de Misskey
@@ -59,7 +59,7 @@ Construisez Misskey comme ceci :
`npm run build`
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential`, `python`.
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential` et `python`.
Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
@@ -77,7 +77,7 @@ Lancez tout simplement `npm start`. Bonne chance et amusez-vous bien !
### Démarrage avec systemd
1. Créez une service systemd sur : `/etc/systemd/system/misskey.service`
1. Créez un service systemd sur : `/etc/systemd/system/misskey.service`
2. Editez-le puis copiez et coller ceci dans le fichier :
```
+13
View File
@@ -132,6 +132,8 @@ common:
line-width-thin: "細い"
line-width-normal: "普通"
line-width-thick: "太い"
hide-password: "パスワードを隠す"
show-password: "パスワードを表示する"
do-not-use-in-production: "これは開発ビルドです。本番環境で使用しないでください。"
user-suspended: "このユーザーは凍結されています。"
@@ -1161,6 +1163,7 @@ admin/views/instance.vue:
max-note-text-length: "投稿の最大文字数"
disable-registration: "ユーザー登録の受付を停止する"
disable-local-timeline: "ローカルタイムラインを無効にする"
disable-global-timeline: "グローバルタイムラインを無効にする"
invite: "招待"
save: "保存"
saved: "保存しました"
@@ -1253,6 +1256,14 @@ admin/views/users.vue:
createdAtDesc: "登録日時が新しい順"
updatedAtAsc: "更新日時が古い順"
updatedAtDesc: "更新日時が新しい順"
state:
title: "状態"
all: "すべて"
admin: "管理者"
moderator: "モデレーター"
adminOrModerator: "管理者+モデレーター"
verified: "公式アカウント"
suspended: "凍結済み"
origin:
title: "オリジン"
combined: "ローカル+リモート"
@@ -1266,6 +1277,8 @@ admin/views/moderators.vue:
title: "モデレーターの登録"
add: "登録"
added: "モデレーターを登録しました"
remove: "解除"
removed: "モデレーター登録を解除しました"
admin/views/emoji.vue:
add-emoji:
+15 -13
View File
@@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.74.0",
"clientVersion": "2.0.13345",
"version": "10.77.0",
"clientVersion": "2.0.13397",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@@ -43,6 +43,7 @@
"@types/gulp-uglify": "3.0.6",
"@types/gulp-util": "3.0.34",
"@types/is-root": "1.0.0",
"@types/is-svg": "3.0.0",
"@types/is-url": "1.2.28",
"@types/js-yaml": "3.11.4",
"@types/katex": "0.5.0",
@@ -60,7 +61,7 @@
"@types/minio": "7.0.1",
"@types/mkdirp": "0.5.2",
"@types/mocha": "5.2.5",
"@types/mongodb": "3.1.14",
"@types/mongodb": "3.1.18",
"@types/ms": "0.7.30",
"@types/node": "10.12.18",
"@types/nodemailer": "4.6.5",
@@ -76,7 +77,7 @@
"@types/rimraf": "2.0.2",
"@types/seedrandom": "2.4.27",
"@types/sharp": "0.21.0",
"@types/showdown": "1.9.0",
"@types/showdown": "1.9.1",
"@types/speakeasy": "2.0.3",
"@types/systeminformation": "3.23.1",
"@types/tinycolor2": "1.4.1",
@@ -96,12 +97,12 @@
"bootstrap-vue": "2.0.0-rc.11",
"cafy": "12.0.0",
"chai": "4.2.0",
"chai-http": "4.2.0",
"chalk": "2.4.1",
"chalk": "2.4.2",
"chai-http": "4.2.1",
"commander": "2.19.0",
"crc-32": "1.2.0",
"css-loader": "1.0.1",
"cssnano": "4.1.7",
"cssnano": "4.1.8",
"dateformat": "3.0.3",
"debug": "4.1.0",
"deep-equal": "1.0.1",
@@ -111,7 +112,7 @@
"elasticsearch": "15.2.0",
"emojilib": "2.4.0",
"escape-regexp": "0.0.1",
"eslint": "5.11.1",
"eslint": "5.12.0",
"eslint-plugin-vue": "5.0.0",
"eventemitter3": "3.1.0",
"feed": "2.0.2",
@@ -136,6 +137,7 @@
"http-signature": "1.2.0",
"insert-text-at-cursor": "0.1.1",
"is-root": "2.0.0",
"is-svg": "3.0.0",
"is-url": "1.2.4",
"js-yaml": "3.12.0",
"jsdom": "13.1.0",
@@ -157,7 +159,7 @@
"langmap": "0.0.16",
"loader-utils": "1.2.3",
"lookup-dns-cache": "2.1.0",
"minio": "7.0.2",
"minio": "7.0.3",
"mkdirp": "0.5.1",
"mocha": "5.2.0",
"moji": "0.5.1",
@@ -206,11 +208,11 @@
"summaly": "2.2.0",
"systeminformation": "3.52.2",
"syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.1.0",
"terser-webpack-plugin": "1.2.1",
"textarea-caret": "3.1.0",
"tinycolor2": "1.4.1",
"tmp": "0.0.33",
"ts-loader": "5.3.1",
"ts-loader": "5.3.3",
"ts-node": "7.0.1",
"tslint": "5.12.0",
"tslint-sonarts": "1.8.0",
@@ -224,7 +226,7 @@
"vue-color": "2.7.0",
"vue-content-loading": "1.5.3",
"vue-cropperjs": "3.0.0",
"vue-i18n": "8.6.0",
"vue-i18n": "8.7.0",
"vue-js-modal": "1.3.28",
"vue-loader": "15.4.2",
"vue-marquee-text-component": "1.1.1",
@@ -240,7 +242,7 @@
"web-push": "3.3.3",
"webfinger.js": "2.7.0",
"webpack": "4.28.3",
"webpack-cli": "3.1.2",
"webpack-cli": "3.2.1",
"websocket": "1.0.28",
"ws": "6.1.2",
"xev": "2.0.1"
+5 -1
View File
@@ -22,6 +22,7 @@
<section>
<ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
<ui-switch v-model="disableLocalTimeline">{{ $t('disable-local-timeline') }}</ui-switch>
<ui-switch v-model="disableGlobalTimeline">{{ $t('disable-global-timeline') }}</ui-switch>
</section>
<section class="fit-bottom">
<header><fa icon="cloud"/> {{ $t('drive-config') }}</header>
@@ -54,7 +55,7 @@
</ui-horizon-group>
<ui-horizon-group inputs>
<ui-input v-model="smtpUser" :disabled="!enableEmail">{{ $t('smtp-user') }}</ui-input>
<ui-input v-model="smtpPass" :disabled="!enableEmail">{{ $t('smtp-pass') }}</ui-input>
<ui-input v-model="smtpPass" type="password" :withPasswordToggle="true" :disabled="!enableEmail">{{ $t('smtp-pass') }}</ui-input>
</ui-horizon-group>
<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<span slot="desc">{{ $t('smtp-secure-info') }}</span></ui-switch>
</section>
@@ -150,6 +151,7 @@ export default Vue.extend({
maintainerEmail: null,
disableRegistration: false,
disableLocalTimeline: false,
disableGlobalTimeline: false,
mascotImageUrl: null,
bannerUrl: null,
errorImageUrl: null,
@@ -198,6 +200,7 @@ export default Vue.extend({
this.maintainerEmail = meta.maintainer.email;
this.disableRegistration = meta.disableRegistration;
this.disableLocalTimeline = meta.disableLocalTimeline;
this.disableGlobalTimeline = meta.disableGlobalTimeline;
this.mascotImageUrl = meta.mascotImageUrl;
this.bannerUrl = meta.bannerUrl;
this.errorImageUrl = meta.errorImageUrl;
@@ -256,6 +259,7 @@ export default Vue.extend({
maintainerEmail: this.maintainerEmail,
disableRegistration: this.disableRegistration,
disableLocalTimeline: this.disableLocalTimeline,
disableGlobalTimeline: this.disableGlobalTimeline,
mascotImageUrl: this.mascotImageUrl,
bannerUrl: this.bannerUrl,
errorImageUrl: this.errorImageUrl,
+27 -4
View File
@@ -6,7 +6,8 @@
<ui-input v-model="username" type="text">
<span slot="prefix">@</span>
</ui-input>
<ui-button @click="add" :disabled="adding">{{ $t('add-moderator.add') }}</ui-button>
<ui-button @click="add" :disabled="changing">{{ $t('add-moderator.add') }}</ui-button>
<ui-button @click="remove" :disabled="changing">{{ $t('add-moderator.remove') }}</ui-button>
</section>
</ui-card>
</div>
@@ -23,13 +24,13 @@ export default Vue.extend({
data() {
return {
username: '',
adding: false
changing: false
};
},
methods: {
async add() {
this.adding = true;
this.changing = true;
const process = async () => {
const user = await this.$root.api('users/show', parseAcct(this.username));
@@ -47,7 +48,29 @@ export default Vue.extend({
});
});
this.adding = false;
this.changing = false;
},
async remove() {
this.changing = true;
const process = async () => {
const user = await this.$root.api('users/show', parseAcct(this.username));
await this.$root.api('admin/moderators/remove', { userId: user.id });
this.$root.dialog({
type: 'success',
text: this.$t('add-moderator.removed')
});
};
await process().catch(e => {
this.$root.dialog({
type: 'error',
text: e.toString()
});
});
this.changing = false;
},
}
});
+37 -2
View File
@@ -31,6 +31,14 @@
<option value="-updatedAt">{{ $t('users.sort.updatedAtAsc') }}</option>
<option value="+updatedAt">{{ $t('users.sort.updatedAtDesc') }}</option>
</ui-select>
<ui-select v-model="state">
<span slot="label">{{ $t('users.state.title') }}</span>
<option value="all">{{ $t('users.state.all') }}</option>
<option value="admin">{{ $t('users.state.admin') }}</option>
<option value="moderator">{{ $t('users.state.moderator') }}</option>
<option value="verified">{{ $t('users.state.verified') }}</option>
<option value="suspended">{{ $t('users.state.suspended') }}</option>
</ui-select>
<ui-select v-model="origin">
<span slot="label">{{ $t('users.origin.title') }}</span>
<option value="combined">{{ $t('users.origin.combined') }}</option>
@@ -39,7 +47,7 @@
</ui-select>
</ui-horizon-group>
<sequential-entrance animation="entranceFromTop" delay="25">
<div class="kofvwchc" v-for="user in users">
<div class="kofvwchc" v-for="user in users" :key="user.id">
<div>
<a :href="user | userPage(null, true)">
<mk-avatar class="avatar" :user="user" :disable-link="true"/>
@@ -49,6 +57,10 @@
<header>
<b><mk-user-name :user="user"/></b>
<span class="username">@{{ user | acct }}</span>
<span class="is-admin" v-if="user.isAdmin">admin</span>
<span class="is-moderator" v-if="user.isModerator">moderator</span>
<span class="is-verified" v-if="user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
<span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span>
</header>
<div>
<span>{{ $t('users.updatedAt') }}: <mk-time :time="user.updatedAt" mode="detail"/></span>
@@ -84,6 +96,7 @@ export default Vue.extend({
suspending: false,
unsuspending: false,
sort: '+createdAt',
state: 'all',
origin: 'combined',
limit: 10,
offset: 0,
@@ -100,6 +113,12 @@ export default Vue.extend({
this.fetchUsers();
},
state() {
this.users = [];
this.offset = 0;
this.fetchUsers();
},
origin() {
this.users = [];
this.offset = 0;
@@ -236,7 +255,8 @@ export default Vue.extend({
},
fetchUsers() {
this.$root.api('users', {
this.$root.api('admin/show-users', {
state: this.state,
origin: this.origin,
sort: this.sort,
offset: this.offset,
@@ -284,4 +304,19 @@ export default Vue.extend({
margin-left 8px
opacity 0.7
> .is-admin
> .is-moderator
flex-shrink 0
align-self center
margin 0 0 0 .5em
padding 1px 6px
font-size 80%
border-radius 3px
background var(--noteHeaderAdminBg)
color var(--noteHeaderAdminFg)
> .is-verified
> .is-suspended
margin 0 0 0 .5em
color #4dabf7
</style>
@@ -18,7 +18,7 @@
<span class="visibility" v-if="note.visibility != 'public'">
<fa v-if="note.visibility == 'home'" icon="home"/>
<fa v-if="note.visibility == 'followers'" icon="unlock"/>
<fa v-if="note.visibility == 'specified'" icon="lock"/>
<fa v-if="note.visibility == 'specified'" icon="envelope"/>
</span>
<span class="localOnly" v-if="note.localOnly == true"><fa icon="heart"/></span>
</div>
@@ -13,7 +13,7 @@
<span class="visibility" v-if="note.visibility != 'public'">
<fa v-if="note.visibility == 'home'" icon="home"/>
<fa v-if="note.visibility == 'followers'" icon="unlock"/>
<fa v-if="note.visibility == 'specified'" icon="lock"/>
<fa v-if="note.visibility == 'specified'" icon="envelope"/>
</span>
<span class="localOnly" v-if="note.localOnly == true"><fa icon="heart"/></span>
</div>
@@ -6,7 +6,7 @@
<span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span>
</ui-input>
<ui-input v-model="password" type="password" required styl="fill">
<ui-input v-model="password" type="password" :with-password-toggle="true" required styl="fill">
<span>{{ $t('password') }}</span>
<span slot="prefix"><fa icon="lock"/></span>
</ui-input>
@@ -1,5 +1,5 @@
<template>
<div class="vnxwkwuf" :class="{ inputs, noGrow }">
<div class="vnxwkwuf" :class="{ inputs, noGrow }" :data-children-count="children">
<slot></slot>
</div>
</template>
@@ -21,6 +21,16 @@ export default Vue.extend({
required: false,
default: false
}
},
data() {
return {
children: 0
};
},
mounted() {
this.$nextTick(() => {
this.children = this.$slots.default.length;
});
}
});
</script>
@@ -48,4 +58,19 @@ export default Vue.extend({
> *:not(:last-child)
margin-right 16px !important
&[data-children-count="3"]
@media (max-width 600px)
display block
> *
display block
width 100% !important
margin 16px 0 !important
&:first-child
margin-top 0 !important
&:last-child
margin-bottom 0 !important
</style>
@@ -39,6 +39,12 @@
</template>
<div class="suffix" ref="suffix"><slot name="suffix"></slot></div>
</div>
<div class="toggle" v-if="withPasswordToggle">
<a @click='togglePassword'>
<span v-if="type == 'password'"><fa :icon="['fa', 'eye']"/> {{ $t('@.show-password') }}</span>
<span v-if="type != 'password'"><fa :icon="['far', 'eye-slash']"/> {{ $t('@.hide-password') }}</span>
</a>
</div>
<div class="desc"><slot name="desc"></slot></div>
</div>
</template>
@@ -97,6 +103,11 @@ export default Vue.extend({
required: false,
default: false
},
withPasswordToggle: {
type: Boolean,
required: false,
default: false
},
inline: {
type: Boolean,
required: false,
@@ -178,6 +189,13 @@ export default Vue.extend({
focus() {
this.$refs.input.focus();
},
togglePassword() {
if (this.type == 'password') {
this.type = 'text'
} else {
this.type = 'password'
}
},
chooseFile() {
this.$refs.file.click();
},
@@ -356,6 +374,17 @@ root(fill)
if fill
padding-right 12px
> .toggle
cursor pointer
padding-left 0.5em
font-size 0.7em
opacity 0.7
text-align left
> a
color var(--inputLabel)
text-decoration none
> .desc
margin 6px 0
font-size 13px
@@ -23,7 +23,7 @@
</div>
</div>
<div @click="choose('specified')" :class="{ active: v == 'specified' }">
<div><fa icon="lock"/></div>
<div><fa icon="envelope"/></div>
<div>
<span>{{ $t('specified') }}</span>
<span>{{ $t('specified-desc') }}</span>
@@ -32,7 +32,7 @@
<span class="visibility" v-if="appearNote.visibility != 'public'">
<fa v-if="appearNote.visibility == 'home'" icon="home"/>
<fa v-if="appearNote.visibility == 'followers'" icon="unlock"/>
<fa v-if="appearNote.visibility == 'specified'" icon="lock"/>
<fa v-if="appearNote.visibility == 'specified'" icon="envelope"/>
</span>
<span class="localOnly" v-if="appearNote.localOnly == true"><fa icon="heart"/></span>
</div>
@@ -70,7 +70,7 @@
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
<button v-if="['public', 'home'].includes(appearNote.visibility)" class="renoteButton" @click="renote()" :title="$t('renote')">
<fa icon="retweet"/>
<fa icon="retweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
</button>
<button v-else class="inhibitedButton">
<fa icon="ban"/>
@@ -48,7 +48,7 @@
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
<button v-if="['public', 'home'].includes(appearNote.visibility)" class="renoteButton" @click="renote()" :title="$t('renote')">
<fa icon="retweet"/>
<fa icon="retweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
</button>
<button v-else class="inhibitedButton">
<fa icon="ban"/>
@@ -50,7 +50,7 @@
<span v-if="visibility === 'public'"><fa icon="globe"/></span>
<span v-if="visibility === 'home'"><fa icon="home"/></span>
<span v-if="visibility === 'followers'"><fa icon="unlock"/></span>
<span v-if="visibility === 'specified'"><fa icon="lock"/></span>
<span v-if="visibility === 'specified'"><fa icon="envelope"/></span>
</button>
<p class="text-count" :class="{ over: trimmedLength(text) > maxNoteTextLength }">{{ maxNoteTextLength - trimmedLength(text) }}</p>
<ui-button primary :wait="posting" class="submit" :disabled="!canPost" @click="post">
@@ -222,12 +222,6 @@ export default Vue.extend({
});
}
// keep cw when reply
if (this.reply && this.reply.cw != null) {
this.useCw = true;
this.cw = this.reply.cw;
}
this.$nextTick(() => {
// 書きかけの投稿を復元
if (!this.instant && !this.mention) {
@@ -4,7 +4,7 @@
<span :data-active="src == 'home'" @click="src = 'home'"><fa icon="home"/> {{ $t('home') }}</span>
<span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline"><fa :icon="['far', 'comments']"/> {{ $t('local') }}</span>
<span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline"><fa icon="share-alt"/> {{ $t('hybrid') }}</span>
<span :data-active="src == 'global'" @click="src = 'global'"><fa icon="globe"/> {{ $t('global') }}</span>
<span :data-active="src == 'global'" @click="src = 'global'" v-if="enableGlobalTimeline"><fa icon="globe"/> {{ $t('global') }}</span>
<span :data-active="src == 'tag'" @click="src = 'tag'" v-if="tagTl"><fa icon="hashtag"/> {{ tagTl.title }}</span>
<span :data-active="src == 'list'" @click="src = 'list'" v-if="list"><fa icon="list"/> {{ list.title }}</span>
<div class="buttons">
@@ -43,7 +43,8 @@ export default Vue.extend({
src: 'home',
list: null,
tagTl: null,
enableLocalTimeline: false
enableLocalTimeline: false,
enableGlobalTimeline: false,
};
},
@@ -65,7 +66,8 @@ export default Vue.extend({
created() {
this.$root.getMeta().then(meta => {
this.enableLocalTimeline = !meta.disableLocalTimeline;
this.enableLocalTimeline = !meta.disableLocalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin;
this.enableGlobalTimeline = !meta.disableGlobalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin;
});
if (this.$store.state.device.tl) {
@@ -169,6 +169,7 @@ export default Vue.extend({
this.$root.api('users/notes', {
userId: this.user.id,
fileType: image,
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
limit: 9,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => {
@@ -29,9 +29,11 @@ export default Vue.extend({
'image/png',
'image/gif'
];
this.$root.api('users/notes', {
userId: this.user.id,
fileType: image,
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
limit: 9,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => {
@@ -37,6 +37,7 @@
</div>
<div class="menu">
<div>
<ui-input readonly :value="file.url">URL</ui-input>
<ui-button link :href="`${file.url}?download`" :download="file.name"><fa icon="download"/> {{ $t('download') }}</ui-button>
<ui-button @click="rename"><fa icon="pencil-alt"/> {{ $t('rename') }}</ui-button>
<ui-button @click="move"><fa :icon="['far', 'folder-open']"/> {{ $t('move') }}</ui-button>
@@ -200,7 +201,7 @@ export default Vue.extend({
color #bf4633
> .menu
padding 14px
padding 0 14px 14px 14px
border-top solid 1px var(--faceDivider)
> div
@@ -54,7 +54,7 @@
<span class="visibility" v-if="appearNote.visibility != 'public'">
<fa v-if="appearNote.visibility == 'home'" icon="home"/>
<fa v-if="appearNote.visibility == 'followers'" icon="unlock"/>
<fa v-if="appearNote.visibility == 'specified'" icon="lock"/>
<fa v-if="appearNote.visibility == 'specified'" icon="envelope"/>
</span>
<span class="localOnly" v-if="appearNote.localOnly == true"><fa icon="heart"/></span>
</div>
@@ -66,7 +66,7 @@
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
<button v-if="['public', 'home'].includes(appearNote.visibility)" @click="renote()" title="Renote">
<fa icon="retweet"/>
<fa icon="retweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
</button>
<button v-else>
<fa icon="ban"/>
@@ -44,7 +44,7 @@
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
<button v-if="['public', 'home'].includes(appearNote.visibility)" @click="renote()" title="Renote">
<fa icon="retweet"/>
<fa icon="retweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
</button>
<button v-else>
<fa icon="ban"/>
@@ -41,7 +41,7 @@
<span v-if="visibility === 'public'"><fa icon="globe"/></span>
<span v-if="visibility === 'home'"><fa icon="home"/></span>
<span v-if="visibility === 'followers'"><fa icon="unlock"/></span>
<span v-if="visibility === 'specified'"><fa icon="lock"/></span>
<span v-if="visibility === 'specified'"><fa icon="envelope"/></span>
</button>
</footer>
<input ref="file" class="file" type="file" multiple="multiple" @change="onChangeFile"/>
@@ -211,12 +211,6 @@ export default Vue.extend({
});
}
// keep cw when reply
if (this.reply && this.reply.cw != null) {
this.useCw = true;
this.cw = this.reply.cw;
}
this.focus();
this.$nextTick(() => {
+5 -3
View File
@@ -31,7 +31,7 @@
<span :data-active="src == 'home'" @click="src = 'home'"><fa icon="home"/> {{ $t('home') }}</span>
<span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline"><fa :icon="['far', 'comments']"/> {{ $t('local') }}</span>
<span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline"><fa icon="share-alt"/> {{ $t('hybrid') }}</span>
<span :data-active="src == 'global'" @click="src = 'global'"><fa icon="globe"/> {{ $t('global') }}</span>
<span :data-active="src == 'global'" @click="src = 'global'" v-if="enableGlobalTimeline"><fa icon="globe"/> {{ $t('global') }}</span>
<div class="hr"></div>
<span :data-active="src == 'mentions'" @click="src = 'mentions'"><fa icon="at"/> {{ $t('mentions') }}<i class="badge" v-if="$store.state.i.hasUnreadMentions"><fa icon="circle"/></i></span>
<span :data-active="src == 'messages'" @click="src = 'messages'"><fa :icon="['far', 'envelope']"/> {{ $t('messages') }}<i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes"><fa icon="circle"/></i></span>
@@ -79,7 +79,8 @@ export default Vue.extend({
lists: null,
tagTl: null,
showNav: false,
enableLocalTimeline: false
enableLocalTimeline: false,
enableGlobalTimeline: false,
};
},
@@ -112,7 +113,8 @@ export default Vue.extend({
created() {
this.$root.getMeta().then(meta => {
this.enableLocalTimeline = !meta.disableLocalTimeline;
this.enableLocalTimeline = !meta.disableLocalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin;
this.enableGlobalTimeline = !meta.disableGlobalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin;
});
if (this.$store.state.device.tl) {
@@ -34,6 +34,7 @@ export default Vue.extend({
this.$root.api('users/notes', {
userId: this.user.id,
fileType: image,
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
limit: 9,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => {
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

+1
View File
@@ -16,5 +16,6 @@
renoteGradient: '#ffe1c7',
renoteText: '$primary',
quoteBorder: '$primary',
mfmMention: '#56907b',
},
}
+1 -1
View File
@@ -205,7 +205,7 @@ const mfm = P.createLanguage({
hashtag: r =>
P((input, i) => {
const text = input.substr(i);
const match = text.match(/^#([^\s\.,!\?#]+)/i);
const match = text.match(/^#([^\s\.,!\?#:]+)/i);
if (!match) return P.makeFailure(i, 'not a hashtag');
let hashtag = match[1];
hashtag = removeOrphanedBrackets(hashtag);
+1
View File
@@ -184,6 +184,7 @@ export type IMeta = {
disableRegistration?: boolean;
disableLocalTimeline?: boolean;
disableGlobalTimeline?: boolean;
hidedTags?: string[];
mascotImageUrl?: string;
bannerUrl?: string;
@@ -0,0 +1,123 @@
import $ from 'cafy';
import User, { pack } from '../../../../models/user';
import define from '../../define';
export const meta = {
requireCredential: true,
requireModerator: true,
params: {
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
offset: {
validator: $.num.optional.min(0),
default: 0
},
sort: {
validator: $.str.optional.or([
'+follower',
'-follower',
'+createdAt',
'-createdAt',
'+updatedAt',
'-updatedAt',
]),
},
state: {
validator: $.str.optional.or([
'all',
'admin',
'moderator',
'adminOrModerator',
'verified',
'suspended',
]),
default: 'all'
},
origin: {
validator: $.str.optional.or([
'combined',
'local',
'remote',
]),
default: 'local'
}
}
};
export default define(meta, (ps, me) => new Promise(async (res, rej) => {
let _sort;
if (ps.sort) {
if (ps.sort == '+follower') {
_sort = {
followersCount: -1
};
} else if (ps.sort == '-follower') {
_sort = {
followersCount: 1
};
} else if (ps.sort == '+createdAt') {
_sort = {
createdAt: -1
};
} else if (ps.sort == '+updatedAt') {
_sort = {
updatedAt: -1
};
} else if (ps.sort == '-createdAt') {
_sort = {
createdAt: 1
};
} else if (ps.sort == '-updatedAt') {
_sort = {
updatedAt: 1
};
}
} else {
_sort = {
_id: -1
};
}
const q = {
$and: []
} as any;
// state
q.$and.push(
ps.state == 'admin' ? { isAdmin: true } :
ps.state == 'moderator' ? { isModerator: true } :
ps.state == 'adminOrModerator' ? {
$or: [{
isAdmin: true
}, {
isModerator: true
}]
} :
ps.state == 'verified' ? { isVerified: true } :
ps.state == 'suspended' ? { isSuspended: true } :
{}
);
// origin
q.$and.push(
ps.origin == 'local' ? { host: null } :
ps.origin == 'remote' ? { host: { $ne: null } } :
{}
);
const users = await User
.find(q, {
limit: ps.limit,
sort: _sort,
skip: ps.offset
});
res(await Promise.all(users.map(user => pack(user, me, { detail: true }))));
}));
@@ -37,6 +37,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
return rej('cannot suspend admin');
}
if (user.isModerator) {
return rej('cannot suspend moderator');
}
await User.findOneAndUpdate({
_id: user._id
}, {
@@ -32,6 +32,13 @@ export const meta = {
}
},
disableGlobalTimeline: {
validator: $.bool.optional.nullable,
desc: {
'ja-JP': 'グローバルタイムラインを無効にするか否か'
}
},
hidedTags: {
validator: $.arr($.str).optional.nullable,
desc: {
@@ -331,6 +338,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
set.disableLocalTimeline = ps.disableLocalTimeline;
}
if (typeof ps.disableGlobalTimeline === 'boolean') {
set.disableGlobalTimeline = ps.disableGlobalTimeline;
}
if (Array.isArray(ps.hidedTags)) {
set.hidedTags = ps.hidedTags;
}
+2
View File
@@ -59,6 +59,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
broadcasts: instance.broadcasts || [],
disableRegistration: instance.disableRegistration,
disableLocalTimeline: instance.disableLocalTimeline,
disableGlobalTimeline: instance.disableGlobalTimeline,
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
cacheRemoteFiles: instance.cacheRemoteFiles,
@@ -81,6 +82,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
response.features = {
registration: !instance.disableRegistration,
localTimeLine: !instance.disableLocalTimeline,
globalTimeLine: !instance.disableGlobalTimeline,
elasticsearch: config.elasticsearch ? true : false,
recaptcha: instance.enableRecaptcha,
objectStorage: config.drive && config.drive.storage === 'minio',
@@ -4,6 +4,7 @@ import Mute from '../../../../models/mute';
import { packMany } from '../../../../models/note';
import define from '../../define';
import { countIf } from '../../../../prelude/array';
import fetchMeta from '../../../../misc/fetch-meta';
export const meta = {
desc: {
@@ -51,6 +52,13 @@ export const meta = {
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
const meta = await fetchMeta();
if (meta.disableGlobalTimeline) {
if (user == null || (!user.isAdmin && !user.isModerator)) {
return rej('global timeline disabled');
}
}
// Check if only one of sinceId, untilId, sinceDate, untilDate specified
if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) {
return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified');
@@ -5,6 +5,7 @@ import { getFriends } from '../../common/get-friends';
import { packMany } from '../../../../models/note';
import define from '../../define';
import { countIf } from '../../../../prelude/array';
import fetchMeta from '../../../../misc/fetch-meta';
export const meta = {
desc: {
@@ -91,6 +92,11 @@ export const meta = {
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
const meta = await fetchMeta();
if (meta.disableLocalTimeline && !user.isAdmin && !user.isModerator) {
return rej('local timeline disabled');
}
// Check if only one of sinceId, untilId, sinceDate, untilDate specified
if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) {
return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified');
@@ -4,6 +4,7 @@ import Mute from '../../../../models/mute';
import { packMany } from '../../../../models/note';
import define from '../../define';
import { countIf } from '../../../../prelude/array';
import fetchMeta from '../../../../misc/fetch-meta';
export const meta = {
desc: {
@@ -66,6 +67,13 @@ export const meta = {
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
const meta = await fetchMeta();
if (meta.disableLocalTimeline) {
if (user == null || (!user.isAdmin && !user.isModerator)) {
return rej('local timeline disabled');
}
}
// Check if only one of sinceId, untilId, sinceDate, untilDate specified
if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) {
return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified');
+14
View File
@@ -124,6 +124,14 @@ export const meta = {
'ja-JP': '指定された種類のファイルが添付された投稿のみを取得します'
}
},
excludeNsfw: {
validator: $.bool.optional,
default: false,
desc: {
'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)'
}
},
}
};
@@ -233,6 +241,12 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
query['_files.contentType'] = {
$in: ps.fileType
};
if (ps.excludeNsfw) {
query['_files.metadata.isSensitive'] = {
$ne: true
};
}
}
//#endregion
@@ -3,6 +3,7 @@ import Mute from '../../../../models/mute';
import { pack } from '../../../../models/note';
import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
import fetchMeta from '../../../../misc/fetch-meta';
export default class extends Channel {
public readonly chName = 'globalTimeline';
@@ -13,6 +14,11 @@ export default class extends Channel {
@autobind
public async init(params: any) {
const meta = await fetchMeta();
if (meta.disableGlobalTimeline) {
if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return;
}
// Subscribe events
this.subscriber.on('globalTimeline', this.onNote);
@@ -3,6 +3,7 @@ import Mute from '../../../../models/mute';
import { pack } from '../../../../models/note';
import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
import fetchMeta from '../../../../misc/fetch-meta';
export default class extends Channel {
public readonly chName = 'hybridTimeline';
@@ -13,6 +14,9 @@ export default class extends Channel {
@autobind
public async init(params: any) {
const meta = await fetchMeta();
if (meta.disableLocalTimeline && !this.user.isAdmin && !this.user.isModerator) return;
// Subscribe events
this.subscriber.on('hybridTimeline', this.onNewNote);
this.subscriber.on(`hybridTimeline:${this.user._id}`, this.onNewNote);
@@ -3,6 +3,7 @@ import Mute from '../../../../models/mute';
import { pack } from '../../../../models/note';
import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
import fetchMeta from '../../../../misc/fetch-meta';
export default class extends Channel {
public readonly chName = 'localTimeline';
@@ -13,6 +14,11 @@ export default class extends Channel {
@autobind
public async init(params: any) {
const meta = await fetchMeta();
if (meta.disableLocalTimeline) {
if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return;
}
// Subscribe events
this.subscriber.on('localTimeline', this.onNote);
+24
View File
@@ -2,6 +2,7 @@
* Web Client Server
*/
import * as os from 'os';
import ms = require('ms');
import * as Koa from 'koa';
import * as Router from 'koa-router';
@@ -18,6 +19,8 @@ import config from '../../config';
import Note, { pack as packNote } from '../../models/note';
import getNoteSummary from '../../misc/get-note-summary';
import fetchMeta from '../../misc/fetch-meta';
import Emoji from '../../models/emoji';
const pkg = require('../../../package.json');
const client = `${__dirname}/../../client/`;
@@ -195,6 +198,27 @@ router.get('/notes/:note', async ctx => {
});
//#endregion
router.get('/info', async ctx => {
const meta = await fetchMeta();
const emojis = await Emoji.find({ host: null }, {
fields: {
_id: false
}
});
await ctx.render('info', {
version: pkg.version,
machine: os.hostname(),
os: os.platform(),
node: process.version,
cpu: {
model: os.cpus()[0].model,
cores: os.cpus().length
},
emojis: emojis,
meta: meta
});
});
// Render base html for all requests
router.get('*', async ctx => {
const meta = await fetchMeta();
+128
View File
@@ -0,0 +1,128 @@
doctype html
html
head
meta(charset='utf-8')
meta(name='application-name' content='Misskey')
title Misskey
style.
html {
font-family: sans-serif;
}
main {
max-width: 934px;
margin: 0 auto;
}
header {
padding: 5px;
background: rgb(153, 153, 204);
border: 1px solid #000;
}
header:after {
content: '';
display: block;
clear: both;
}
header > h1 {
float: left;
font-size: 2em;
}
header > img {
float: right;
width: 220px;
}
table {
margin: 1em 0;
width: 100%;
border-collapse: collapse;
}
table tr th {
background-color: #ccf;
border: 1px solid #000;
width: 300px;
font-weight: bold;
padding: 4px 5px;
text-align: left;
}
table tr td {
background-color: #ddd;
border: 1px solid #000;
padding: 4px 5px;
}
body
main
header
h1 Misskey Version #{version}
img(src='/assets/misskey-php-like-logo.png' alt='')
table
tr
th Instance
td= meta.name
tr
th Description
td= meta.description
tr
th Maintainer
td
= meta.maintainer.name
| &lt;#{meta.maintainer.email}&gt;
tr
th System
td= os
tr
th Node version
td= node
tr
th Machine
td= machine
tr
th CPU
td= cpu.model
tr
th Original users
td= meta.stats.originalUsersCount
tr
th Original notes
td= meta.stats.originalNotesCount
tr
th Registration
td= !meta.disableRegistration ? 'yes' : 'no'
tr
th reCAPTCHA enabled
td= meta.enableRecaptcha ? 'enabled' : 'disabled'
tr
th LTL(STL) enabled
td= !meta.disableLocalTimeline ? 'enabled' : 'disabled'
tr
th GTL enabled
td= !meta.disableGlobalTimeline ? 'enabled' : 'disabled'
tr
th Cache remote files
td= meta.cacheRemoteFiles ? 'yes' : 'no'
tr
th Drive capacity per local user
td
= meta.localDriveCapacityMb
| MB
tr
th Drive capacity per remote user
td
= meta.remoteDriveCapacityMb
| MB
tr
th Max text length
td= meta.maxNoteTextLength
tr
th Emojis
td
each emoji in emojis
| :#{emoji.name}:
= ' '
+4 -1
View File
@@ -4,10 +4,11 @@ import * as fs from 'fs';
import * as mongodb from 'mongodb';
import * as crypto from 'crypto';
import * as debug from 'debug';
import fileType = require('file-type');
import * as Minio from 'minio';
import * as uuid from 'uuid';
import * as sharp from 'sharp';
import * as fileType from 'file-type';
import * as isSvg from 'is-svg';
import DriveFile, { IMetadata, getDriveFileBucket, IDriveFile } from '../../models/drive-file';
import DriveFolder from '../../models/drive-folder';
@@ -320,6 +321,8 @@ export default async function(
const type = fileType(buffer);
if (type) {
res([type.mime, type.ext]);
} else if (isSvg(buffer)) {
res(['image/svg+xml', 'svg'])
} else {
// 種類が同定できなかったら application/octet-stream にする
res(['application/octet-stream', null]);
+1 -6
View File
@@ -183,11 +183,6 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
data.visibleUsers.push(u);
}
}
// ダイレクト投稿でユーザーが指定されていなかったらreject
if (data.visibleUsers.length === 0) {
return rej('Target user is not specified');
}
}
const note = await insertNote(user, data, tags, emojis, mentionedUsers);
@@ -546,7 +541,7 @@ async function publishToFollowers(note: INote, user: IUser, noteActivity: any) {
if (!following.stalk) {
// この投稿が返信ならスキップ
if (note.replyId && !note._reply.userId.equals(following.followerId) && !note._reply.userId.equals(note.userId))
return;
continue;
}
// Publish event to followers stream
-18
View File
@@ -1,31 +1,17 @@
import * as mongo from 'mongodb';
import redis from './db/redis';
import Xev from 'xev';
import { IMeta } from './models/meta';
import fetchMeta from './misc/fetch-meta';
type ID = string | mongo.ObjectID;
class Publisher {
private ev: Xev;
private meta: IMeta;
constructor() {
// Redisがインストールされてないときはプロセス間通信を使う
if (redis == null) {
this.ev = new Xev();
}
setInterval(async () => {
this.meta = await fetchMeta();
}, 5000);
}
public fetchMeta = async () => {
if (this.meta != null) return this.meta;
this.meta = await fetchMeta();
return this.meta;
}
private publish = (channel: string, type: string, value?: any): void => {
@@ -83,14 +69,10 @@ class Publisher {
}
public publishLocalTimelineStream = async (note: any): Promise<void> => {
const meta = await this.fetchMeta();
if (meta.disableLocalTimeline) return;
this.publish('localTimeline', null, note);
}
public publishHybridTimelineStream = async (userId: ID, note: any): Promise<void> => {
const meta = await this.fetchMeta();
if (meta.disableLocalTimeline) return;
this.publish(userId ? `hybridTimeline:${userId}` : 'hybridTimeline', null, note);
}
+57
View File
@@ -0,0 +1,57 @@
// node built/tools/show-signin-history username
// => {Success} {Date} {IPAddrsss}
// node built/tools/show-signin-history username user-agent,x-forwarded-for
// with user-agent and x-forwarded-for
// node built/tools/show-signin-history username all
// with full request headers
import User from '../models/user';
import Signin from '../models/signin';
async function main(username: string, headers: string[]) {
const user = await User.findOne({
host: null,
usernameLower: username.toLowerCase(),
});
if (user === null) throw 'User not found';
const history = await Signin.find({
userId: user._id
});
for (const signin of history) {
console.log(`${signin.success ? 'OK' : 'NG'} ${signin.createdAt ? signin.createdAt.toISOString() : 'Unknown'} ${signin.ip}`);
// headers
if (headers != null) {
for (const key of Object.keys(signin.headers)) {
if (headers.includes('all') || headers.includes(key)) {
console.log(` ${key}: ${signin.headers[key]}`);
}
}
}
}
}
// get args
const args = process.argv.slice(2);
let username = args[0];
let headers: string[];
if (args[1] != null) {
headers = args[1].split(/,/).map(header => header.toLowerCase());
}
// normalize args
username = username.replace(/^@/, '');
main(username, headers).then(() => {
process.exit(0);
}).catch(e => {
console.warn(e);
process.exit(1);
});
+14
View File
@@ -808,6 +808,20 @@ describe('API', () => {
expect(res).have.status(400);
}));
it('SVGファイルを作成できる', async(async () => {
const izumi = await signup({ username: 'izumi' });
const res = await assert.request(server)
.post('/drive/files/create')
.field('i', izumi.token)
.attach('file', fs.readFileSync(__dirname + '/resources/image.svg'), 'image.svg');
expect(res).have.status(200);
expect(res.body).be.a('object');
expect(res.body).have.property('name').eql('image.svg');
expect(res.body).have.property('type').eql('image/svg+xml');
}));
});
describe('drive/files/update', () => {
+8
View File
@@ -357,6 +357,14 @@ describe('MFM', () => {
]);
});
it('ignore colon', () => {
const tokens = analyze('#Foo:');
assert.deepStrictEqual(tokens, [
leaf('hashtag', { hashtag: 'Foo' }),
text(':'),
]);
});
it('allow including number', () => {
const tokens = analyze('#foo123');
assert.deepStrictEqual(tokens, [
Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B