Merge branch 'develop'
This commit is contained in:
		
						commit
						796252357e
					
				
					 27 changed files with 268 additions and 235 deletions
				
			
		| 
						 | 
					@ -86,6 +86,7 @@ drive:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# OR
 | 
					# OR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#drive:
 | 
				
			||||||
#  storage: 'minio'
 | 
					#  storage: 'minio'
 | 
				
			||||||
#  bucket:
 | 
					#  bucket:
 | 
				
			||||||
#  prefix:
 | 
					#  prefix:
 | 
				
			||||||
| 
						 | 
					@ -96,25 +97,38 @@ drive:
 | 
				
			||||||
#    accessKey:
 | 
					#    accessKey:
 | 
				
			||||||
#    secretKey:
 | 
					#    secretKey:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # S3 example
 | 
					# S3/GCS example
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# * Replace <endpoint> to
 | 
				
			||||||
 | 
					#     S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
 | 
				
			||||||
 | 
					#     GCS: use 'storage.googleapis.com'
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# * Replace <region> to
 | 
				
			||||||
 | 
					#     S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
 | 
				
			||||||
 | 
					#     GCS: not needed (just delete the region line)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#drive:
 | 
				
			||||||
#  storage: 'minio'
 | 
					#  storage: 'minio'
 | 
				
			||||||
#  bucket: bucket-name
 | 
					#  bucket: bucket-name
 | 
				
			||||||
#  prefix: files
 | 
					#  prefix: files
 | 
				
			||||||
 | 
					#  baseUrl: https://bucket-name.<endpoint>
 | 
				
			||||||
#  config:
 | 
					#  config:
 | 
				
			||||||
  #   endPoint: s3-us-west-2.amazonaws.com
 | 
					#    endPoint: <endpoint>
 | 
				
			||||||
  #   region: us-west-2
 | 
					#    region: <region>
 | 
				
			||||||
#    useSSL: true
 | 
					#    useSSL: true
 | 
				
			||||||
#    accessKey: XXX
 | 
					#    accessKey: XXX
 | 
				
			||||||
#    secretKey: YYY
 | 
					#    secretKey: YYY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # S3 example (with CDN, custom domain)
 | 
					# S3/GCS example (with CDN, custom domain)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#drive:
 | 
				
			||||||
#  storage: 'minio'
 | 
					#  storage: 'minio'
 | 
				
			||||||
#  bucket: drive.example.com
 | 
					#  bucket: drive.example.com
 | 
				
			||||||
#  prefix: files
 | 
					#  prefix: files
 | 
				
			||||||
#  baseUrl: https://drive.example.com
 | 
					#  baseUrl: https://drive.example.com
 | 
				
			||||||
#  config:
 | 
					#  config:
 | 
				
			||||||
  #   endPoint: s3-us-west-2.amazonaws.com
 | 
					#    endPoint: <endpoint>
 | 
				
			||||||
  #   region: us-west-2
 | 
					#    region: <region>
 | 
				
			||||||
#    useSSL: true
 | 
					#    useSSL: true
 | 
				
			||||||
#    accessKey: XXX
 | 
					#    accessKey: XXX
 | 
				
			||||||
#    secretKey: YYY
 | 
					#    secretKey: YYY
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										20
									
								
								CHANGELOG.md
									
										
									
									
									
								
							| 
						 | 
					@ -73,6 +73,26 @@ mongodb:
 | 
				
			||||||
8. master ブランチに戻す
 | 
					8. master ブランチに戻す
 | 
				
			||||||
9. enjoy
 | 
					9. enjoy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					11.12.0 (2019/05/10)
 | 
				
			||||||
 | 
					--------------------
 | 
				
			||||||
 | 
					### 注意
 | 
				
			||||||
 | 
					このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Improvements
 | 
				
			||||||
 | 
					* インスタンス運営者がおすすめアカウントを設定できるように
 | 
				
			||||||
 | 
					* MisskeyPagesでNAME環境変数がNULLにならないように
 | 
				
			||||||
 | 
					* MisskeyPagesにNULL環境変数を追加
 | 
				
			||||||
 | 
					* MisskeyPagesで変数を並べ替えられるように
 | 
				
			||||||
 | 
					* MisskeyPagesのテキストのリスト内で変数埋め込みできるように
 | 
				
			||||||
 | 
					* 自分の指定した投稿のRenoteを全て解除するAPIを追加
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Fixes
 | 
				
			||||||
 | 
					* Noteをpull取得した時にhost名がvalidateされていない問題を修正
 | 
				
			||||||
 | 
					* みつけるで人気のタグが表示されない問題を修正
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### その他
 | 
				
			||||||
 | 
					* アカウントのisVerifiedフラグを廃止
 | 
				
			||||||
 | 
					
 | 
				
			||||||
11.11.2 (2019/05/07)
 | 
					11.11.2 (2019/05/07)
 | 
				
			||||||
--------------------
 | 
					--------------------
 | 
				
			||||||
### Fixes
 | 
					### Fixes
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
					@ -105,6 +105,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
 | 
				
			||||||
<table><tr>
 | 
					<table><tr>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
 | 
				
			||||||
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
 | 
				
			||||||
| 
						 | 
					@ -112,6 +113,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
 | 
				
			||||||
</tr><tr>
 | 
					</tr><tr>
 | 
				
			||||||
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
 | 
					<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
 | 
					<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
 | 
				
			||||||
 | 
					<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
 | 
					<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
 | 
					<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
 | 
					<td><a href="https://www.patreon.com/osapon">osapon</a></td>
 | 
				
			||||||
| 
						 | 
					@ -127,7 +129,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
 | 
				
			||||||
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
 | 
					<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
 | 
					 | 
				
			||||||
</tr><tr>
 | 
					</tr><tr>
 | 
				
			||||||
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
 | 
					<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
 | 
					<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
 | 
				
			||||||
| 
						 | 
					@ -138,9 +139,9 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
 | 
				
			||||||
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
 | 
					<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td>
 | 
					<td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s</a></td>
 | 
					<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
 | 
					 | 
				
			||||||
</tr></table>
 | 
					</tr></table>
 | 
				
			||||||
