Compare commits
24 commits
kb_develop
...
releases/1
Author | SHA1 | Date | |
---|---|---|---|
b768870895 | |||
3e7972c5b1 | |||
7a74c056ee | |||
750f5f4885 | |||
3daed614d5 | |||
f600d9f95b | |||
5267d19474 | |||
|
59657684e1 | ||
|
44efbc7141 | ||
|
25d176c280 | ||
|
f6447c0f4d | ||
|
da60c95317 | ||
|
49e05d9f2b | ||
|
9524487909 | ||
|
d21a4f8c39 | ||
|
d510b5d8b9 | ||
|
ff90575e2c | ||
|
f41c09f291 | ||
|
a7bc288569 | ||
|
843a5446e8 | ||
|
038d3b1513 | ||
|
b091cbdbea | ||
|
8992602acf | ||
|
c68762e2bf |
48
CHANGELOG.md
|
@ -2,6 +2,54 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [4.3.8] - 2025-05-06
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Update dependencies
|
||||||
|
- Check scheme on account, profile, and media URLs ([GHSA-x2rc-v5wx-g3m5](https://github.com/mastodon/mastodon/security/advisories/GHSA-x2rc-v5wx-g3m5))
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add warning for REDIS_NAMESPACE deprecation at startup (#34581 by @ClearlyClaire)
|
||||||
|
- Add built-in context for interaction policies (#34574 by @ClearlyClaire)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change activity distribution error handling to skip retrying for deleted accounts (#33617 by @ClearlyClaire)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Remove double-query for signed query strings (#34610 by @ClearlyClaire)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix incorrect redirect in response to unauthenticated API requests in limited federation mode (#34549 by @ClearlyClaire)
|
||||||
|
- Fix sign-up e-mail confirmation page reloading on error or redirect (#34548 by @ClearlyClaire)
|
||||||
|
|
||||||
|
## [4.3.7] - 2025-04-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add delay to profile updates to debounce them (#34137 by @ClearlyClaire)
|
||||||
|
- Add support for paginating partial collections in `SynchronizeFollowersService` (#34272 and #34277 by @ClearlyClaire)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change account suspensions to be federated to recently-followed accounts as well (#34294 by @ClearlyClaire)
|
||||||
|
- Change `AccountReachFinder` to consider statuses based on suspension date (#32805 and #34291 by @ClearlyClaire and @mjankowski)
|
||||||
|
- Change user archive signed URL TTL from 10 seconds to 1 hour (#34254 by @ClearlyClaire)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix static version of animated PNG emojis not being properly extracted (#34337 by @ClearlyClaire)
|
||||||
|
- Fix filters not applying in detailed view, favourites and bookmarks (#34259 and #34260 by @ClearlyClaire)
|
||||||
|
- Fix handling of malformed/unusual HTML (#34201 by @ClearlyClaire)
|
||||||
|
- Fix `CacheBuster` being queued for missing media attachments (#34253 by @ClearlyClaire)
|
||||||
|
- Fix incorrect URL being used when cache busting (#34189 by @ClearlyClaire)
|
||||||
|
- Fix streaming server refusing unix socket path in `DATABASE_URL` (#34091 by @ClearlyClaire)
|
||||||
|
- Fix “x” hotkey not working on boosted filtered posts (#33758 by @ClearlyClaire)
|
||||||
|
|
||||||
## [4.3.6] - 2025-03-13
|
## [4.3.6] - 2025-03-13
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
|
@ -435,7 +435,7 @@ GEM
|
||||||
mutex_m (0.3.0)
|
mutex_m (0.3.0)
|
||||||
net-http (0.6.0)
|
net-http (0.6.0)
|
||||||
uri
|
uri
|
||||||
net-imap (0.5.6)
|
net-imap (0.5.7)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ldap (0.19.0)
|
net-ldap (0.19.0)
|
||||||
|
@ -446,7 +446,7 @@ GEM
|
||||||
net-smtp (0.5.1)
|
net-smtp (0.5.1)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.4)
|
nio4r (2.7.4)
|
||||||
nokogiri (1.18.7)
|
nokogiri (1.18.8)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
oj (3.16.10)
|
oj (3.16.10)
|
||||||
|
|
|
@ -72,6 +72,13 @@ class Api::BaseController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Redefine `require_functional!` to properly output JSON instead of HTML redirects
|
||||||
|
def require_functional!
|
||||||
|
return if current_user.functional?
|
||||||
|
|
||||||
|
require_user!
|
||||||
|
end
|
||||||
|
|
||||||
def render_empty
|
def render_empty
|
||||||
render json: {}, status: 200
|
render json: {}, status: 200
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Api::V1::Trends::TagsController < Api::BaseController
|
||||||
|
|
||||||
after_action :insert_pagination_headers
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
DEFAULT_TAGS_LIMIT = 10
|
DEFAULT_TAGS_LIMIT = (ENV['MAX_TRENDING_TAGS'] || 10).to_i
|
||||||
|
|
||||||
deprecate_api '2022-03-30', only: :index, if: -> { request.path == '/api/v1/trends' }
|
deprecate_api '2022-03-30', only: :index, if: -> { request.path == '/api/v1/trends' }
|
||||||
|
|
||||||
|
|
|
@ -72,10 +72,24 @@ class ApplicationController < ActionController::Base
|
||||||
def require_functional!
|
def require_functional!
|
||||||
return if current_user.functional?
|
return if current_user.functional?
|
||||||
|
|
||||||
if current_user.confirmed?
|
respond_to do |format|
|
||||||
redirect_to edit_user_registration_path
|
format.any do
|
||||||
else
|
if current_user.confirmed?
|
||||||
redirect_to auth_setup_path
|
redirect_to edit_user_registration_path
|
||||||
|
else
|
||||||
|
redirect_to auth_setup_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
format.json do
|
||||||
|
if !current_user.confirmed?
|
||||||
|
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
|
||||||
|
elsif !current_user.approved?
|
||||||
|
render json: { error: 'Your login is currently pending approval' }, status: 403
|
||||||
|
elsif !current_user.functional?
|
||||||
|
render json: { error: 'Your login is currently disabled' }, status: 403
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,13 @@ module ContextHelper
|
||||||
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
||||||
attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } },
|
attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } },
|
||||||
misskey_license: { 'misskey' => 'https://misskey-hub.net/ns#', '_misskey_license' => 'misskey:_misskey_license' },
|
misskey_license: { 'misskey' => 'https://misskey-hub.net/ns#', '_misskey_license' => 'misskey:_misskey_license' },
|
||||||
|
interaction_policies: {
|
||||||
|
'gts' => 'https://gotosocial.org/ns#',
|
||||||
|
'interactionPolicy' => { '@id' => 'gts:interactionPolicy', '@type' => '@id' },
|
||||||
|
'canQuote' => { '@id' => 'gts:canQuote', '@type' => '@id' },
|
||||||
|
'automaticApproval' => { '@id' => 'gts:automaticApproval', '@type' => '@id' },
|
||||||
|
'manualApproval' => { '@id' => 'gts:manualApproval', '@type' => '@id' },
|
||||||
|
},
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def full_context
|
def full_context
|
||||||
|
|
|
@ -4,9 +4,12 @@ import axios from 'axios';
|
||||||
import ready from '../mastodon/ready';
|
import ready from '../mastodon/ready';
|
||||||
|
|
||||||
async function checkConfirmation() {
|
async function checkConfirmation() {
|
||||||
const response = await axios.get('/api/v1/emails/check_confirmation');
|
const response = await axios.get('/api/v1/emails/check_confirmation', {
|
||||||
|
headers: { Accept: 'application/json' },
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
|
||||||
if (response.data) {
|
if (response.status === 200 && response.data === true) {
|
||||||
window.location.href = '/start';
|
window.location.href = '/start';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
app/javascript/icons/android-chrome-144x144.png
Executable file → Normal file
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 5.7 KiB |
BIN
app/javascript/icons/android-chrome-192x192.png
Executable file → Normal file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 8.5 KiB |
BIN
app/javascript/icons/android-chrome-256x256.png
Executable file → Normal file
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
BIN
app/javascript/icons/android-chrome-36x36.png
Executable file → Normal file
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 950 B |
BIN
app/javascript/icons/android-chrome-384x384.png
Executable file → Normal file
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 21 KiB |
BIN
app/javascript/icons/android-chrome-48x48.png
Executable file → Normal file
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
app/javascript/icons/android-chrome-512x512.png
Executable file → Normal file
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 31 KiB |
BIN
app/javascript/icons/android-chrome-72x72.png
Executable file → Normal file
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
app/javascript/icons/android-chrome-96x96.png
Executable file → Normal file
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 3.2 KiB |
BIN
app/javascript/icons/apple-touch-icon-1024x1024.png
Executable file → Normal file
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 76 KiB |
BIN
app/javascript/icons/apple-touch-icon-114x114.png
Executable file → Normal file
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 4 KiB |
BIN
app/javascript/icons/apple-touch-icon-120x120.png
Executable file → Normal file
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 4.3 KiB |
BIN
app/javascript/icons/apple-touch-icon-144x144.png
Executable file → Normal file
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 5.7 KiB |
BIN
app/javascript/icons/apple-touch-icon-152x152.png
Executable file → Normal file
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 6 KiB |
BIN
app/javascript/icons/apple-touch-icon-167x167.png
Executable file → Normal file
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 6.9 KiB |
BIN
app/javascript/icons/apple-touch-icon-180x180.png
Executable file → Normal file
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 7.5 KiB |
BIN
app/javascript/icons/apple-touch-icon-192x192.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
app/javascript/icons/apple-touch-icon-256x256.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
app/javascript/icons/apple-touch-icon-36x36.png
Normal file
After Width: | Height: | Size: 950 B |
BIN
app/javascript/icons/apple-touch-icon-384x384.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
app/javascript/icons/apple-touch-icon-48x48.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/javascript/icons/apple-touch-icon-512x512.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
app/javascript/icons/apple-touch-icon-57x57.png
Executable file → Normal file
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
app/javascript/icons/apple-touch-icon-60x60.png
Executable file → Normal file
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
app/javascript/icons/apple-touch-icon-72x72.png
Executable file → Normal file
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
app/javascript/icons/apple-touch-icon-76x76.png
Executable file → Normal file
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 2.3 KiB |
BIN
app/javascript/icons/apple-touch-icon-96x96.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
app/javascript/icons/favicon-16x16.png
Executable file → Normal file
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 6 KiB |
BIN
app/javascript/icons/favicon-32x32.png
Executable file → Normal file
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 6 KiB |
BIN
app/javascript/icons/favicon-48x48.png
Executable file → Normal file
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 6 KiB |
|
@ -94,6 +94,17 @@ export function normalizeStatus(status, normalOldStatus) {
|
||||||
normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
|
normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
|
||||||
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
|
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
|
||||||
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
|
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
|
||||||
|
|
||||||
|
if (normalStatus.url && !(normalStatus.url.startsWith('http://') || normalStatus.url.startsWith('https://'))) {
|
||||||
|
normalStatus.url = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalStatus.url ||= normalStatus.uri;
|
||||||
|
|
||||||
|
normalStatus.media_attachments.forEach(item => {
|
||||||
|
if (item.remote_url && !(item.remote_url.startsWith('http://') || item.remote_url.startsWith('https://')))
|
||||||
|
item.remote_url = null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalOldStatus) {
|
if (normalOldStatus) {
|
||||||
|
|
|
@ -173,7 +173,7 @@ class About extends PureComponent {
|
||||||
<div className='about__header'>
|
<div className='about__header'>
|
||||||
<ServerHeroImage blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} srcSet={server.getIn(['thumbnail', 'versions'])?.map((value, key) => `${value} ${key.replace('@', '')}`).join(', ')} className='about__header__hero' />
|
<ServerHeroImage blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} srcSet={server.getIn(['thumbnail', 'versions'])?.map((value, key) => `${value} ${key.replace('@', '')}`).join(', ')} className='about__header__hero' />
|
||||||
<h1>{isLoading ? <Skeleton width='10ch' /> : server.get('domain')}</h1>
|
<h1>{isLoading ? <Skeleton width='10ch' /> : server.get('domain')}</h1>
|
||||||
<p><FormattedMessage id='about.powered_by' defaultMessage='Decentralized social media powered by {mastodon}' values={{ mastodon: <a href='https://joinmastodon.org' className='about__mail' target='_blank' rel='noopener'>Mastodon</a> }} /></p>
|
<p><FormattedMessage id='about.powered_by' defaultMessage='Decentralized social media powered by {domain}' /></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='about__meta'>
|
<div className='about__meta'>
|
||||||
|
|
|
@ -14,6 +14,8 @@ import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?re
|
||||||
import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
|
import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
|
||||||
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
||||||
import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react';
|
import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react';
|
||||||
|
import HashtagIcon from '@/material-icons/400-24px/tag.svg?react';
|
||||||
|
import Directory from '@/material-icons/400-24px/group.svg?react';
|
||||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||||
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
||||||
|
@ -42,6 +44,8 @@ const messages = defineMessages({
|
||||||
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
||||||
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
||||||
public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
||||||
|
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed Hashtags' },
|
||||||
|
directory: { id: 'navigation_bar.directory', defaultMessage: 'Profile directory' },
|
||||||
settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
|
settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
|
||||||
community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
||||||
deep_timeline: { id: 'navigation_bar.deep_timeline', defaultMessage: 'Deep timeline' },
|
deep_timeline: { id: 'navigation_bar.deep_timeline', defaultMessage: 'Deep timeline' },
|
||||||
|
@ -144,6 +148,7 @@ class GettingStarted extends ImmutablePureComponent {
|
||||||
<ColumnLink key='direct' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} to='/conversations' />,
|
<ColumnLink key='direct' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} to='/conversations' />,
|
||||||
<ColumnLink key='bookmark' icon='bookmarks' iconComponent={BookmarksIcon} text={intl.formatMessage(messages.bookmarks)} to='/bookmark_categories' />,
|
<ColumnLink key='bookmark' icon='bookmarks' iconComponent={BookmarksIcon} text={intl.formatMessage(messages.bookmarks)} to='/bookmark_categories' />,
|
||||||
<ColumnLink key='favourites' icon='star' iconComponent={StarIcon} text={intl.formatMessage(messages.favourites)} to='/favourites' />,
|
<ColumnLink key='favourites' icon='star' iconComponent={StarIcon} text={intl.formatMessage(messages.favourites)} to='/favourites' />,
|
||||||
|
<ColumnLink key='followed_tags' icon='tag' iconComponent={followed_tagsIcon} text={intl.formatMessage(messages.followed_tags)} to='/followed_tags' />,
|
||||||
<ColumnLink key='lists' icon='list-ul' iconComponent={ListAltIcon} text={intl.formatMessage(messages.lists)} to='/lists' />,
|
<ColumnLink key='lists' icon='list-ul' iconComponent={ListAltIcon} text={intl.formatMessage(messages.lists)} to='/lists' />,
|
||||||
<ColumnLink key='antennas' icon='wifi' iconComponent={AntennaIcon} text={intl.formatMessage(messages.antennas)} to='/antennas' />,
|
<ColumnLink key='antennas' icon='wifi' iconComponent={AntennaIcon} text={intl.formatMessage(messages.antennas)} to='/antennas' />,
|
||||||
<ColumnLink key='circles' icon='user-circle' iconComponent={CirclesIcon} text={intl.formatMessage(messages.circles)} to='/circles' />,
|
<ColumnLink key='circles' icon='user-circle' iconComponent={CirclesIcon} text={intl.formatMessage(messages.circles)} to='/circles' />,
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Component, useEffect } from 'react';
|
import { Component, useEffect } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, useIntl } from 'react-intl';
|
import { defineMessages, injectIntl, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import CirclesIcon from '@/material-icons/400-24px/account_circle-fill.svg?react';
|
import CirclesIcon from '@/material-icons/400-24px/account_circle-fill.svg?react';
|
||||||
|
@ -13,6 +10,8 @@ import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?re
|
||||||
import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react';
|
import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react';
|
||||||
import ExploreActiveIcon from '@/material-icons/400-24px/explore-fill.svg?react';
|
import ExploreActiveIcon from '@/material-icons/400-24px/explore-fill.svg?react';
|
||||||
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
||||||
|
import HashtagIcon from '@/material-icons/400-24px/tag.svg?react';
|
||||||
|
import DirectoryIcon from '@/material-icons/400-24px/group.svg?react';
|
||||||
import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react';
|
import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react';
|
||||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||||
import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||||
|
@ -31,6 +30,7 @@ import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
||||||
import StarActiveIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
import StarActiveIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||||
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
||||||
import AntennaIcon from '@/material-icons/400-24px/wifi.svg?react';
|
import AntennaIcon from '@/material-icons/400-24px/wifi.svg?react';
|
||||||
|
|
||||||
import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
||||||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||||
import { WordmarkLogo } from 'mastodon/components/logo';
|
import { WordmarkLogo } from 'mastodon/components/logo';
|
||||||
|
@ -50,6 +50,8 @@ const messages = defineMessages({
|
||||||
home: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
home: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
||||||
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
||||||
explore: { id: 'explore.title', defaultMessage: 'Explore' },
|
explore: { id: 'explore.title', defaultMessage: 'Explore' },
|
||||||
|
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
|
||||||
|
directory: { id: 'navigation_bar.directory', defaultMessage: 'Profiles directory' },
|
||||||
local: { id: 'column.local', defaultMessage: 'Local' },
|
local: { id: 'column.local', defaultMessage: 'Local' },
|
||||||
deepLocal: { id: 'column.deep_local', defaultMessage: 'Deep' },
|
deepLocal: { id: 'column.deep_local', defaultMessage: 'Deep' },
|
||||||
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
|
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
|
||||||
|
@ -71,7 +73,6 @@ const messages = defineMessages({
|
||||||
});
|
});
|
||||||
|
|
||||||
const NotificationsLink = () => {
|
const NotificationsLink = () => {
|
||||||
|
|
||||||
const count = useSelector(selectUnreadNotificationGroupsCount);
|
const count = useSelector(selectUnreadNotificationGroupsCount);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
@ -129,25 +130,20 @@ class NavigationPanel extends Component {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
const { signedIn, disabledAccountId, permissions } = this.props.identity;
|
const { signedIn, disabledAccountId, permissions } = this.props.identity;
|
||||||
|
|
||||||
const explorer = (trendsEnabled ? (
|
const explorer = trendsEnabled ? (
|
||||||
<ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} />
|
<ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} />
|
||||||
) : (
|
) : (
|
||||||
<ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} />
|
<ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} />
|
||||||
));
|
);
|
||||||
|
|
||||||
let banner = undefined;
|
|
||||||
|
|
||||||
if (transientSingleColumn) {
|
const banner = transientSingleColumn ? (
|
||||||
banner = (
|
<div className='switch-to-advanced'>
|
||||||
<div className='switch-to-advanced'>
|
{intl.formatMessage(messages.openedInClassicInterface)}{' '}
|
||||||
{intl.formatMessage(messages.openedInClassicInterface)}
|
<a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'>
|
||||||
{" "}
|
{intl.formatMessage(messages.advancedInterface)}
|
||||||
<a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'>
|
</a>
|
||||||
{intl.formatMessage(messages.advancedInterface)}
|
</div>
|
||||||
</a>
|
) : null;
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='navigation-panel'>
|
<div className='navigation-panel'>
|
||||||
|
@ -155,90 +151,59 @@ class NavigationPanel extends Component {
|
||||||
<Link to='/' className='column-link column-link--logo'><WordmarkLogo /></Link>
|
<Link to='/' className='column-link column-link--logo'><WordmarkLogo /></Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{banner &&
|
{banner && <div className='navigation-panel__banner'>{banner}</div>}
|
||||||
<div className='navigation-panel__banner'>
|
|
||||||
{banner}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className='navigation-panel__menu'>
|
<div className='navigation-panel__menu'>
|
||||||
{signedIn && (
|
{signedIn && (
|
||||||
<>
|
<>
|
||||||
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} activeIconComponent={HomeActiveIcon} text={intl.formatMessage(messages.home)} />
|
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} activeIconComponent={HomeActiveIcon} text={intl.formatMessage(messages.home)} />
|
||||||
<NotificationsLink />
|
<NotificationsLink />
|
||||||
</>
|
{enableLocalTimeline && <ColumnLink transparent to='/public/local/fixed' icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.local)} />}
|
||||||
)}
|
{enableDtlMenu && dtlTag && <ColumnLink transparent to={`/tags/${dtlTag}`} icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.deepLocal)} />}
|
||||||
|
<ColumnLink transparent to='/public' isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
|
||||||
{signedIn && enableLocalTimeline && (
|
|
||||||
<ColumnLink transparent to='/public/local/fixed' icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.local)} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{signedIn && enableDtlMenu && dtlTag && (
|
|
||||||
<ColumnLink transparent to={`/tags/${dtlTag}`} icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.deepLocal)} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!signedIn && explorer}
|
|
||||||
|
|
||||||
{signedIn && (
|
|
||||||
<ColumnLink transparent to='/public' isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(!signedIn && timelinePreview) && (
|
|
||||||
<ColumnLink transparent to={enableLocalTimeline ? '/public/local' : '/public'} isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{signedIn && (
|
|
||||||
<>
|
|
||||||
<ListPanel />
|
<ListPanel />
|
||||||
<hr />
|
<hr />
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{signedIn && (
|
|
||||||
<>
|
|
||||||
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={intl.formatMessage(messages.lists)} />
|
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={intl.formatMessage(messages.lists)} />
|
||||||
<ColumnLink transparent to='/antennas' icon='wifi' iconComponent={AntennaIcon} text={intl.formatMessage(messages.antennas)} isActive={this.isAntennasActive} />
|
<ColumnLink transparent to='/antennas' icon='wifi' iconComponent={AntennaIcon} text={intl.formatMessage(messages.antennas)} isActive={this.isAntennasActive} />
|
||||||
<ColumnLink transparent to='/circles' icon='user-circle' iconComponent={CirclesIcon} text={intl.formatMessage(messages.circles)} />
|
<ColumnLink transparent to='/circles' icon='user-circle' iconComponent={CirclesIcon} text={intl.formatMessage(messages.circles)} />
|
||||||
|
<ColumnLink transparent to='/followed_tags' icon='tag' iconComponent={HashtagIcon} text={intl.formatMessage(messages.followed_tags)} />
|
||||||
|
<ColumnLink transparent to='/directory' icon='group' iconComponent={DirectoryIcon} text={intl.formatMessage(messages.directory)} />
|
||||||
<FollowRequestsLink />
|
<FollowRequestsLink />
|
||||||
<ColumnLink transparent to='/conversations' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} />
|
<ColumnLink transparent to='/conversations' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} />
|
||||||
</>
|
{explorer}
|
||||||
)}
|
|
||||||
|
|
||||||
{signedIn && explorer}
|
|
||||||
|
|
||||||
{signedIn && (
|
|
||||||
<>
|
|
||||||
<ColumnLink transparent to='/bookmark_categories' icon='bookmarks' iconComponent={BookmarksIcon} activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} />
|
<ColumnLink transparent to='/bookmark_categories' icon='bookmarks' iconComponent={BookmarksIcon} activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} />
|
||||||
{ !isHideItem('favourite_menu') && <ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} activeIconComponent={StarActiveIcon} text={intl.formatMessage(messages.favourites)} /> }
|
{!isHideItem('favourite_menu') && <ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} activeIconComponent={StarActiveIcon} text={intl.formatMessage(messages.favourites)} />}
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<ColumnLink transparent href='/settings/preferences' icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} />
|
<ColumnLink transparent href='/settings/preferences' icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} />
|
||||||
|
|
||||||
{canManageReports(permissions) && <ColumnLink transparent href='/admin/reports' icon='flag' iconComponent={ModerationIcon} text={intl.formatMessage(messages.moderation)} />}
|
{canManageReports(permissions) && <ColumnLink transparent href='/admin/reports' icon='flag' iconComponent={ModerationIcon} text={intl.formatMessage(messages.moderation)} />}
|
||||||
{canViewAdminDashboard(permissions) && <ColumnLink transparent href='/admin/dashboard' icon='tachometer' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.administration)} />}
|
{canViewAdminDashboard(permissions) && <ColumnLink transparent href='/admin/dashboard' icon='tachometer' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.administration)} />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!signedIn && (
|
{!signedIn && (
|
||||||
<div className='navigation-panel__sign-in-banner'>
|
<>
|
||||||
<hr />
|
{explorer}
|
||||||
{ disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner /> }
|
{(timelinePreview || enableLocalTimeline) && (
|
||||||
</div>
|
<ColumnLink transparent to={enableLocalTimeline ? '/public/local' : '/public'} isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
|
||||||
|
)}
|
||||||
|
<div className='navigation-panel__sign-in-banner'>
|
||||||
|
<hr />
|
||||||
|
{disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner />}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='navigation-panel__legal'>
|
<div className='navigation-panel__legal'>
|
||||||
<hr />
|
<hr />
|
||||||
<ColumnLink transparent to='/about' icon='ellipsis-h' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.about)} />
|
<ColumnLink transparent to='/about' icon='ellipsis-h' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.about)} />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex-spacer' />
|
<div className='flex-spacer' />
|
||||||
|
|
||||||
<NavigationPortal />
|
<NavigationPortal />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectIntl(withIdentity(NavigationPanel));
|
export default injectIntl(withIdentity(NavigationPanel));
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
"about.kmyblue_capabilities": "Features available in this server",
|
"about.kmyblue_capabilities": "Features available in this server",
|
||||||
"about.kmyblue_capability": "This server is using kmyblue, a fork of Mastodon. On this server, kmyblues unique features are configured as follows.",
|
"about.kmyblue_capability": "This server is using kmyblue, a fork of Mastodon. On this server, kmyblues unique features are configured as follows.",
|
||||||
"about.not_available": "This information has not been made available on this server.",
|
"about.not_available": "This information has not been made available on this server.",
|
||||||
"about.powered_by": "Decentralized social media powered by {mastodon}",
|
"about.powered_by": "Decentralized social media powered by {domain}",
|
||||||
"about.public_visibility": "Public visibility",
|
"about.public_visibility": "Public visibility",
|
||||||
"about.rules": "Server rules",
|
"about.rules": "Server rules",
|
||||||
"account.account_note_header": "Personal note",
|
"account.account_note_header": "Personal note",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"about.blocks": "Moderatit servers",
|
"about.blocks": "Moderatit servers",
|
||||||
"about.contact": "Contack:",
|
"about.contact": "Contack:",
|
||||||
"about.disclaimer": "Mastodon is free, open-soorced saftware, an a trademairk o Mastodon gGmbH.",
|
"about.disclaimer": "Join the Fediverse, become part of a community, and break free from Big Tech™'s stranglehold on public discourse.",
|
||||||
"about.domain_blocks.no_reason_available": "Raison no available",
|
"about.domain_blocks.no_reason_available": "Raison no available",
|
||||||
"about.domain_blocks.preamble": "On the hail, Mastodon lats ye view content frae an interack wi uisers fae onie ither server in the fediverse.",
|
"about.domain_blocks.preamble": "On the hail, Mastodon lats ye view content frae an interack wi uisers fae onie ither server in the fediverse.",
|
||||||
"about.domain_blocks.silenced.explanation": "Ye'll generally no see profiles an content frae this server, unless ye explicitly luik it up or opt intae it bi follaein.",
|
"about.domain_blocks.silenced.explanation": "Ye'll generally no see profiles an content frae this server, unless ye explicitly luik it up or opt intae it bi follaein.",
|
||||||
|
|
|
@ -178,5 +178,10 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
|
||||||
),
|
),
|
||||||
note_emojified: emojify(accountJSON.note, emojiMap),
|
note_emojified: emojify(accountJSON.note, emojiMap),
|
||||||
note_plain: unescapeHTML(accountJSON.note),
|
note_plain: unescapeHTML(accountJSON.note),
|
||||||
|
url:
|
||||||
|
accountJSON.url.startsWith('http://') ||
|
||||||
|
accountJSON.url.startsWith('https://')
|
||||||
|
? accountJSON.url
|
||||||
|
: accountJSON.uri,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
$classic-base-color: #282c37; // Midnight Express
|
$classic-base-color: #282c37; // Midnight Express
|
||||||
$classic-secondary-color: #d9e1e8; // Pattens Blue
|
$classic-secondary-color: #d9e1e8; // Pattens Blue
|
||||||
|
|
||||||
// Variables for defaults in UI
|
@use '../mastodon/variables' with (
|
||||||
$simple-background-color: $classic-base-color !default;
|
// Variables for defaults in UI
|
||||||
|
$simple-background-color: $classic-base-color,
|
||||||
|
|
||||||
// Tell UI to use selected colors
|
// Tell UI to use selected colors
|
||||||
$ui-base-lighter-color: #969fbc !default; // Lighter darkest
|
$ui-base-lighter-color: #969fbc,
|
||||||
|
|
||||||
// For texts on inverted backgrounds
|
// Lighter darkest
|
||||||
$inverted-text-color: $classic-secondary-color !default;
|
// For texts on inverted backgrounds
|
||||||
|
$inverted-text-color: $classic-secondary-color
|
||||||
|
);
|
||||||
|
|
|
@ -15,13 +15,15 @@ class ActivityPub::Parser::MediaAttachmentParser
|
||||||
end
|
end
|
||||||
|
|
||||||
def remote_url
|
def remote_url
|
||||||
Addressable::URI.parse(@json['url'])&.normalize&.to_s
|
url = Addressable::URI.parse(@json['url'])&.normalize&.to_s
|
||||||
|
url unless unsupported_uri_scheme?(url)
|
||||||
rescue Addressable::URI::InvalidURIError
|
rescue Addressable::URI::InvalidURIError
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def thumbnail_remote_url
|
def thumbnail_remote_url
|
||||||
Addressable::URI.parse(@json['icon'].is_a?(Hash) ? @json['icon']['url'] : @json['icon'])&.normalize&.to_s
|
url = Addressable::URI.parse(@json['icon'].is_a?(Hash) ? @json['icon']['url'] : @json['icon'])&.normalize&.to_s
|
||||||
|
url unless unsupported_uri_scheme?(url)
|
||||||
rescue Addressable::URI::InvalidURIError
|
rescue Addressable::URI::InvalidURIError
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,7 +33,10 @@ class ActivityPub::Parser::StatusParser
|
||||||
end
|
end
|
||||||
|
|
||||||
def url
|
def url
|
||||||
url_to_href(@object['url'], 'text/html') if @object['url'].present?
|
return if @object['url'].blank?
|
||||||
|
|
||||||
|
url = url_to_href(@object['url'], 'text/html')
|
||||||
|
url unless unsupported_uri_scheme?(url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def text
|
def text
|
||||||
|
|
|
@ -4,6 +4,7 @@ require 'singleton'
|
||||||
|
|
||||||
class ActivityPub::TagManager
|
class ActivityPub::TagManager
|
||||||
include Singleton
|
include Singleton
|
||||||
|
include JsonLdHelper
|
||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
|
|
||||||
CONTEXT = 'https://www.w3.org/ns/activitystreams'
|
CONTEXT = 'https://www.w3.org/ns/activitystreams'
|
||||||
|
@ -17,7 +18,7 @@ class ActivityPub::TagManager
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_for(target)
|
def url_for(target)
|
||||||
return target.url if target.respond_to?(:local?) && !target.local?
|
return unsupported_uri_scheme?(target.url) ? nil : target.url if target.respond_to?(:local?) && !target.local?
|
||||||
|
|
||||||
return unless target.respond_to?(:object_type)
|
return unless target.respond_to?(:object_type)
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,13 @@
|
||||||
class HttpSignatureDraft
|
class HttpSignatureDraft
|
||||||
REQUEST_TARGET = '(request-target)'
|
REQUEST_TARGET = '(request-target)'
|
||||||
|
|
||||||
def initialize(keypair, key_id, full_path: true)
|
def initialize(keypair, key_id)
|
||||||
@keypair = keypair
|
@keypair = keypair
|
||||||
@key_id = key_id
|
@key_id = key_id
|
||||||
@full_path = full_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_target(verb, url)
|
def request_target(verb, url)
|
||||||
if url.query.nil? || !@full_path
|
if url.query.nil?
|
||||||
"#{verb} #{url.path}"
|
"#{verb} #{url.path}"
|
||||||
else
|
else
|
||||||
"#{verb} #{url.path}?#{url.query}"
|
"#{verb} #{url.path}?#{url.query}"
|
||||||
|
|
|
@ -75,7 +75,6 @@ class Request
|
||||||
@url = Addressable::URI.parse(url).normalize
|
@url = Addressable::URI.parse(url).normalize
|
||||||
@http_client = options.delete(:http_client)
|
@http_client = options.delete(:http_client)
|
||||||
@allow_local = options.delete(:allow_local)
|
@allow_local = options.delete(:allow_local)
|
||||||
@full_path = !options.delete(:omit_query_string)
|
|
||||||
@options = {
|
@options = {
|
||||||
follow: {
|
follow: {
|
||||||
max_hops: 3,
|
max_hops: 3,
|
||||||
|
@ -102,7 +101,7 @@ class Request
|
||||||
|
|
||||||
key_id = ActivityPub::TagManager.instance.key_uri_for(actor)
|
key_id = ActivityPub::TagManager.instance.key_uri_for(actor)
|
||||||
keypair = sign_with.present? ? OpenSSL::PKey::RSA.new(sign_with) : actor.keypair
|
keypair = sign_with.present? ? OpenSSL::PKey::RSA.new(sign_with) : actor.keypair
|
||||||
@signing = HttpSignatureDraft.new(keypair, key_id, full_path: @full_path)
|
@signing = HttpSignatureDraft.new(keypair, key_id)
|
||||||
|
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,20 +57,7 @@ class ActivityPub::FetchRepliesService < BaseService
|
||||||
return unless @allow_synchronous_requests
|
return unless @allow_synchronous_requests
|
||||||
return if non_matching_uri_hosts?(@reference_uri, collection_or_uri)
|
return if non_matching_uri_hosts?(@reference_uri, collection_or_uri)
|
||||||
|
|
||||||
# NOTE: For backward compatibility reasons, Mastodon signs outgoing
|
fetch_resource_without_id_validation(collection_or_uri, nil, raise_on_error: :temporary)
|
||||||
# queries incorrectly by default.
|
|
||||||
#
|
|
||||||
# While this is relevant for all URLs with query strings, this is
|
|
||||||
# the only code path where this happens in practice.
|
|
||||||
#
|
|
||||||
# Therefore, retry with correct signatures if this fails.
|
|
||||||
begin
|
|
||||||
fetch_resource_without_id_validation(collection_or_uri, nil, raise_on_error: :temporary)
|
|
||||||
rescue Mastodon::UnexpectedResponseError => e
|
|
||||||
raise unless e.response && e.response.code == 401 && Addressable::URI.parse(collection_or_uri).query.present?
|
|
||||||
|
|
||||||
fetch_resource_without_id_validation(collection_or_uri, nil, raise_on_error: :temporary, request_options: { omit_query_string: false })
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_replies(items)
|
def filter_replies(items)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class StatusLengthValidator < ActiveModel::Validator
|
class StatusLengthValidator < ActiveModel::Validator
|
||||||
MAX_CHARS = 500
|
MAX_CHARS = (ENV['MAX_CHARS'] || 500).to_i
|
||||||
URL_PLACEHOLDER_CHARS = 23
|
URL_PLACEHOLDER_CHARS = 23
|
||||||
URL_PLACEHOLDER = 'x' * 23
|
URL_PLACEHOLDER = 'x' * 23
|
||||||
|
|
||||||
|
|
16
config/initializers/deprecations.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
if ENV['REDIS_NAMESPACE']
|
||||||
|
es_configured = ENV['ES_ENABLED'] == 'true' || ENV.fetch('ES_HOST', 'localhost') != 'localhost' || ENV.fetch('ES_PORT', '9200') != '9200' || ENV.fetch('ES_PASS', 'password') != 'password'
|
||||||
|
|
||||||
|
warn <<~MESSAGE
|
||||||
|
WARNING: the REDIS_NAMESPACE environment variable is deprecated and will be removed in Mastodon 4.4.0.
|
||||||
|
|
||||||
|
Please see documentation at https://github.com/mastodon/redis_namespace_migration
|
||||||
|
MESSAGE
|
||||||
|
|
||||||
|
warn <<~MESSAGE if es_configured && !ENV['ES_PREFIX']
|
||||||
|
|
||||||
|
In addition, as REDIS_NAMESPACE is being used as a prefix for Elasticsearch, please do not forget to set ES_PREFIX to "#{ENV.fetch('REDIS_NAMESPACE')}".
|
||||||
|
MESSAGE
|
||||||
|
end
|
|
@ -66,18 +66,18 @@ en:
|
||||||
setting_custom_css_lead: 'Be sure to remember: In the unlikely event that you make a mistake in entering your custom CSS and the screen does not display properly, you can disable your custom CSS from the link at the bottom of the sign-in screen. Open the sign-in screen in private mode of your browser, for example, and disable it.'
|
setting_custom_css_lead: 'Be sure to remember: In the unlikely event that you make a mistake in entering your custom CSS and the screen does not display properly, you can disable your custom CSS from the link at the bottom of the sign-in screen. Open the sign-in screen in private mode of your browser, for example, and disable it.'
|
||||||
setting_default_searchability: On kmyblue and Fedibird, the search is based on the search permission setting; on Misskey, all public, local public, and non-public posts are searched regardless of this setting; on Mastodon and Firefish, instead of search permission, the "Make public posts freely searchable on other servers" setting in the profile settings is applied. In Mastodon and Firefish, the "Make public posts freely searchable on other servers" setting in the profile settings is applied instead of the search permission.
|
setting_default_searchability: On kmyblue and Fedibird, the search is based on the search permission setting; on Misskey, all public, local public, and non-public posts are searched regardless of this setting; on Mastodon and Firefish, instead of search permission, the "Make public posts freely searchable on other servers" setting in the profile settings is applied. In Mastodon and Firefish, the "Make public posts freely searchable on other servers" setting in the profile settings is applied instead of the search permission.
|
||||||
setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click
|
setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click
|
||||||
setting_disallow_unlisted_public_searchability: この設定を有効にすると、非収載投稿と検索範囲「誰でも」は両立できず不特定多数からの検索が不可になります。Fedibirdと同じ挙動になります
|
setting_disallow_unlisted_public_searchability: If you enable this setting, unlisted posts and the “everyone” search scope cannot coexist, making it impossible for unspecified users to search your posts.
|
||||||
setting_display_media_default: Hide media marked as sensitive
|
setting_display_media_default: Hide media marked as sensitive
|
||||||
setting_display_media_hide_all: Always hide media
|
setting_display_media_hide_all: Always hide media
|
||||||
setting_display_media_show_all: Always show media
|
setting_display_media_show_all: Always show media
|
||||||
setting_dtl_force_searchability: 'With using #%{tag} tag, your post settings will be changed forcibly'
|
setting_dtl_force_searchability: 'With using #%{tag} tag, your post settings will be changed forcibly'
|
||||||
setting_dtl_force_visibility: 'With using #%{tag} tag, your post settings will be changed forcibly'
|
setting_dtl_force_visibility: 'With using #%{tag} tag, your post settings will be changed forcibly'
|
||||||
setting_emoji_reaction_policy: Even with this setting, users on non-kmyblue servers are free to put their emoji reaction on the post and share it within the same server. If you simply want to remove the emoji reaction from your own screen, you can disable it from the appearance settings
|
setting_emoji_reaction_policy: Even with this setting, users on non-kmyblue servers are free to put their emoji reaction on the post and share it within the same server. If you simply want to remove the emoji reaction from your own screen, you can disable it from the appearance settings
|
||||||
setting_emoji_reaction_streaming_notify_impl2: 当該サーバーの独自機能に対応したアプリを利用時に、絵文字リアクション機能を利用できます。動作確認していないため(そもそもそのようなアプリ自体を確認できていないため)正しく動かない場合があります
|
setting_emoji_reaction_streaming_notify_impl2: You can use the emoji reaction feature when using an app that supports this server’s unique functionality. However, since this has not been tested (and such apps have not even been identified), it may not work correctly.
|
||||||
setting_enable_emoji_reaction: If turn off, other users still can react your posts
|
setting_enable_emoji_reaction: If turn off, other users still can react your posts
|
||||||
setting_enabled_visibilities: If turn off, you cannot select and post the privacy.
|
setting_enabled_visibilities: If turn off, you cannot select and post the privacy.
|
||||||
setting_hide_network: フォローとフォロワーの情報がプロフィールページで見られないようにします
|
setting_hide_network: It will hide the following and follower information from the profile page.
|
||||||
setting_public_post_to_unlisted: 未対応のサードパーティアプリからもローカル公開で投稿できますが、公開投稿はWeb以外できなくなります
|
setting_public_post_to_unlisted: You can still post with local visibility using unsupported third-party apps, but public posts will no longer be possible outside of the web interface.
|
||||||
setting_reject_send_limited_to_suspects: This applies to "Mutual Only" posts. Circle posts will be delivered without exception. Some Misskey servers have independently supported limited posting, but this is a setting for those who are concerned about it, as mutual-only posting exposes some of the users you are mutual with to Misskey users!
|
setting_reject_send_limited_to_suspects: This applies to "Mutual Only" posts. Circle posts will be delivered without exception. Some Misskey servers have independently supported limited posting, but this is a setting for those who are concerned about it, as mutual-only posting exposes some of the users you are mutual with to Misskey users!
|
||||||
setting_reject_unlisted_subscription: Misskey and its forks can **subscribe and search** for "non-following" posts from accounts they do not follow. This differs from kmyblue's behavior. It delivers posts in the specified public range to such servers as "followers only". Please understand, however, that due to its structure, it is difficult to handle perfectly and will occasionally be delivered as non-subscribed.
|
setting_reject_unlisted_subscription: Misskey and its forks can **subscribe and search** for "non-following" posts from accounts they do not follow. This differs from kmyblue's behavior. It delivers posts in the specified public range to such servers as "followers only". Please understand, however, that due to its structure, it is difficult to handle perfectly and will occasionally be delivered as non-subscribed.
|
||||||
setting_reverse_search_quote: Double-quotes will result in a search with a wider range of notation, which is the opposite of Mastodon's default behavior.
|
setting_reverse_search_quote: Double-quotes will result in a search with a wider range of notation, which is the opposite of Mastodon's default behavior.
|
||||||
|
@ -121,10 +121,10 @@ en:
|
||||||
peers_api_enabled: A list of domain names this server has encountered in the fediverse. No data is included here about whether you federate with a given server, just that your server knows about it. This is used by services that collect statistics on federation in a general sense.
|
peers_api_enabled: A list of domain names this server has encountered in the fediverse. No data is included here about whether you federate with a given server, just that your server knows about it. This is used by services that collect statistics on federation in a general sense.
|
||||||
profile_directory: The profile directory lists all users who have opted-in to be discoverable.
|
profile_directory: The profile directory lists all users who have opted-in to be discoverable.
|
||||||
receive_other_servers_emoji_reaction: It can cause load. It is recommended to enable it only when there are few people.
|
receive_other_servers_emoji_reaction: It can cause load. It is recommended to enable it only when there are few people.
|
||||||
registrations_end_hour: 新規登録が承認なしで可能な時間帯の開始時間を指定します。これより前の時間に登録することはできません。終了時間より後にすることはできません。この時間帯から外れた新規登録には、別途承認が必要となります。
|
registrations_end_hour: Specifies the end of the time window during which new registrations can be made without approval. Registrations cannot be made before the start time or after this end time. Registrations outside this time window will require separate approval.
|
||||||
registrations_limit: 現在のユーザー数がこれを超過すると、管理者がこの数値を増やさない限り新規登録できません。0を指定すると、この制限を無効化します。
|
registrations_limit: If the current number of users exceeds this value, new registrations will not be possible unless the administrator increases the limit. Setting this to 0 disables the restriction.
|
||||||
registrations_limit_per_day: 本日登録されたユーザー数がこれを超過すると、UTC時刻で翌日0時にならない限り新規登録できません。0を指定すると、この制限を無効化します。
|
registrations_limit_per_day: If the number of users registered today exceeds this value, no new registrations will be allowed until 00:00 UTC the next day. Setting this to 0 disables the restriction.
|
||||||
registrations_start_hour: 新規登録が承認なしで可能な時間帯の終了時間を指定します。これより後の時間に登録することはできません。開始時間より前にすることはできません。この時間帯から外れた新規登録には、別途承認が必要となります。
|
registrations_start_hour: Specifies the start of the time window during which new registrations can be made without approval. Registrations cannot be made before this time or after the end time. Registrations outside this time window will require separate approval.
|
||||||
require_invite_text: When sign-ups require manual approval, make the “Why do you want to join?” text input mandatory rather than optional
|
require_invite_text: When sign-ups require manual approval, make the “Why do you want to join?” text input mandatory rather than optional
|
||||||
site_contact_email: How people can reach you for legal or support inquiries.
|
site_contact_email: How people can reach you for legal or support inquiries.
|
||||||
site_contact_username: How people can reach you on Mastodon.
|
site_contact_username: How people can reach you on Mastodon.
|
||||||
|
@ -201,8 +201,8 @@ en:
|
||||||
discoverable: Feature profile and posts in discovery algorithms
|
discoverable: Feature profile and posts in discovery algorithms
|
||||||
fields:
|
fields:
|
||||||
examples:
|
examples:
|
||||||
name_1: 例) GitHub
|
name_1: Example Gitea
|
||||||
value_1: 例) https://github.com/xxxxxx
|
value_1: Example https://giteahub.com
|
||||||
name: Label
|
name: Label
|
||||||
value: Content
|
value: Content
|
||||||
indexable: Include public posts in search results
|
indexable: Include public posts in search results
|
||||||
|
|
|
@ -59,7 +59,7 @@ services:
|
||||||
web:
|
web:
|
||||||
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
||||||
build: .
|
build: .
|
||||||
image: kmyblue:18.0-dev
|
image: kmyblue:18.1
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: bundle exec puma -C config/puma.rb
|
command: bundle exec puma -C config/puma.rb
|
||||||
|
@ -83,7 +83,7 @@ services:
|
||||||
build:
|
build:
|
||||||
dockerfile: ./streaming/Dockerfile
|
dockerfile: ./streaming/Dockerfile
|
||||||
context: .
|
context: .
|
||||||
image: kmyblue-streaming:18.0-dev
|
image: kmyblue-streaming:18.1
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: node ./streaming/index.js
|
command: node ./streaming/index.js
|
||||||
|
@ -101,7 +101,7 @@ services:
|
||||||
|
|
||||||
sidekiq:
|
sidekiq:
|
||||||
build: .
|
build: .
|
||||||
image: kmyblue:18.0-dev
|
image: kmyblue:18.1
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: bundle exec sidekiq
|
command: bundle exec sidekiq
|
||||||
|
|
|
@ -13,13 +13,13 @@ module Mastodon
|
||||||
end
|
end
|
||||||
|
|
||||||
def kmyblue_minor
|
def kmyblue_minor
|
||||||
0
|
1
|
||||||
end
|
end
|
||||||
|
|
||||||
def kmyblue_flag
|
def kmyblue_flag
|
||||||
# 'LTS'
|
# 'LTS'
|
||||||
'dev'
|
# 'dev'
|
||||||
# nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def major
|
def major
|
||||||
|
@ -35,7 +35,7 @@ module Mastodon
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_prerelease
|
def default_prerelease
|
||||||
'alpha.4'
|
'alpha.5'
|
||||||
end
|
end
|
||||||
|
|
||||||
def prerelease
|
def prerelease
|
||||||
|
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.4 KiB |
BIN
public/favicon.ico
Executable file → Normal file
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 6 KiB |