<table><tr>
 | 
					<table><tr>
 | 
				
			||||||
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpe?token-time=2145916800&token-hash=CPxGQhKIlEaa6WUcgbyHixyKEhakiw9RFdOhsIJBQ_o%3D" alt="takimura" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpe?token-time=2145916800&token-hash=CPxGQhKIlEaa6WUcgbyHixyKEhakiw9RFdOhsIJBQ_o%3D" alt="takimura" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4.jpe?token-time=2145916800&token-hash=UslrPVM-8TXOe8AapuNiaFYjcIJgPNcU-fKpGbfGJNI%3D" alt="Damillora" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4.jpe?token-time=2145916800&token-hash=UslrPVM-8TXOe8AapuNiaFYjcIJgPNcU-fKpGbfGJNI%3D" alt="Damillora" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
 | 
				
			||||||
| 
						 | 
					@ -149,8 +150,8 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1.jpeg?token-time=2145916800&token-hash=d8jBQLMOHD87KtXs5C9fk1o58DMF73pQ-dYH3uZJPBE%3D" alt="Gargron" width="100"></td>
 | 
					 | 
				
			||||||
</tr><tr>
 | 
					</tr><tr>
 | 
				
			||||||
 | 
					<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
 | 
					<td><a href="https://www.patreon.com/takimura">takimura</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/damillora">Damillora</a></td>
 | 
					<td><a href="https://www.patreon.com/damillora">Damillora</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
 | 
					<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
 | 
				
			||||||
| 
						 | 
					@ -159,17 +160,18 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
 | 
				
			||||||
<td><a href="https://www.patreon.com/Corset">CG</a></td>
 | 
					<td><a href="https://www.patreon.com/Corset">CG</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
 | 
					<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
 | 
					<td><a href="https://www.patreon.com/dansup">dansup</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
 | 
					 | 
				
			||||||
</tr></table>
 | 
					</tr></table>
 | 
				
			||||||
<table><tr>
 | 
					<table><tr>
 | 
				
			||||||
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1.jpeg?token-time=2145916800&token-hash=d8jBQLMOHD87KtXs5C9fk1o58DMF73pQ-dYH3uZJPBE%3D" alt="Gargron" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
 | 
				
			||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
 | 
					<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
 | 
				
			||||||
</tr><tr>
 | 
					</tr><tr>
 | 
				
			||||||
 | 
					<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
 | 
					<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
 | 
				
			||||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
 | 
					<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
 | 
				
			||||||
</tr></table>
 | 
					</tr></table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Last updated:** Fri, 03 May 2019 05:33:07 UTC
 | 
					**Last updated:** Tue, 07 May 2019 11:55:07 UTC
 | 
				
			||||||
<!-- PATREON_END -->
 | 
					<!-- PATREON_END -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:four_leaf_clover: Copyright
 | 
					:four_leaf_clover: Copyright
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -263,7 +263,6 @@ common:
 | 
				
			||||||
  update-available-title: "更新があります"
 | 
					  update-available-title: "更新があります"
 | 
				
			||||||
  update-available: "Misskeyの新しいバージョンがあります({newer}。現在{current}を利用中)。ページを再度読み込みすると更新が適用されます。"
 | 
					  update-available: "Misskeyの新しいバージョンがあります({newer}。現在{current}を利用中)。ページを再度読み込みすると更新が適用されます。"
 | 
				
			||||||
  my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。"
 | 
					  my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。"
 | 
				
			||||||
  verified-user: "公式アカウント"
 | 
					 | 
				
			||||||
  hide-password: "パスワードを隠す"
 | 
					  hide-password: "パスワードを隠す"
 | 
				
			||||||
  show-password: "パスワードを表示する"
 | 
					  show-password: "パスワードを表示する"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -339,7 +338,7 @@ auth/views/index.vue:
 | 
				
			||||||
  sign-in: "サインインしてください"
 | 
					  sign-in: "サインインしてください"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
common/views/pages/explore.vue:
 | 
					common/views/pages/explore.vue:
 | 
				
			||||||
  verified-users: "公式アカウント"
 | 
					  pinned-users: "ピン留めされたユーザー"
 | 
				
			||||||
  popular-users: "人気のユーザー"
 | 
					  popular-users: "人気のユーザー"
 | 
				
			||||||
  recently-updated-users: "最近投稿したユーザー"
 | 
					  recently-updated-users: "最近投稿したユーザー"
 | 
				
			||||||
  recently-registered-users: "新規ユーザー"
 | 
					  recently-registered-users: "新規ユーザー"
 | 
				
			||||||
| 
						 | 
					@ -1264,7 +1263,7 @@ admin/views/instance.vue:
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  save: "保存"
 | 
					  save: "保存"
 | 
				
			||||||
  saved: "保存しました"
 | 
					  saved: "保存しました"
 | 
				
			||||||
  user-recommendation-config: "おすすめユーザー"
 | 
					  pinned-users: "ピン留めユーザー"
 | 
				
			||||||
  email-config: "メールサーバーの設定"
 | 
					  email-config: "メールサーバーの設定"
 | 
				
			||||||
  email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
 | 
					  email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
 | 
				
			||||||
  enable-email: "メール配信を有効にする"
 | 
					  enable-email: "メール配信を有効にする"
 | 
				
			||||||
| 
						 | 
					@ -1351,12 +1350,6 @@ admin/views/users.vue:
 | 
				
			||||||
  silence-confirm: "サイレンスしますか?"
 | 
					  silence-confirm: "サイレンスしますか?"
 | 
				
			||||||
  unmake-silence: "サイレンスの解除"
 | 
					  unmake-silence: "サイレンスの解除"
 | 
				
			||||||
  unsilence-confirm: "サイレンスを解除しますか?"
 | 
					  unsilence-confirm: "サイレンスを解除しますか?"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					 | 
				
			||||||
  verify-confirm: "公式アカウントにしますか?"
 | 
					 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					 | 
				
			||||||
  unverify-confirm: "公式アカウントを解除しますか?"
 | 
					 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					 | 
				
			||||||
  update-remote-user: "リモートユーザー情報の更新"
 | 
					  update-remote-user: "リモートユーザー情報の更新"
 | 
				
			||||||
  remote-user-updated: "リモートユーザー情報を更新しました"
 | 
					  remote-user-updated: "リモートユーザー情報を更新しました"
 | 
				
			||||||
  users:
 | 
					  users:
 | 
				
			||||||
| 
						 | 
					@ -1373,7 +1366,6 @@ admin/views/users.vue:
 | 
				
			||||||
      admin: "管理者"
 | 
					      admin: "管理者"
 | 
				
			||||||
      moderator: "モデレーター"
 | 
					      moderator: "モデレーター"
 | 
				
			||||||
      adminOrModerator: "管理者+モデレーター"
 | 
					      adminOrModerator: "管理者+モデレーター"
 | 
				
			||||||
      verified: "公式アカウント"
 | 
					 | 
				
			||||||
      silenced: "サイレンス済み"
 | 
					      silenced: "サイレンス済み"
 | 
				
			||||||
      suspended: "凍結済み"
 | 
					      suspended: "凍結済み"
 | 
				
			||||||
    origin:
 | 
					    origin:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								migration/1557476068003-PinnedUsers.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								migration/1557476068003-PinnedUsers.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					import {MigrationInterface, QueryRunner} from "typeorm";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class PinnedUsers1557476068003 implements MigrationInterface {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async up(queryRunner: QueryRunner): Promise<any> {
 | 
				
			||||||
 | 
					        await queryRunner.query(`ALTER TABLE "meta" ADD "pinnedUsers" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async down(queryRunner: QueryRunner): Promise<any> {
 | 
				
			||||||
 | 
					        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedUsers"`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	"name": "misskey",
 | 
						"name": "misskey",
 | 
				
			||||||
	"author": "syuilo <i@syuilo.com>",
 | 
						"author": "syuilo <i@syuilo.com>",
 | 
				
			||||||
	"version": "11.11.2",
 | 
						"version": "11.12.0",
 | 
				
			||||||
	"codename": "daybreak",
 | 
						"codename": "daybreak",
 | 
				
			||||||
	"repository": {
 | 
						"repository": {
 | 
				
			||||||
		"type": "git",
 | 
							"type": "git",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,6 +82,14 @@
 | 
				
			||||||
		</section>
 | 
							</section>
 | 
				
			||||||
	</ui-card>
 | 
						</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<template #title>{{ $t('pinned-users') }}</template>
 | 
				
			||||||
 | 
							<section>
 | 
				
			||||||
 | 
								<ui-textarea v-model="pinnedUsers"></ui-textarea>
 | 
				
			||||||
 | 
								<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<ui-card>
 | 
						<ui-card>
 | 
				
			||||||
		<template #title>{{ $t('invite') }}</template>
 | 
							<template #title>{{ $t('invite') }}</template>
 | 
				
			||||||
		<section>
 | 
							<section>
 | 
				
			||||||
| 
						 | 
					@ -190,6 +198,7 @@ export default Vue.extend({
 | 
				
			||||||
			enableServiceWorker: false,
 | 
								enableServiceWorker: false,
 | 
				
			||||||
			swPublicKey: null,
 | 
								swPublicKey: null,
 | 
				
			||||||
			swPrivateKey: null,
 | 
								swPrivateKey: null,
 | 
				
			||||||
 | 
								pinnedUsers: [],
 | 
				
			||||||
			faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope, faBolt
 | 
								faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope, faBolt
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					@ -239,6 +248,7 @@ export default Vue.extend({
 | 
				
			||||||
			this.enableServiceWorker = meta.enableServiceWorker;
 | 
								this.enableServiceWorker = meta.enableServiceWorker;
 | 
				
			||||||
			this.swPublicKey = meta.swPublickey;
 | 
								this.swPublicKey = meta.swPublickey;
 | 
				
			||||||
			this.swPrivateKey = meta.swPrivateKey;
 | 
								this.swPrivateKey = meta.swPrivateKey;
 | 
				
			||||||
 | 
								this.pinnedUsers = meta.pinnedUsers.join('\n');
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -297,7 +307,8 @@ export default Vue.extend({
 | 
				
			||||||
				smtpPass: this.smtpAuth ? this.smtpPass : '',
 | 
									smtpPass: this.smtpAuth ? this.smtpPass : '',
 | 
				
			||||||
				enableServiceWorker: this.enableServiceWorker,
 | 
									enableServiceWorker: this.enableServiceWorker,
 | 
				
			||||||
				swPublicKey: this.swPublicKey,
 | 
									swPublicKey: this.swPublicKey,
 | 
				
			||||||
				swPrivateKey: this.swPrivateKey
 | 
									swPrivateKey: this.swPrivateKey,
 | 
				
			||||||
 | 
									pinnedUsers: this.pinnedUsers.split('\n')
 | 
				
			||||||
			}).then(() => {
 | 
								}).then(() => {
 | 
				
			||||||
				this.$root.dialog({
 | 
									this.$root.dialog({
 | 
				
			||||||
					type: 'success',
 | 
										type: 'success',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,6 @@
 | 
				
			||||||
			<span class="username">@{{ user | acct }}</span>
 | 
								<span class="username">@{{ user | acct }}</span>
 | 
				
			||||||
			<span class="is-admin" v-if="user.isAdmin">admin</span>
 | 
								<span class="is-admin" v-if="user.isAdmin">admin</span>
 | 
				
			||||||
			<span class="is-moderator" v-if="user.isModerator">moderator</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-silenced" v-if="user.isSilenced" :title="$t('@.silenced-user')"><fa :icon="faMicrophoneSlash"/></span>
 | 
								<span class="is-silenced" v-if="user.isSilenced" :title="$t('@.silenced-user')"><fa :icon="faMicrophoneSlash"/></span>
 | 
				
			||||||
			<span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span>
 | 
								<span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span>
 | 
				
			||||||
		</header>
 | 
							</header>
 | 
				
			||||||
| 
						 | 
					@ -77,7 +76,6 @@ export default Vue.extend({
 | 
				
			||||||
				background var(--noteHeaderAdminBg)
 | 
									background var(--noteHeaderAdminBg)
 | 
				
			||||||
				color var(--noteHeaderAdminFg)
 | 
									color var(--noteHeaderAdminFg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			> .is-verified
 | 
					 | 
				
			||||||
			> .is-silenced
 | 
								> .is-silenced
 | 
				
			||||||
			> .is-suspended
 | 
								> .is-suspended
 | 
				
			||||||
				margin 0 0 0 .5em
 | 
									margin 0 0 0 .5em
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,10 +12,6 @@
 | 
				
			||||||
				<x-user :user='user'/>
 | 
									<x-user :user='user'/>
 | 
				
			||||||
				<div class="actions">
 | 
									<div class="actions">
 | 
				
			||||||
					<ui-button @click="resetPassword"><fa :icon="faKey"/> {{ $t('reset-password') }}</ui-button>
 | 
										<ui-button @click="resetPassword"><fa :icon="faKey"/> {{ $t('reset-password') }}</ui-button>
 | 
				
			||||||
					<ui-horizon-group>
 | 
					 | 
				
			||||||
						<ui-button @click="verifyUser" :disabled="verifying"><fa :icon="faCertificate"/> {{ $t('verify') }}</ui-button>
 | 
					 | 
				
			||||||
						<ui-button @click="unverifyUser" :disabled="unverifying">{{ $t('unverify') }}</ui-button>
 | 
					 | 
				
			||||||
					</ui-horizon-group>
 | 
					 | 
				
			||||||
					<ui-horizon-group>
 | 
										<ui-horizon-group>
 | 
				
			||||||
						<ui-button @click="silenceUser"><fa :icon="faMicrophoneSlash"/> {{ $t('make-silence') }}</ui-button>
 | 
											<ui-button @click="silenceUser"><fa :icon="faMicrophoneSlash"/> {{ $t('make-silence') }}</ui-button>
 | 
				
			||||||
						<ui-button @click="unsilenceUser">{{ $t('unmake-silence') }}</ui-button>
 | 
											<ui-button @click="unsilenceUser">{{ $t('unmake-silence') }}</ui-button>
 | 
				
			||||||
| 
						 | 
					@ -47,7 +43,6 @@
 | 
				
			||||||
					<option value="all">{{ $t('users.state.all') }}</option>
 | 
										<option value="all">{{ $t('users.state.all') }}</option>
 | 
				
			||||||
					<option value="admin">{{ $t('users.state.admin') }}</option>
 | 
										<option value="admin">{{ $t('users.state.admin') }}</option>
 | 
				
			||||||
					<option value="moderator">{{ $t('users.state.moderator') }}</option>
 | 
										<option value="moderator">{{ $t('users.state.moderator') }}</option>
 | 
				
			||||||
					<option value="verified">{{ $t('users.state.verified') }}</option>
 | 
					 | 
				
			||||||
					<option value="silenced">{{ $t('users.state.silenced') }}</option>
 | 
										<option value="silenced">{{ $t('users.state.silenced') }}</option>
 | 
				
			||||||
					<option value="suspended">{{ $t('users.state.suspended') }}</option>
 | 
										<option value="suspended">{{ $t('users.state.suspended') }}</option>
 | 
				
			||||||
				</ui-select>
 | 
									</ui-select>
 | 
				
			||||||
| 
						 | 
					@ -71,7 +66,7 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../i18n';
 | 
					import i18n from '../../i18n';
 | 
				
			||||||
import parseAcct from "../../../../misc/acct/parse";
 | 
					import parseAcct from "../../../../misc/acct/parse";
 | 
				
			||||||
import { faCertificate, faUsers, faTerminal, faSearch, faKey, faSync, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
 | 
					import { faUsers, faTerminal, faSearch, faKey, faSync, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
 | 
					import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
 | 
				
			||||||
import XUser from './users.user.vue';
 | 
					import XUser from './users.user.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,8 +79,6 @@ export default Vue.extend({
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			user: null,
 | 
								user: null,
 | 
				
			||||||
			target: null,
 | 
								target: null,
 | 
				
			||||||
			verifying: false,
 | 
					 | 
				
			||||||
			unverifying: false,
 | 
					 | 
				
			||||||
			suspending: false,
 | 
								suspending: false,
 | 
				
			||||||
			unsuspending: false,
 | 
								unsuspending: false,
 | 
				
			||||||
			sort: '+createdAt',
 | 
								sort: '+createdAt',
 | 
				
			||||||
| 
						 | 
					@ -95,7 +88,7 @@ export default Vue.extend({
 | 
				
			||||||
			offset: 0,
 | 
								offset: 0,
 | 
				
			||||||
			users: [],
 | 
								users: [],
 | 
				
			||||||
			existMore: false,
 | 
								existMore: false,
 | 
				
			||||||
			faTerminal, faCertificate, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash
 | 
								faTerminal, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -181,56 +174,6 @@ export default Vue.extend({
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		async verifyUser() {
 | 
					 | 
				
			||||||
			if (!await this.getConfirmed(this.$t('verify-confirm'))) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.verifying = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const process = async () => {
 | 
					 | 
				
			||||||
				await this.$root.api('admin/verify-user', { userId: this.user.id });
 | 
					 | 
				
			||||||
				this.$root.dialog({
 | 
					 | 
				
			||||||
					type: 'success',
 | 
					 | 
				
			||||||
					text: this.$t('verified')
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			await process().catch(e => {
 | 
					 | 
				
			||||||
				this.$root.dialog({
 | 
					 | 
				
			||||||
					type: 'error',
 | 
					 | 
				
			||||||
					text: e.toString()
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.verifying = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.refreshUser();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async unverifyUser() {
 | 
					 | 
				
			||||||
			if (!await this.getConfirmed(this.$t('unverify-confirm'))) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.unverifying = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const process = async () => {
 | 
					 | 
				
			||||||
				await this.$root.api('admin/unverify-user', { userId: this.user.id });
 | 
					 | 
				
			||||||
				this.$root.dialog({
 | 
					 | 
				
			||||||
					type: 'success',
 | 
					 | 
				
			||||||
					text: this.$t('unverified')
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			await process().catch(e => {
 | 
					 | 
				
			||||||
				this.$root.dialog({
 | 
					 | 
				
			||||||
					type: 'error',
 | 
					 | 
				
			||||||
					text: e.toString()
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.unverifying = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.refreshUser();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async silenceUser() {
 | 
							async silenceUser() {
 | 
				
			||||||
			if (!await this.getConfirmed(this.$t('silence-confirm'))) return;
 | 
								if (!await this.getConfirmed(this.$t('silence-confirm'))) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,6 @@
 | 
				
			||||||
	<span class="is-bot" v-if="note.user.isBot">bot</span>
 | 
						<span class="is-bot" v-if="note.user.isBot">bot</span>
 | 
				
			||||||
	<span class="is-cat" v-if="note.user.isCat">cat</span>
 | 
						<span class="is-cat" v-if="note.user.isCat">cat</span>
 | 
				
			||||||
	<span class="username"><mk-acct :user="note.user"/></span>
 | 
						<span class="username"><mk-acct :user="note.user"/></span>
 | 
				
			||||||
	<span class="is-verified" v-if="note.user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
 | 
					 | 
				
			||||||
	<div class="info">
 | 
						<div class="info">
 | 
				
			||||||
		<span class="app" v-if="note.app && !mini && $store.state.settings.showVia">via <b>{{ note.app.name }}</b></span>
 | 
							<span class="app" v-if="note.app && !mini && $store.state.settings.showVia">via <b>{{ note.app.name }}</b></span>
 | 
				
			||||||
		<span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span>
 | 
							<span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span>
 | 
				
			||||||
| 
						 | 
					@ -95,10 +94,6 @@ export default Vue.extend({
 | 
				
			||||||
		color var(--noteHeaderAcct)
 | 
							color var(--noteHeaderAcct)
 | 
				
			||||||
		flex-shrink 2147483647
 | 
							flex-shrink 2147483647
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .is-verified
 | 
					 | 
				
			||||||
		margin 0 .5em 0 0
 | 
					 | 
				
			||||||
		color #4dabf7
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> .info
 | 
						> .info
 | 
				
			||||||
		margin-left auto
 | 
							margin-left auto
 | 
				
			||||||
		font-size 0.9em
 | 
							font-size 0.9em
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn">
 | 
					<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn" :draggable="draggable">
 | 
				
			||||||
	<template #header><fa v-if="icon" :icon="icon"/> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template>
 | 
						<template #header><fa v-if="icon" :icon="icon"/> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template>
 | 
				
			||||||
	<template #func>
 | 
						<template #func>
 | 
				
			||||||
		<button @click="changeType()">
 | 
							<button @click="changeType()">
 | 
				
			||||||
| 
						 | 
					@ -93,6 +93,10 @@ export default Vue.extend({
 | 
				
			||||||
		fnSlots: {
 | 
							fnSlots: {
 | 
				
			||||||
			required: false,
 | 
								required: false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							draggable: {
 | 
				
			||||||
 | 
								required: false,
 | 
				
			||||||
 | 
								default: false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,9 +53,8 @@
 | 
				
			||||||
	<ui-container :body-togglable="true">
 | 
						<ui-container :body-togglable="true">
 | 
				
			||||||
		<template #header><fa :icon="faMagic"/> {{ $t('variables') }}</template>
 | 
							<template #header><fa :icon="faMagic"/> {{ $t('variables') }}</template>
 | 
				
			||||||
		<div class="qmuvgica">
 | 
							<div class="qmuvgica">
 | 
				
			||||||
			<div class="variables" v-show="variables.length > 0">
 | 
								<x-draggable tag="div" class="variables" v-show="variables.length > 0" :list="variables" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5">
 | 
				
			||||||
				<template v-for="variable in variables">
 | 
									<x-variable v-for="variable in variables"
 | 
				
			||||||
					<x-variable
 | 
					 | 
				
			||||||
					:value="variable"
 | 
										:value="variable"
 | 
				
			||||||
					:removable="true"
 | 
										:removable="true"
 | 
				
			||||||
					@input="v => updateVariable(v)"
 | 
										@input="v => updateVariable(v)"
 | 
				
			||||||
| 
						 | 
					@ -64,9 +63,9 @@
 | 
				
			||||||
					:ai-script="aiScript"
 | 
										:ai-script="aiScript"
 | 
				
			||||||
					:name="variable.name"
 | 
										:name="variable.name"
 | 
				
			||||||
					:title="variable.name"
 | 
										:title="variable.name"
 | 
				
			||||||
 | 
										:draggable="true"
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
				</template>
 | 
								</x-draggable>
 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<ui-button @click="addVariable()" class="add" v-if="!readonly"><fa :icon="faPlus"/></ui-button>
 | 
								<ui-button @click="addVariable()" class="add" v-if="!readonly"><fa :icon="faPlus"/></ui-button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,6 +91,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as XDraggable from 'vuedraggable';
 | 
				
			||||||
import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
 | 
					import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
 | 
					import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
 | 
				
			||||||
import i18n from '../../../../i18n';
 | 
					import i18n from '../../../../i18n';
 | 
				
			||||||
| 
						 | 
					@ -107,7 +107,7 @@ export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('pages'),
 | 
						i18n: i18n('pages'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XVariable, XBlocks
 | 
							XDraggable, XVariable, XBlocks
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	props: {
 | 
						props: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,8 +13,8 @@
 | 
				
			||||||
		<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
 | 
							<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<div class="vxjfqztj">
 | 
							<div class="vxjfqztj">
 | 
				
			||||||
			<router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.name}`" :key="'local:' + tag.name" class="local">{{ tag.name }}</router-link>
 | 
								<router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</router-link>
 | 
				
			||||||
			<router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.name}`" :key="'remote:' + tag.name">{{ tag.name }}</router-link>
 | 
								<router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</router-link>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</ui-container>
 | 
						</ui-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,8 +26,8 @@
 | 
				
			||||||
	</mk-user-list>
 | 
						</mk-user-list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template v-if="tag == null">
 | 
						<template v-if="tag == null">
 | 
				
			||||||
		<mk-user-list :make-promise="verifiedUsers">
 | 
							<mk-user-list :make-promise="pinnedUsers">
 | 
				
			||||||
			<fa :icon="faBookmark" fixed-width/>{{ $t('verified-users') }}
 | 
								<fa :icon="faBookmark" fixed-width/>{{ $t('pinned-users') }}
 | 
				
			||||||
		</mk-user-list>
 | 
							</mk-user-list>
 | 
				
			||||||
		<mk-user-list :make-promise="popularUsers">
 | 
							<mk-user-list :make-promise="popularUsers">
 | 
				
			||||||
			<fa :icon="faChartLine" fixed-width/>{{ $t('popular-users') }}
 | 
								<fa :icon="faChartLine" fixed-width/>{{ $t('popular-users') }}
 | 
				
			||||||
| 
						 | 
					@ -60,12 +60,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			verifiedUsers: () => this.$root.api('users', {
 | 
								pinnedUsers: () => this.$root.api('pinned-users'),
 | 
				
			||||||
				state: 'verified',
 | 
					 | 
				
			||||||
				origin: 'local',
 | 
					 | 
				
			||||||
				sort: '+follower',
 | 
					 | 
				
			||||||
				limit: 10
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			popularUsers: () => this.$root.api('users', {
 | 
								popularUsers: () => this.$root.api('users', {
 | 
				
			||||||
				state: 'alive',
 | 
									state: 'alive',
 | 
				
			||||||
				origin: 'local',
 | 
									origin: 'local',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ export class ASEvaluator {
 | 
				
			||||||
			VERSION: opts.version,
 | 
								VERSION: opts.version,
 | 
				
			||||||
			URL: opts.page ? `${opts.url}/@${opts.page.user.username}/pages/${opts.page.name}` : '',
 | 
								URL: opts.page ? `${opts.url}/@${opts.page.user.username}/pages/${opts.page.name}` : '',
 | 
				
			||||||
			LOGIN: opts.visitor != null,
 | 
								LOGIN: opts.visitor != null,
 | 
				
			||||||
			NAME: opts.visitor ? opts.visitor.name : '',
 | 
								NAME: opts.visitor ? opts.visitor.name || opts.visitor.username : '',
 | 
				
			||||||
			USERNAME: opts.visitor ? opts.visitor.username : '',
 | 
								USERNAME: opts.visitor ? opts.visitor.username : '',
 | 
				
			||||||
			USERID: opts.visitor ? opts.visitor.id : '',
 | 
								USERID: opts.visitor ? opts.visitor.id : '',
 | 
				
			||||||
			NOTES_COUNT: opts.visitor ? opts.visitor.notesCount : 0,
 | 
								NOTES_COUNT: opts.visitor ? opts.visitor.notesCount : 0,
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,8 @@ export class ASEvaluator {
 | 
				
			||||||
			MY_FOLLOWERS_COUNT: opts.user ? opts.user.followersCount : 0,
 | 
								MY_FOLLOWERS_COUNT: opts.user ? opts.user.followersCount : 0,
 | 
				
			||||||
			MY_FOLLOWING_COUNT: opts.user ? opts.user.followingCount : 0,
 | 
								MY_FOLLOWING_COUNT: opts.user ? opts.user.followingCount : 0,
 | 
				
			||||||
			SEED: opts.randomSeed ? opts.randomSeed : '',
 | 
								SEED: opts.randomSeed ? opts.randomSeed : '',
 | 
				
			||||||
			YMD: `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
 | 
								YMD: `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`,
 | 
				
			||||||
 | 
								NULL: null
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,7 +105,7 @@ export class ASEvaluator {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (block.type === 'textList') {
 | 
							if (block.type === 'textList') {
 | 
				
			||||||
			return block.value.trim().split('\n');
 | 
								return this.interpolate(block.value || '', scope).trim().split('\n');
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (block.type === 'ref') {
 | 
							if (block.type === 'ref') {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,6 +127,7 @@ export const envVarsDef: Record<string, Type> = {
 | 
				
			||||||
	MY_FOLLOWING_COUNT: 'number',
 | 
						MY_FOLLOWING_COUNT: 'number',
 | 
				
			||||||
	SEED: null,
 | 
						SEED: null,
 | 
				
			||||||
	YMD: 'string',
 | 
						YMD: 'string',
 | 
				
			||||||
 | 
						NULL: null,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function isLiteralBlock(v: Block) {
 | 
					export function isLiteralBlock(v: Block) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,6 +69,11 @@ export class Meta {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public langs: string[];
 | 
						public langs: string[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('varchar', {
 | 
				
			||||||
 | 
							length: 256, array: true, default: '{}'
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public pinnedUsers: string[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Column('varchar', {
 | 
						@Column('varchar', {
 | 
				
			||||||
		length: 256, array: true, default: '{}'
 | 
							length: 256, array: true, default: '{}'
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -157,11 +157,6 @@ export class User {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public isModerator: boolean;
 | 
						public isModerator: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Column('boolean', {
 | 
					 | 
				
			||||||
		default: false,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public isVerified: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('varchar', {
 | 
						@Column('varchar', {
 | 
				
			||||||
		length: 128, array: true, default: '{}'
 | 
							length: 128, array: true, default: '{}'
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,7 +87,6 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
			isAdmin: user.isAdmin || falsy,
 | 
								isAdmin: user.isAdmin || falsy,
 | 
				
			||||||
			isBot: user.isBot || falsy,
 | 
								isBot: user.isBot || falsy,
 | 
				
			||||||
			isCat: user.isCat || falsy,
 | 
								isCat: user.isCat || falsy,
 | 
				
			||||||
			isVerified: user.isVerified || falsy,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// カスタム絵文字添付
 | 
								// カスタム絵文字添付
 | 
				
			||||||
			emojis: user.emojis.length > 0 ? Emojis.find({
 | 
								emojis: user.emojis.length > 0 ? Emojis.find({
 | 
				
			||||||
| 
						 | 
					@ -369,10 +368,6 @@ export const packedUserSchema = {
 | 
				
			||||||
			nullable: bool.false, optional: bool.true,
 | 
								nullable: bool.false, optional: bool.true,
 | 
				
			||||||
			description: 'Whether this account is a moderator.'
 | 
								description: 'Whether this account is a moderator.'
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		isVerified: {
 | 
					 | 
				
			||||||
			type: types.boolean,
 | 
					 | 
				
			||||||
			nullable: bool.false, optional: bool.true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		isLocked: {
 | 
							isLocked: {
 | 
				
			||||||
			type: types.boolean,
 | 
								type: types.boolean,
 | 
				
			||||||
			nullable: bool.false, optional: bool.true,
 | 
								nullable: bool.false, optional: bool.true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,28 @@ import { ensure } from '../../../prelude/ensure';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function validateNote(object: any, uri: string) {
 | 
				
			||||||
 | 
						const expectHost = extractDbHost(uri);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (object == null) {
 | 
				
			||||||
 | 
							return new Error('invalid Note: object is null');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!['Note', 'Question', 'Article'].includes(object.type)) {
 | 
				
			||||||
 | 
							return new Error(`invalid Note: invalied object type ${object.type}`);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (object.id && extractDbHost(object.id) !== expectHost) {
 | 
				
			||||||
 | 
							return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (object.attributedTo && extractDbHost(object.attributedTo) !== expectHost) {
 | 
				
			||||||
 | 
							return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Noteをフェッチします。
 | 
					 * Noteをフェッチします。
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -59,8 +81,10 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const object: any = await resolver.resolve(value);
 | 
						const object: any = await resolver.resolve(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!object || !['Note', 'Question', 'Article'].includes(object.type)) {
 | 
						const entryUri = value.id || value;
 | 
				
			||||||
		logger.error(`invalid note: ${value}`, {
 | 
						const err = validateNote(object, entryUri);
 | 
				
			||||||
 | 
						if (err) {
 | 
				
			||||||
 | 
							logger.error(`${err.message}`, {
 | 
				
			||||||
			resolver: {
 | 
								resolver: {
 | 
				
			||||||
				history: resolver.getHistory()
 | 
									history: resolver.getHistory()
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,6 @@ export const meta = {
 | 
				
			||||||
				'admin',
 | 
									'admin',
 | 
				
			||||||
				'moderator',
 | 
									'moderator',
 | 
				
			||||||
				'adminOrModerator',
 | 
									'adminOrModerator',
 | 
				
			||||||
				'verified',
 | 
					 | 
				
			||||||
				'silenced',
 | 
									'silenced',
 | 
				
			||||||
				'suspended',
 | 
									'suspended',
 | 
				
			||||||
			]),
 | 
								]),
 | 
				
			||||||
| 
						 | 
					@ -61,7 +60,6 @@ export default define(meta, async (ps, me) => {
 | 
				
			||||||
		case 'admin': query.where('user.isAdmin = TRUE'); break;
 | 
							case 'admin': query.where('user.isAdmin = TRUE'); break;
 | 
				
			||||||
		case 'moderator': query.where('user.isModerator = TRUE'); break;
 | 
							case 'moderator': query.where('user.isModerator = TRUE'); break;
 | 
				
			||||||
		case 'adminOrModerator': query.where('user.isAdmin = TRUE OR isModerator = TRUE'); break;
 | 
							case 'adminOrModerator': query.where('user.isAdmin = TRUE OR isModerator = TRUE'); break;
 | 
				
			||||||
		case 'verified': query.where('user.isVerified = TRUE'); break;
 | 
					 | 
				
			||||||
		case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
 | 
							case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
 | 
				
			||||||
		case 'silenced': query.where('user.isSilenced = TRUE'); break;
 | 
							case 'silenced': query.where('user.isSilenced = TRUE'); break;
 | 
				
			||||||
		case 'suspended': query.where('user.isSuspended = TRUE'); break;
 | 
							case 'suspended': query.where('user.isSuspended = TRUE'); break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,38 +0,0 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					 | 
				
			||||||
import { ID } from '../../../../misc/cafy-id';
 | 
					 | 
				
			||||||
import define from '../../define';
 | 
					 | 
				
			||||||
import { Users } from '../../../../models';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const meta = {
 | 
					 | 
				
			||||||
	desc: {
 | 
					 | 
				
			||||||
		'ja-JP': '指定したユーザーの公式アカウントを解除します。',
 | 
					 | 
				
			||||||
		'en-US': 'Mark a user as unverified.'
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tags: ['admin'],
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	requireCredential: true,
 | 
					 | 
				
			||||||
	requireModerator: true,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	params: {
 | 
					 | 
				
			||||||
		userId: {
 | 
					 | 
				
			||||||
			validator: $.type(ID),
 | 
					 | 
				
			||||||
			desc: {
 | 
					 | 
				
			||||||
				'ja-JP': '対象のユーザーID',
 | 
					 | 
				
			||||||
				'en-US': 'The user ID which you want to unverify'
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default define(meta, async (ps) => {
 | 
					 | 
				
			||||||
	const user = await Users.findOne(ps.userId as string);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (user == null) {
 | 
					 | 
				
			||||||
		throw new Error('user not found');
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	await Users.update(user.id, {
 | 
					 | 
				
			||||||
		isVerified: false
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
| 
						 | 
					@ -56,6 +56,13 @@ export const meta = {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pinnedUsers: {
 | 
				
			||||||
 | 
								validator: $.optional.nullable.arr($.str),
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									'ja-JP': 'ピン留めユーザー'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		hiddenTags: {
 | 
							hiddenTags: {
 | 
				
			||||||
			validator: $.optional.nullable.arr($.str),
 | 
								validator: $.optional.nullable.arr($.str),
 | 
				
			||||||
			desc: {
 | 
								desc: {
 | 
				
			||||||
| 
						 | 
					@ -353,6 +360,10 @@ export default define(meta, async (ps) => {
 | 
				
			||||||
		set.useStarForReactionFallback = ps.useStarForReactionFallback;
 | 
							set.useStarForReactionFallback = ps.useStarForReactionFallback;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (Array.isArray(ps.pinnedUsers)) {
 | 
				
			||||||
 | 
							set.pinnedUsers = ps.pinnedUsers;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (Array.isArray(ps.hiddenTags)) {
 | 
						if (Array.isArray(ps.hiddenTags)) {
 | 
				
			||||||
		set.hiddenTags = ps.hiddenTags;
 | 
							set.hiddenTags = ps.hiddenTags;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,38 +0,0 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					 | 
				
			||||||
import { ID } from '../../../../misc/cafy-id';
 | 
					 | 
				
			||||||
import define from '../../define';
 | 
					 | 
				
			||||||
import { Users } from '../../../../models';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const meta = {
 | 
					 | 
				
			||||||
	desc: {
 | 
					 | 
				
			||||||
		'ja-JP': '指定したユーザーを公式アカウントにします。',
 | 
					 | 
				
			||||||
		'en-US': 'Mark a user as verified.'
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tags: ['admin'],
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	requireCredential: true,
 | 
					 | 
				
			||||||
	requireModerator: true,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	params: {
 | 
					 | 
				
			||||||
		userId: {
 | 
					 | 
				
			||||||
			validator: $.type(ID),
 | 
					 | 
				
			||||||
			desc: {
 | 
					 | 
				
			||||||
				'ja-JP': '対象のユーザーID',
 | 
					 | 
				
			||||||
				'en-US': 'The user ID which you want to verify'
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default define(meta, async (ps) => {
 | 
					 | 
				
			||||||
	const user = await Users.findOne(ps.userId as string);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (user == null) {
 | 
					 | 
				
			||||||
		throw new Error('user not found');
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	await Users.update(user.id, {
 | 
					 | 
				
			||||||
		isVerified: true
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
| 
						 | 
					@ -160,6 +160,7 @@ export default define(meta, async (ps, me) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (me && (me.isAdmin || me.isModerator)) {
 | 
						if (me && (me.isAdmin || me.isModerator)) {
 | 
				
			||||||
		response.useStarForReactionFallback = instance.useStarForReactionFallback;
 | 
							response.useStarForReactionFallback = instance.useStarForReactionFallback;
 | 
				
			||||||
 | 
							response.pinnedUsers = instance.pinnedUsers;
 | 
				
			||||||
		response.hiddenTags = instance.hiddenTags;
 | 
							response.hiddenTags = instance.hiddenTags;
 | 
				
			||||||
		response.recaptchaSecretKey = instance.recaptchaSecretKey;
 | 
							response.recaptchaSecretKey = instance.recaptchaSecretKey;
 | 
				
			||||||
		response.proxyAccount = instance.proxyAccount;
 | 
							response.proxyAccount = instance.proxyAccount;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										60
									
								
								src/server/api/endpoints/notes/unrenote.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/server/api/endpoints/notes/unrenote.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					import $ from 'cafy';
 | 
				
			||||||
 | 
					import { ID } from '../../../../misc/cafy-id';
 | 
				
			||||||
 | 
					import deleteNote from '../../../../services/note/delete';
 | 
				
			||||||
 | 
					import define from '../../define';
 | 
				
			||||||
 | 
					import * as ms from 'ms';
 | 
				
			||||||
 | 
					import { getNote } from '../../common/getters';
 | 
				
			||||||
 | 
					import { ApiError } from '../../error';
 | 
				
			||||||
 | 
					import { Notes } from '../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const meta = {
 | 
				
			||||||
 | 
						desc: {
 | 
				
			||||||
 | 
							'ja-JP': '指定した投稿のRenoteを解除します。',
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tags: ['notes'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requireCredential: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kind: 'write:notes',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						limit: {
 | 
				
			||||||
 | 
							duration: ms('1hour'),
 | 
				
			||||||
 | 
							max: 300,
 | 
				
			||||||
 | 
							minInterval: ms('1sec')
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params: {
 | 
				
			||||||
 | 
							noteId: {
 | 
				
			||||||
 | 
								validator: $.type(ID),
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									'ja-JP': '対象の投稿のID',
 | 
				
			||||||
 | 
									'en-US': 'Target note ID.'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errors: {
 | 
				
			||||||
 | 
							noSuchNote: {
 | 
				
			||||||
 | 
								message: 'No such note.',
 | 
				
			||||||
 | 
								code: 'NO_SUCH_NOTE',
 | 
				
			||||||
 | 
								id: 'efd4a259-2442-496b-8dd7-b255aa1a160f'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
 | 
						const note = await getNote(ps.noteId).catch(e => {
 | 
				
			||||||
 | 
							if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
 | 
				
			||||||
 | 
							throw e;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const renotes = await Notes.find({
 | 
				
			||||||
 | 
							userId: user.id,
 | 
				
			||||||
 | 
							renoteId: note.id
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (const note of renotes) {
 | 
				
			||||||
 | 
							deleteNote(user, note);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/server/api/endpoints/pinned-users.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/server/api/endpoints/pinned-users.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					import define from '../define';
 | 
				
			||||||
 | 
					import { Users } from '../../../models';
 | 
				
			||||||
 | 
					import { types, bool } from '../../../misc/schema';
 | 
				
			||||||
 | 
					import { fetchMeta } from '../../../misc/fetch-meta';
 | 
				
			||||||
 | 
					import parseAcct from '../../../misc/acct/parse';
 | 
				
			||||||
 | 
					import { User } from '../../../models/entities/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const meta = {
 | 
				
			||||||
 | 
						tags: ['users'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requireCredential: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params: {
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res: {
 | 
				
			||||||
 | 
							type: types.array,
 | 
				
			||||||
 | 
							optional: bool.false, nullable: bool.false,
 | 
				
			||||||
 | 
							items: {
 | 
				
			||||||
 | 
								type: types.object,
 | 
				
			||||||
 | 
								optional: bool.false, nullable: bool.false,
 | 
				
			||||||
 | 
								ref: 'User',
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default define(meta, async (ps, me) => {
 | 
				
			||||||
 | 
						const meta = await fetchMeta();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const users = await Promise.all(meta.pinnedUsers.map(acct => Users.findOne(parseAcct(acct))));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return await Users.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,6 @@ export const meta = {
 | 
				
			||||||
				'admin',
 | 
									'admin',
 | 
				
			||||||
				'moderator',
 | 
									'moderator',
 | 
				
			||||||
				'adminOrModerator',
 | 
									'adminOrModerator',
 | 
				
			||||||
				'verified',
 | 
					 | 
				
			||||||
				'alive'
 | 
									'alive'
 | 
				
			||||||
			]),
 | 
								]),
 | 
				
			||||||
			default: 'all'
 | 
								default: 'all'
 | 
				
			||||||
| 
						 | 
					@ -71,7 +70,6 @@ export default define(meta, async (ps, me) => {
 | 
				
			||||||
		case 'admin': query.where('user.isAdmin = TRUE'); break;
 | 
							case 'admin': query.where('user.isAdmin = TRUE'); break;
 | 
				
			||||||
		case 'moderator': query.where('user.isModerator = TRUE'); break;
 | 
							case 'moderator': query.where('user.isModerator = TRUE'); break;
 | 
				
			||||||
		case 'adminOrModerator': query.where('user.isAdmin = TRUE OR isModerator = TRUE'); break;
 | 
							case 'adminOrModerator': query.where('user.isAdmin = TRUE OR isModerator = TRUE'); break;
 | 
				
			||||||
		case 'verified': query.where('user.isVerified = TRUE'); break;
 | 
					 | 
				
			||||||
		case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
 | 
							case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue