Merge commit '0ad66175bf59a34b03d9ab2347181548d07089ea' into glitch-soc/merge-upstream

Conflicts:
- `app/javascript/mastodon/features/compose/components/compose_form.jsx`:
  Upstream changed one import, close to a glitch-soc-only import to handle
  different max character counts.
  Ported upstream's change.
This commit is contained in:
Claire 2023-10-24 18:41:20 +02:00
commit b0c76eaadd
49 changed files with 242 additions and 226 deletions

View File

@ -9,6 +9,10 @@ module FormattingHelper
TextFormatter.new(text, options).to_s TextFormatter.new(text, options).to_s
end end
def url_for_preview_card(preview_card)
preview_card.url
end
def extract_status_plain_text(status) def extract_status_plain_text(status)
PlainTextFormatter.new(status.text, status.local?).to_s PlainTextFormatter.new(status.text, status.local?).to_s
end end

View File

@ -1,7 +1,7 @@
import { render, fireEvent, screen } from '@testing-library/react'; import { render, fireEvent, screen } from '@testing-library/react';
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import Button from '../button'; import { Button } from '../button';
describe('<Button />', () => { describe('<Button />', () => {
it('renders a button element', () => { it('renders a button element', () => {

View File

@ -15,7 +15,7 @@ import { VerifiedBadge } from 'mastodon/components/verified_badge';
import { me } from '../initial_state'; import { me } from '../initial_state';
import { Avatar } from './avatar'; import { Avatar } from './avatar';
import Button from './button'; import { Button } from './button';
import { FollowersCounter } from './counters'; import { FollowersCounter } from './counters';
import { DisplayName } from './display_name'; import { DisplayName } from './display_name';
import { IconButton } from './icon_button'; import { IconButton } from './icon_button';

View File

@ -1,58 +0,0 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import classNames from 'classnames';
export default class Button extends PureComponent {
static propTypes = {
text: PropTypes.node,
type: PropTypes.string,
onClick: PropTypes.func,
disabled: PropTypes.bool,
block: PropTypes.bool,
secondary: PropTypes.bool,
className: PropTypes.string,
title: PropTypes.string,
children: PropTypes.node,
};
static defaultProps = {
type: 'button',
};
handleClick = (e) => {
if (!this.props.disabled && this.props.onClick) {
this.props.onClick(e);
}
};
setRef = (c) => {
this.node = c;
};
focus() {
this.node.focus();
}
render () {
const className = classNames('button', this.props.className, {
'button-secondary': this.props.secondary,
'button--block': this.props.block,
});
return (
<button
className={className}
disabled={this.props.disabled}
onClick={this.handleClick}
ref={this.setRef}
title={this.props.title}
type={this.props.type}
>
{this.props.text || this.props.children}
</button>
);
}
}

View File

@ -0,0 +1,58 @@
import { useCallback } from 'react';
import classNames from 'classnames';
interface BaseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
block?: boolean;
secondary?: boolean;
text?: JSX.Element;
}
interface PropsWithChildren extends BaseProps {
text?: never;
}
interface PropsWithText extends BaseProps {
text: JSX.Element;
children: never;
}
type Props = PropsWithText | PropsWithChildren;
export const Button: React.FC<Props> = ({
text,
type = 'button',
onClick,
disabled,
block,
secondary,
className,
title,
children,
...props
}) => {
const handleClick = useCallback<React.MouseEventHandler<HTMLButtonElement>>(
(e) => {
if (!disabled && onClick) {
onClick(e);
}
},
[disabled, onClick],
);
return (
<button
className={classNames('button', className, {
'button-secondary': secondary,
'button--block': block,
})}
disabled={disabled}
onClick={handleClick}
title={title}
type={type}
{...props}
>
{text ?? children}
</button>
);
};

View File

@ -11,7 +11,7 @@ import { HotKeys } from 'react-hotkeys';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder'; import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder';
import { withOptionalRouter, WithRouterPropTypes } from 'mastodon/utils/react_router'; import { withOptionalRouter, WithOptionalRouterPropTypes } from 'mastodon/utils/react_router';
import Card from '../features/status/components/card'; import Card from '../features/status/components/card';
// We use the component (and not the container) since we do not want // We use the component (and not the container) since we do not want
@ -113,7 +113,7 @@ class Status extends ImmutablePureComponent {
inUse: PropTypes.bool, inUse: PropTypes.bool,
available: PropTypes.bool, available: PropTypes.bool,
}), }),
...WithRouterPropTypes, ...WithOptionalRouterPropTypes,
}; };
// Avoid checking props that are functions (and whose equality will always // Avoid checking props that are functions (and whose equality will always

View File

@ -11,7 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { Avatar } from 'mastodon/components/avatar'; import { Avatar } from 'mastodon/components/avatar';
import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge'; import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { FollowersCounter, FollowingCounter, StatusesCounter } from 'mastodon/components/counters'; import { FollowersCounter, FollowingCounter, StatusesCounter } from 'mastodon/components/counters';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button'; import { IconButton } from 'mastodon/components/icon_button';

View File

@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { revealAccount } from 'mastodon/actions/accounts'; import { revealAccount } from 'mastodon/actions/accounts';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { domain } from 'mastodon/initial_state'; import { domain } from 'mastodon/initial_state';
const mapDispatchToProps = (dispatch, { accountId }) => ({ const mapDispatchToProps = (dispatch, { accountId }) => ({

View File

@ -14,7 +14,7 @@ import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/
import AutosuggestInput from '../../../components/autosuggest_input'; import AutosuggestInput from '../../../components/autosuggest_input';
import AutosuggestTextarea from '../../../components/autosuggest_textarea'; import AutosuggestTextarea from '../../../components/autosuggest_textarea';
import Button from '../../../components/button'; import { Button } from '../../../components/button';
import { maxChars } from '../../../initial_state'; import { maxChars } from '../../../initial_state';
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container'; import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
import LanguageDropdown from '../containers/language_dropdown_container'; import LanguageDropdown from '../containers/language_dropdown_container';

View File

@ -17,7 +17,7 @@ import {
} from 'mastodon/actions/accounts'; } from 'mastodon/actions/accounts';
import { openModal } from 'mastodon/actions/modal'; import { openModal } from 'mastodon/actions/modal';
import { Avatar } from 'mastodon/components/avatar'; import { Avatar } from 'mastodon/components/avatar';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { DisplayName } from 'mastodon/components/display_name'; import { DisplayName } from 'mastodon/components/display_name';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
import { autoPlayGif, me, unfollowModal } from 'mastodon/initial_state'; import { autoPlayGif, me, unfollowModal } from 'mastodon/initial_state';

View File

@ -45,13 +45,10 @@ class Statuses extends PureComponent {
const emptyMessage = <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />; const emptyMessage = <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />;
return ( return (
<>
<DismissableBanner id='explore/statuses'>
<FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.' />
</DismissableBanner>
<StatusList <StatusList
trackScroll trackScroll
prepend={<DismissableBanner id='explore/statuses'><FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.' /></DismissableBanner>}
alwaysPrepend
timelineId='explore' timelineId='explore'
statusIds={statusIds} statusIds={statusIds}
scrollKey='explore-statuses' scrollKey='explore-statuses'
@ -62,7 +59,6 @@ class Statuses extends PureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
withCounters withCounters
/> />
</>
); );
} }

View File

@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { toServerSideType } from 'mastodon/utils/filters'; import { toServerSideType } from 'mastodon/utils/filters';
const mapStateToProps = (state, { filterId }) => ({ const mapStateToProps = (state, { filterId }) => ({

View File

@ -4,7 +4,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
const messages = defineMessages({ const messages = defineMessages({

View File

@ -11,7 +11,7 @@ import { throttle, escapeRegExp } from 'lodash';
import { openModal, closeModal } from 'mastodon/actions/modal'; import { openModal, closeModal } from 'mastodon/actions/modal';
import api from 'mastodon/api'; import api from 'mastodon/api';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { registrationsOpen, sso_redirect } from 'mastodon/initial_state'; import { registrationsOpen, sso_redirect } from 'mastodon/initial_state';

View File

@ -6,7 +6,7 @@ import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { changeListEditorTitle, submitListEditor } from 'mastodon/actions/lists'; import { changeListEditorTitle, submitListEditor } from 'mastodon/actions/lists';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
const messages = defineMessages({ const messages = defineMessages({
label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' }, label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' },

View File

@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { requestBrowserPermission } from 'mastodon/actions/notifications'; import { requestBrowserPermission } from 'mastodon/actions/notifications';
import { changeSetting } from 'mastodon/actions/settings'; import { changeSetting } from 'mastodon/actions/settings';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button'; import { IconButton } from 'mastodon/components/icon_button';

View File

@ -7,7 +7,7 @@ import { List as ImmutableList } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import Option from './components/option'; import Option from './components/option';

View File

@ -11,7 +11,7 @@ import { createSelector } from 'reselect';
import Toggle from 'react-toggle'; import Toggle from 'react-toggle';
import { fetchAccount } from 'mastodon/actions/accounts'; import { fetchAccount } from 'mastodon/actions/accounts';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
const messages = defineMessages({ const messages = defineMessages({

View File

@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import Option from './components/option'; import Option from './components/option';

View File

@ -7,7 +7,7 @@ import { OrderedSet } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import StatusCheckBox from 'mastodon/features/report/containers/status_check_box_container'; import StatusCheckBox from 'mastodon/features/report/containers/status_check_box_container';

View File

@ -11,7 +11,7 @@ import {
muteAccount, muteAccount,
blockAccount, blockAccount,
} from 'mastodon/actions/accounts'; } from 'mastodon/actions/accounts';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
const mapStateToProps = () => ({}); const mapStateToProps = () => ({});

View File

@ -9,7 +9,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { followAccount } from 'mastodon/actions/accounts'; import { followAccount } from 'mastodon/actions/accounts';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { IconButton } from 'mastodon/components/icon_button'; import { IconButton } from 'mastodon/components/icon_button';
import Option from 'mastodon/features/report/components/option'; import Option from 'mastodon/features/report/components/option';
import { languages as preloadedLanguages } from 'mastodon/initial_state'; import { languages as preloadedLanguages } from 'mastodon/initial_state';

View File

@ -8,7 +8,7 @@ import { connect } from 'react-redux';
import { blockAccount } from '../../../actions/accounts'; import { blockAccount } from '../../../actions/accounts';
import { closeModal } from '../../../actions/modal'; import { closeModal } from '../../../actions/modal';
import { initReport } from '../../../actions/reports'; import { initReport } from '../../../actions/reports';
import Button from '../../../components/button'; import { Button } from '../../../components/button';
import { makeGetAccount } from '../../../selectors'; import { makeGetAccount } from '../../../selectors';
const makeMapStateToProps = () => { const makeMapStateToProps = () => {
@ -51,10 +51,6 @@ class BlockModal extends PureComponent {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
componentDidMount() {
this.button.focus();
}
handleClick = () => { handleClick = () => {
this.props.onClose(); this.props.onClose();
this.props.onConfirm(this.props.account); this.props.onConfirm(this.props.account);
@ -69,10 +65,6 @@ class BlockModal extends PureComponent {
this.props.onClose(); this.props.onClose();
}; };
setRef = (c) => {
this.button = c;
};
render () { render () {
const { account } = this.props; const { account } = this.props;
@ -95,7 +87,7 @@ class BlockModal extends PureComponent {
<Button onClick={this.handleSecondary} className='confirmation-modal__secondary-button'> <Button onClick={this.handleSecondary} className='confirmation-modal__secondary-button'>
<FormattedMessage id='confirmations.block.block_and_report' defaultMessage='Block & Report' /> <FormattedMessage id='confirmations.block.block_and_report' defaultMessage='Block & Report' />
</Button> </Button>
<Button onClick={this.handleClick} ref={this.setRef}> <Button onClick={this.handleClick} autoFocus>
<FormattedMessage id='confirmations.block.confirm' defaultMessage='Block' /> <FormattedMessage id='confirmations.block.confirm' defaultMessage='Block' />
</Button> </Button>
</div> </div>

View File

@ -16,7 +16,7 @@ import PrivacyDropdown from 'mastodon/features/compose/components/privacy_dropdo
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { Avatar } from '../../../components/avatar'; import { Avatar } from '../../../components/avatar';
import Button from '../../../components/button'; import { Button } from '../../../components/button';
import { DisplayName } from '../../../components/display_name'; import { DisplayName } from '../../../components/display_name';
import { RelativeTimestamp } from '../../../components/relative_timestamp'; import { RelativeTimestamp } from '../../../components/relative_timestamp';
import StatusContent from '../../../components/status_content'; import StatusContent from '../../../components/status_content';
@ -55,10 +55,6 @@ class BoostModal extends ImmutablePureComponent {
...WithRouterPropTypes, ...WithRouterPropTypes,
}; };
componentDidMount() {
this.button.focus();
}
handleReblog = () => { handleReblog = () => {
this.props.onReblog(this.props.status, this.props.privacy); this.props.onReblog(this.props.status, this.props.privacy);
this.props.onClose(); this.props.onClose();
@ -76,10 +72,6 @@ class BoostModal extends ImmutablePureComponent {
return document.getElementsByClassName('modal-root__container')[0]; return document.getElementsByClassName('modal-root__container')[0];
}; };
setRef = (c) => {
this.button = c;
};
render () { render () {
const { status, privacy, intl } = this.props; const { status, privacy, intl } = this.props;
const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog; const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog;
@ -133,7 +125,7 @@ class BoostModal extends ImmutablePureComponent {
onChange={this.props.onChangeBoostPrivacy} onChange={this.props.onChangeBoostPrivacy}
/> />
)} )}
<Button text={intl.formatMessage(buttonText)} onClick={this.handleReblog} ref={this.setRef} /> <Button text={intl.formatMessage(buttonText)} onClick={this.handleReblog} autoFocus />
</div> </div>
</div> </div>
); );

View File

@ -7,7 +7,7 @@ import classNames from 'classnames';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import { autoPlayGif } from 'mastodon/initial_state'; import { autoPlayGif } from 'mastodon/initial_state';

View File

@ -3,7 +3,7 @@ import { PureComponent } from 'react';
import { injectIntl, FormattedMessage } from 'react-intl'; import { injectIntl, FormattedMessage } from 'react-intl';
import Button from '../../../components/button'; import { Button } from '../../../components/button';
class ConfirmationModal extends PureComponent { class ConfirmationModal extends PureComponent {
@ -22,10 +22,6 @@ class ConfirmationModal extends PureComponent {
closeWhenConfirm: true, closeWhenConfirm: true,
}; };
componentDidMount() {
this.button.focus();
}
handleClick = () => { handleClick = () => {
if (this.props.closeWhenConfirm) { if (this.props.closeWhenConfirm) {
this.props.onClose(); this.props.onClose();
@ -42,10 +38,6 @@ class ConfirmationModal extends PureComponent {
this.props.onClose(); this.props.onClose();
}; };
setRef = (c) => {
this.button = c;
};
render () { render () {
const { message, confirm, secondary } = this.props; const { message, confirm, secondary } = this.props;
@ -62,7 +54,7 @@ class ConfirmationModal extends PureComponent {
{secondary !== undefined && ( {secondary !== undefined && (
<Button text={secondary} onClick={this.handleSecondary} className='confirmation-modal__secondary-button' /> <Button text={secondary} onClick={this.handleSecondary} className='confirmation-modal__secondary-button' />
)} )}
<Button text={confirm} onClick={this.handleClick} ref={this.setRef} /> <Button text={confirm} onClick={this.handleClick} autoFocus />
</div> </div>
</div> </div>
); );

View File

@ -16,7 +16,7 @@ import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js'; import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
import Button from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { GIFV } from 'mastodon/components/gifv'; import { GIFV } from 'mastodon/components/gifv';
import { IconButton } from 'mastodon/components/icon_button'; import { IconButton } from 'mastodon/components/icon_button';
import Audio from 'mastodon/features/audio'; import Audio from 'mastodon/features/audio';

View File

@ -10,7 +10,7 @@ import Toggle from 'react-toggle';
import { muteAccount } from '../../../actions/accounts'; import { muteAccount } from '../../../actions/accounts';
import { closeModal } from '../../../actions/modal'; import { closeModal } from '../../../actions/modal';
import { toggleHideNotifications, changeMuteDuration } from '../../../actions/mutes'; import { toggleHideNotifications, changeMuteDuration } from '../../../actions/mutes';
import Button from '../../../components/button'; import { Button } from '../../../components/button';
const messages = defineMessages({ const messages = defineMessages({
minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' }, minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' },
@ -63,10 +63,6 @@ class MuteModal extends PureComponent {
onChangeMuteDuration: PropTypes.func.isRequired, onChangeMuteDuration: PropTypes.func.isRequired,
}; };
componentDidMount() {
this.button.focus();
}
handleClick = () => { handleClick = () => {
this.props.onClose(); this.props.onClose();
this.props.onConfirm(this.props.account, this.props.notifications, this.props.muteDuration); this.props.onConfirm(this.props.account, this.props.notifications, this.props.muteDuration);
@ -76,10 +72,6 @@ class MuteModal extends PureComponent {
this.props.onClose(); this.props.onClose();
}; };
setRef = (c) => {
this.button = c;
};
toggleNotifications = () => { toggleNotifications = () => {
this.props.onToggleNotifications(); this.props.onToggleNotifications();
}; };
@ -134,7 +126,7 @@ class MuteModal extends PureComponent {
<Button onClick={this.handleCancel} className='mute-modal__cancel-button'> <Button onClick={this.handleCancel} className='mute-modal__cancel-button'>
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' /> <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
</Button> </Button>
<Button onClick={this.handleClick} ref={this.setRef}> <Button onClick={this.handleClick} autoFocus>
<FormattedMessage id='confirmations.mute.confirm' defaultMessage='Mute' /> <FormattedMessage id='confirmations.mute.confirm' defaultMessage='Mute' />
</Button> </Button>
</div> </div>

View File

@ -163,13 +163,13 @@
"confirmation_modal.cancel": "Annulearje", "confirmation_modal.cancel": "Annulearje",
"confirmations.block.block_and_report": "Blokkearje en rapportearje", "confirmations.block.block_and_report": "Blokkearje en rapportearje",
"confirmations.block.confirm": "Blokkearje", "confirmations.block.confirm": "Blokkearje",
"confirmations.block.message": "Bisto wis datsto {name} blokkearje wolst?", "confirmations.block.message": "Binne jo wis dat jo {name} blokkearje wolle?",
"confirmations.cancel_follow_request.confirm": "Fersyk annulearje", "confirmations.cancel_follow_request.confirm": "Fersyk annulearje",
"confirmations.cancel_follow_request.message": "Binne jo wis dat jo jo fersyk om {name} te folgjen annulearje wolle?", "confirmations.cancel_follow_request.message": "Binne jo wis dat jo jo fersyk om {name} te folgjen annulearje wolle?",
"confirmations.delete.confirm": "Fuortsmite", "confirmations.delete.confirm": "Fuortsmite",
"confirmations.delete.message": "Binne jo wis dat jo dit berjocht fuortsmite wolle?", "confirmations.delete.message": "Binne jo wis dat jo dit berjocht fuortsmite wolle?",
"confirmations.delete_list.confirm": "Fuortsmite", "confirmations.delete_list.confirm": "Fuortsmite",
"confirmations.delete_list.message": "Bisto wis datsto dizze list foar permanint fuortsmite wolst?", "confirmations.delete_list.message": "Binne jo wis dat jo dizze list foar permanint fuortsmite wolle?",
"confirmations.discard_edit_media.confirm": "Fuortsmite", "confirmations.discard_edit_media.confirm": "Fuortsmite",
"confirmations.discard_edit_media.message": "Jo hawwe net-bewarre wizigingen yn de mediabeskriuwing of foarfertoaning, wolle jo dizze dochs fuortsmite?", "confirmations.discard_edit_media.message": "Jo hawwe net-bewarre wizigingen yn de mediabeskriuwing of foarfertoaning, wolle jo dizze dochs fuortsmite?",
"confirmations.domain_block.confirm": "Alles fan dit domein blokkearje", "confirmations.domain_block.confirm": "Alles fan dit domein blokkearje",
@ -177,16 +177,16 @@
"confirmations.edit.confirm": "Bewurkje", "confirmations.edit.confirm": "Bewurkje",
"confirmations.edit.message": "Troch no te bewurkjen sil it berjocht dat jo no oan it skriuwen binne oerskreaun wurde. Wolle jo trochgean?", "confirmations.edit.message": "Troch no te bewurkjen sil it berjocht dat jo no oan it skriuwen binne oerskreaun wurde. Wolle jo trochgean?",
"confirmations.logout.confirm": "Ofmelde", "confirmations.logout.confirm": "Ofmelde",
"confirmations.logout.message": "Bisto wis datsto ôfmelde wolst?", "confirmations.logout.message": "Binne jo wis dat jo ôfmelde wolle?",
"confirmations.mute.confirm": "Negearje", "confirmations.mute.confirm": "Negearje",
"confirmations.mute.explanation": "Dit sil berjochten fan harren en berjochten dêrt se yn fermeld wurde ûnsichtber meitsje, mar se sille berjochten noch hieltyd sjen kinne en jo folgje kinne.", "confirmations.mute.explanation": "Dit sil berjochten fan harren en berjochten dêrt se yn fermeld wurde ûnsichtber meitsje, mar se sille jo berjochten noch hieltyd sjen kinne en jo folgje kinne.",
"confirmations.mute.message": "Binne jo wis dat jo {name} negearje wolle?", "confirmations.mute.message": "Binne jo wis dat jo {name} negearje wolle?",
"confirmations.redraft.confirm": "Fuortsmite en opnij opstelle", "confirmations.redraft.confirm": "Fuortsmite en opnij opstelle",
"confirmations.redraft.message": "Binne jo wis dat jo dit berjocht fuortsmite en opnij opstelle wolle? Favoriten en boosts geane dan ferlern en reaksjes op it oarspronklike berjocht reitsje jo kwyt.", "confirmations.redraft.message": "Binne jo wis dat jo dit berjocht fuortsmite en opnij opstelle wolle? Favoriten en boosts geane dan ferlern en reaksjes op it oarspronklike berjocht reitsje jo kwyt.",
"confirmations.reply.confirm": "Reagearje", "confirmations.reply.confirm": "Reagearje",
"confirmations.reply.message": "Troch no te reagearjen sil it berjocht dat jo no oan it skriuwen binne oerskreaun wurde. Wolle jo trochgean?", "confirmations.reply.message": "Troch no te reagearjen sil it berjocht dat jo no oan it skriuwen binne oerskreaun wurde. Wolle jo trochgean?",
"confirmations.unfollow.confirm": "Net mear folgje", "confirmations.unfollow.confirm": "Net mear folgje",
"confirmations.unfollow.message": "Bisto wis datsto {name} net mear folgje wolst?", "confirmations.unfollow.message": "Binne jo wis dat jo {name} net mear folgje wolle?",
"conversation.delete": "Petear fuortsmite", "conversation.delete": "Petear fuortsmite",
"conversation.mark_as_read": "As lêzen markearje", "conversation.mark_as_read": "As lêzen markearje",
"conversation.open": "Petear toane", "conversation.open": "Petear toane",
@ -351,7 +351,7 @@
"keyboard_shortcuts.local": "to open local timeline", "keyboard_shortcuts.local": "to open local timeline",
"keyboard_shortcuts.mention": "Skriuwer fermelde", "keyboard_shortcuts.mention": "Skriuwer fermelde",
"keyboard_shortcuts.muted": "to open muted users list", "keyboard_shortcuts.muted": "to open muted users list",
"keyboard_shortcuts.my_profile": "Dyn profyl iepenje", "keyboard_shortcuts.my_profile": "Jo profyl iepenje",
"keyboard_shortcuts.notifications": "Meldingen toane", "keyboard_shortcuts.notifications": "Meldingen toane",
"keyboard_shortcuts.open_media": "Media iepenje", "keyboard_shortcuts.open_media": "Media iepenje",
"keyboard_shortcuts.pinned": "Fêstsette berjochten toane", "keyboard_shortcuts.pinned": "Fêstsette berjochten toane",
@ -421,20 +421,20 @@
"navigation_bar.public_timeline": "Globale tiidline", "navigation_bar.public_timeline": "Globale tiidline",
"navigation_bar.search": "Sykje", "navigation_bar.search": "Sykje",
"navigation_bar.security": "Befeiliging", "navigation_bar.security": "Befeiliging",
"not_signed_in_indicator.not_signed_in": "Do moatst oanmelde om tagong ta dizze ynformaasje te krijen.", "not_signed_in_indicator.not_signed_in": "Jo moatte oanmelde om tagong ta dizze ynformaasje te krijen.",
"notification.admin.report": "{name} hat {target} rapportearre", "notification.admin.report": "{name} hat {target} rapportearre",
"notification.admin.sign_up": "{name} hat harren registrearre", "notification.admin.sign_up": "{name} hat harren registrearre",
"notification.favourite": "{name} hat jo berjocht as favoryt markearre", "notification.favourite": "{name} hat jo berjocht as favoryt markearre",
"notification.follow": "{name} folget dy", "notification.follow": "{name} folget dy",
"notification.follow_request": "{name} hat dy in folchfersyk stjoerd", "notification.follow_request": "{name} hat dy in folchfersyk stjoerd",
"notification.mention": "{name} hat dy fermeld", "notification.mention": "{name} hat dy fermeld",
"notification.own_poll": "Dyn poll is beëinige", "notification.own_poll": "Jo poll is beëinige",
"notification.poll": "In enkête dêrt jo yn stimd hawwe is beëinige", "notification.poll": "In enkête dêrt jo yn stimd hawwe is beëinige",
"notification.reblog": "{name} hat jo berjocht boost", "notification.reblog": "{name} hat jo berjocht boost",
"notification.status": "{name} hat in berjocht pleatst", "notification.status": "{name} hat in berjocht pleatst",
"notification.update": "{name} hat in berjocht bewurke", "notification.update": "{name} hat in berjocht bewurke",
"notifications.clear": "Meldingen wiskje", "notifications.clear": "Meldingen wiskje",
"notifications.clear_confirmation": "Bisto wis datsto al dyn meldingen permanint fuortsmite wolst?", "notifications.clear_confirmation": "Binne jo wis dat jo al jo meldingen permanint fuortsmite wolle?",
"notifications.column_settings.admin.report": "Nije rapportaazjes:", "notifications.column_settings.admin.report": "Nije rapportaazjes:",
"notifications.column_settings.admin.sign_up": "Nije registraasjes:", "notifications.column_settings.admin.sign_up": "Nije registraasjes:",
"notifications.column_settings.alert": "Desktopmeldingen", "notifications.column_settings.alert": "Desktopmeldingen",

View File

@ -204,7 +204,7 @@
"dismissable_banner.explore_links": "이 소식들은 오늘 소셜 웹에서 가장 많이 공유된 내용들입니다. 새 소식을 더 많은 사람들이 공유할수록 높은 순위가 됩니다.", "dismissable_banner.explore_links": "이 소식들은 오늘 소셜 웹에서 가장 많이 공유된 내용들입니다. 새 소식을 더 많은 사람들이 공유할수록 높은 순위가 됩니다.",
"dismissable_banner.explore_statuses": "이 게시물들은 오늘 소셜 웹에서 호응을 얻고 있는 게시물들입니다. 부스트와 관심을 받는 새로운 글들이 높은 순위가 됩니다.", "dismissable_banner.explore_statuses": "이 게시물들은 오늘 소셜 웹에서 호응을 얻고 있는 게시물들입니다. 부스트와 관심을 받는 새로운 글들이 높은 순위가 됩니다.",
"dismissable_banner.explore_tags": "이 해시태그들은 이 서버와 분산화된 네트워크의 다른 서버에서 사람들의 인기를 끌고 있는 것들입니다.", "dismissable_banner.explore_tags": "이 해시태그들은 이 서버와 분산화된 네트워크의 다른 서버에서 사람들의 인기를 끌고 있는 것들입니다.",
"dismissable_banner.public_timeline": "이것들은 {domain}에 있는 사람들이 팔로우한 사람들의 최신 게시물들입니다.", "dismissable_banner.public_timeline": "{domain} 사람들이 팔로우하는 소셜 웹 사람들의 최신 공개 게시물입니다.",
"embed.instructions": "아래의 코드를 복사하여 대화를 원하는 곳으로 공유하세요.", "embed.instructions": "아래의 코드를 복사하여 대화를 원하는 곳으로 공유하세요.",
"embed.preview": "이렇게 표시됩니다:", "embed.preview": "이렇게 표시됩니다:",
"emoji_button.activity": "활동", "emoji_button.activity": "활동",

View File

@ -153,7 +153,7 @@
"compose_form.publish": "Legg ut", "compose_form.publish": "Legg ut",
"compose_form.publish_form": "Legg ut", "compose_form.publish_form": "Legg ut",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Gøym", "compose_form.save_changes": "Lagre endringar",
"compose_form.sensitive.hide": "{count, plural, one {Marker mediet som ømtolig} other {Marker media som ømtolige}}", "compose_form.sensitive.hide": "{count, plural, one {Marker mediet som ømtolig} other {Marker media som ømtolige}}",
"compose_form.sensitive.marked": "{count, plural, one {Mediet er markert som ømtolig} other {Media er markerte som ømtolige}}", "compose_form.sensitive.marked": "{count, plural, one {Mediet er markert som ømtolig} other {Media er markerte som ømtolige}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Mediet er ikkje markert som ømtolig} other {Media er ikkje markerte som ømtolige}}", "compose_form.sensitive.unmarked": "{count, plural, one {Mediet er ikkje markert som ømtolig} other {Media er ikkje markerte som ømtolige}}",
@ -171,7 +171,7 @@
"confirmations.delete_list.confirm": "Slett", "confirmations.delete_list.confirm": "Slett",
"confirmations.delete_list.message": "Er du sikker på at du vil sletta denne lista for alltid?", "confirmations.delete_list.message": "Er du sikker på at du vil sletta denne lista for alltid?",
"confirmations.discard_edit_media.confirm": "Forkast", "confirmations.discard_edit_media.confirm": "Forkast",
"confirmations.discard_edit_media.message": "Du har ulagra endringar i mediaskildringa eller førehandsvisinga. Vil du forkaste dei likevel?", "confirmations.discard_edit_media.message": "Du har ulagra endringar i mediaskildringa eller førehandsvisinga. Vil du forkasta dei likevel?",
"confirmations.domain_block.confirm": "Skjul alt frå domenet", "confirmations.domain_block.confirm": "Skjul alt frå domenet",
"confirmations.domain_block.message": "Er du heilt, heilt sikker på at du vil skjula heile {domain}? I dei fleste tilfelle er det godt nok og føretrekt med nokre få målretta blokkeringar eller målbindingar. Du kjem ikkje til å sjå innhald frå domenet i fødererte tidsliner eller i varsla dine. Fylgjarane dine frå domenet vert fjerna.", "confirmations.domain_block.message": "Er du heilt, heilt sikker på at du vil skjula heile {domain}? I dei fleste tilfelle er det godt nok og føretrekt med nokre få målretta blokkeringar eller målbindingar. Du kjem ikkje til å sjå innhald frå domenet i fødererte tidsliner eller i varsla dine. Fylgjarane dine frå domenet vert fjerna.",
"confirmations.edit.confirm": "Rediger", "confirmations.edit.confirm": "Rediger",
@ -285,7 +285,7 @@
"footer.privacy_policy": "Personvernsreglar", "footer.privacy_policy": "Personvernsreglar",
"footer.source_code": "Vis kjeldekode", "footer.source_code": "Vis kjeldekode",
"footer.status": "Status", "footer.status": "Status",
"generic.saved": "Gøymt", "generic.saved": "Lagra",
"getting_started.heading": "Kom i gang", "getting_started.heading": "Kom i gang",
"hashtag.column_header.tag_mode.all": "og {additional}", "hashtag.column_header.tag_mode.all": "og {additional}",
"hashtag.column_header.tag_mode.any": "eller {additional}", "hashtag.column_header.tag_mode.any": "eller {additional}",
@ -314,7 +314,7 @@
"home.pending_critical_update.link": "Sjå oppdateringar", "home.pending_critical_update.link": "Sjå oppdateringar",
"home.pending_critical_update.title": "Kritisk sikkerheitsoppdatering er tilgjengeleg!", "home.pending_critical_update.title": "Kritisk sikkerheitsoppdatering er tilgjengeleg!",
"home.show_announcements": "Vis kunngjeringar", "home.show_announcements": "Vis kunngjeringar",
"interaction_modal.description.favourite": "Med ein konto på Mastodon kan du favorittmerkja dette innlegget for å visa forfattaren at du set pris på det, og for å lagra det til seinare.", "interaction_modal.description.favourite": "Med ein konto på Mastodon kan du favorittmerka dette innlegget for å visa forfattaren at du set pris på det, og for å lagra det til seinare.",
"interaction_modal.description.follow": "Med ein konto på Mastodon kan du fylgja {name} for å sjå innlegga deira i din heimestraum.", "interaction_modal.description.follow": "Med ein konto på Mastodon kan du fylgja {name} for å sjå innlegga deira i din heimestraum.",
"interaction_modal.description.reblog": "Med ein konto på Mastodon kan du framheva dette innlegget for å dela det med dine eigne fylgjarar.", "interaction_modal.description.reblog": "Med ein konto på Mastodon kan du framheva dette innlegget for å dela det med dine eigne fylgjarar.",
"interaction_modal.description.reply": "Med ein konto på Mastodon kan du svara på dette innlegget.", "interaction_modal.description.reply": "Med ein konto på Mastodon kan du svara på dette innlegget.",
@ -673,7 +673,7 @@
"status.unmute_conversation": "Opphev målbinding av samtalen", "status.unmute_conversation": "Opphev målbinding av samtalen",
"status.unpin": "Løys frå profil", "status.unpin": "Løys frå profil",
"subscribed_languages.lead": "Kun innlegg på valde språk vil bli dukke opp i heimestraumen din og i listene dine etter denne endringa. For å motta innlegg på alle språk, la vere å velje nokon.", "subscribed_languages.lead": "Kun innlegg på valde språk vil bli dukke opp i heimestraumen din og i listene dine etter denne endringa. For å motta innlegg på alle språk, la vere å velje nokon.",
"subscribed_languages.save": "Gøym", "subscribed_languages.save": "Lagre endringar",
"subscribed_languages.target": "Endre abonnerte språk for {target}", "subscribed_languages.target": "Endre abonnerte språk for {target}",
"tabs_bar.home": "Heim", "tabs_bar.home": "Heim",
"tabs_bar.notifications": "Varsel", "tabs_bar.notifications": "Varsel",

View File

@ -303,7 +303,7 @@
"hashtag.unfollow": "Отпрати хеш ознаку", "hashtag.unfollow": "Отпрати хеш ознаку",
"hashtags.and_other": "…и {count, plural, one {још #} few {још #}other {још #}}", "hashtags.and_other": "…и {count, plural, one {још #} few {још #}other {још #}}",
"home.actions.go_to_explore": "Погледате шта је у тренду", "home.actions.go_to_explore": "Погледате шта је у тренду",
"home.actions.go_to_suggestions": "Пронађeте људе које бисте пратили", "home.actions.go_to_suggestions": "Пронађете људе које бисте пратили",
"home.column_settings.basic": "Основна", "home.column_settings.basic": "Основна",
"home.column_settings.show_reblogs": "Прикажи подржавања", "home.column_settings.show_reblogs": "Прикажи подржавања",
"home.column_settings.show_replies": "Прикажи одговоре", "home.column_settings.show_replies": "Прикажи одговоре",

View File

@ -114,7 +114,7 @@
"column.directory": "瀏覽個人檔案", "column.directory": "瀏覽個人檔案",
"column.domain_blocks": "已封鎖網域", "column.domain_blocks": "已封鎖網域",
"column.favourites": "最愛", "column.favourites": "最愛",
"column.firehose": "即時河道", "column.firehose": "即時內容",
"column.follow_requests": "跟隨請求", "column.follow_requests": "跟隨請求",
"column.home": "首頁", "column.home": "首頁",
"column.lists": "列表", "column.lists": "列表",
@ -629,7 +629,7 @@
"status.edit": "編輯", "status.edit": "編輯",
"status.edited": "編輯於 {date}", "status.edited": "編輯於 {date}",
"status.edited_x_times": "已編輯 {count, plural, one {{count} 次} other {{count} 次}}", "status.edited_x_times": "已編輯 {count, plural, one {{count} 次} other {{count} 次}}",
"status.embed": "內嵌", "status.embed": "內嵌嘟文",
"status.favourite": "最愛", "status.favourite": "最愛",
"status.filter": "過濾此嘟文", "status.filter": "過濾此嘟文",
"status.filtered": "已過濾", "status.filtered": "已過濾",

View File

@ -49,6 +49,7 @@ export function withOptionalRouter(Component) {
C.displayName = displayName; C.displayName = displayName;
C.WrappedComponent = Component; C.WrappedComponent = Component;
C.propTypes = { C.propTypes = {
...Component.propTypes,
wrappedComponentRef: PropTypes.oneOfType([ wrappedComponentRef: PropTypes.oneOfType([
PropTypes.string, PropTypes.string,
PropTypes.func, PropTypes.func,

View File

@ -19,7 +19,7 @@ class ActivityPub::LinkedDataSignature
return unless type == 'RsaSignature2017' return unless type == 'RsaSignature2017'
creator = ActivityPub::TagManager.instance.uri_to_actor(creator_uri) creator = ActivityPub::TagManager.instance.uri_to_actor(creator_uri)
creator ||= ActivityPub::FetchRemoteKeyService.new.call(creator_uri, id: false) creator = ActivityPub::FetchRemoteKeyService.new.call(creator_uri, id: false) if creator&.public_key.blank?
return if creator.nil? return if creator.nil?
@ -28,6 +28,8 @@ class ActivityPub::LinkedDataSignature
to_be_verified = options_hash + document_hash to_be_verified = options_hash + document_hash
creator if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified) creator if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified)
rescue OpenSSL::PKey::RSAError
false
end end
def sign!(creator, sign_with: nil) def sign!(creator, sign_with: nil)

View File

@ -55,7 +55,7 @@ class PreviewCard < ApplicationRecord
has_attached_file :image, processors: [:thumbnail, :blurhash_transcoder], styles: ->(f) { image_styles(f) }, convert_options: { all: '-quality 90 +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' }, validate_media_type: false has_attached_file :image, processors: [:thumbnail, :blurhash_transcoder], styles: ->(f) { image_styles(f) }, convert_options: { all: '-quality 90 +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' }, validate_media_type: false
validates :url, presence: true, uniqueness: true validates :url, presence: true, uniqueness: true, url: true
validates_attachment_content_type :image, content_type: IMAGE_MIME_TYPES validates_attachment_content_type :image, content_type: IMAGE_MIME_TYPES
validates_attachment_size :image, less_than: LIMIT validates_attachment_size :image, less_than: LIMIT
remotable_attachment :image, LIMIT remotable_attachment :image, LIMIT

View File

@ -4,7 +4,7 @@
.batch-table__row__content.pending-account .batch-table__row__content.pending-account
.pending-account__header .pending-account__header
= link_to preview_card.title, preview_card.url = link_to preview_card.title, url_for_preview_card(preview_card)
%br/ %br/

View File

@ -1,39 +0,0 @@
{
"ignored_warnings": [
{
"warning_type": "Cross-Site Scripting",
"warning_code": 4,
"fingerprint": "cd5cfd7f40037fbfa753e494d7129df16e358bfc43ef0da3febafbf4ee1ed3ac",
"check_name": "LinkToHref",
"message": "Potentially unsafe model attribute in `link_to` href",
"file": "app/views/admin/trends/links/_preview_card.html.haml",
"line": 7,
"link": "https://brakemanscanner.org/docs/warning_types/link_to_href",
"code": "link_to((Unresolved Model).new.title, (Unresolved Model).new.url)",
"render_path": [
{
"type": "template",
"name": "admin/trends/links/index",
"line": 49,
"file": "app/views/admin/trends/links/index.html.haml",
"rendered": {
"name": "admin/trends/links/_preview_card",
"file": "app/views/admin/trends/links/_preview_card.html.haml"
}
}
],
"location": {
"type": "template",
"template": "admin/trends/links/_preview_card"
},
"user_input": "(Unresolved Model).new.url",
"confidence": "Weak",
"cwe_id": [
79
],
"note": ""
}
],
"updated": "2023-07-12 11:20:51 -0400",
"brakeman_version": "6.0.0"
}

View File

@ -1,3 +1,5 @@
--- ---
:skip_checks: :skip_checks:
- CheckPermitAttributes - CheckPermitAttributes
:url_safe_methods:
- url_for_preview_card

View File

@ -36,7 +36,7 @@ zh-CN:
status: status:
attributes: attributes:
reblog: reblog:
taken: 被转嘟过 taken: 被转嘟过
user: user:
attributes: attributes:
email: email:

View File

@ -13,7 +13,7 @@ fy:
locked: Jo account is blokkearre. locked: Jo account is blokkearre.
not_found_in_database: "%{authentication_keys} of wachtwurd ûnjildich." not_found_in_database: "%{authentication_keys} of wachtwurd ûnjildich."
pending: Jo account moat noch hieltyd beoardiele wurde. pending: Jo account moat noch hieltyd beoardiele wurde.
timeout: Dyn sesje is ferrûn, meld dy opnij oan. timeout: Jo sesje is ferrûn. Meld jo opnij oan om troch te gean.
unauthenticated: Jo moatte oanmelde of registrearje. unauthenticated: Jo moatte oanmelde of registrearje.
unconfirmed: Jo moatte earst jo account befêstigje. unconfirmed: Jo moatte earst jo account befêstigje.
mailer: mailer:

View File

@ -9,7 +9,7 @@ zh-CN:
already_authenticated: 你已登录。 already_authenticated: 你已登录。
inactive: 你还没有激活账户。 inactive: 你还没有激活账户。
invalid: "%{authentication_keys} 无效或密码错误。" invalid: "%{authentication_keys} 无效或密码错误。"
last_attempt: 你只有最后一次尝试机会,若未通过,号将被锁定。 last_attempt: 你只有最后一次尝试机会,若未通过,号将被锁定。
locked: 你的账户已被锁定。 locked: 你的账户已被锁定。
not_found_in_database: "%{authentication_keys}或密码错误。" not_found_in_database: "%{authentication_keys}或密码错误。"
pending: 你的账号仍在审核中。 pending: 你的账号仍在审核中。
@ -85,7 +85,7 @@ zh-CN:
send_instructions: 如果你的电子邮件地址存在于我们的数据库中,你将在几分钟后收到一个密码恢复链接。如果你没有收到这封邮件,请检查你邮箱的垃圾箱。 send_instructions: 如果你的电子邮件地址存在于我们的数据库中,你将在几分钟后收到一个密码恢复链接。如果你没有收到这封邮件,请检查你邮箱的垃圾箱。
send_paranoid_instructions: 如果你的电子邮件地址存在于我们的数据库中,你将在几分钟后收到一个密码恢复链接。如果你没有收到这封邮件,请检查你邮箱的垃圾箱。 send_paranoid_instructions: 如果你的电子邮件地址存在于我们的数据库中,你将在几分钟后收到一个密码恢复链接。如果你没有收到这封邮件,请检查你邮箱的垃圾箱。
updated: 你的密码已成功修改,现在你已登录。 updated: 你的密码已成功修改,现在你已登录。
updated_not_active: 你的密码已成功修改 updated_not_active: 你的密码已修改成功。
registrations: registrations:
destroyed: 再见!你的账户已成功注销。我们希望很快可以再见到你。 destroyed: 再见!你的账户已成功注销。我们希望很快可以再见到你。
signed_up: 欢迎!你已成功注册。 signed_up: 欢迎!你已成功注册。

View File

@ -43,7 +43,7 @@ fy:
new: Nije tapassing new: Nije tapassing
scopes: Tastimmingen scopes: Tastimmingen
show: Toane show: Toane
title: Dyn tapassingen title: Jo tapassingen
new: new:
title: Nije tapassing title: Nije tapassing
show: show:
@ -60,7 +60,7 @@ fy:
error: error:
title: Der is in flater bard title: Der is in flater bard
new: new:
prompt_html: "%{client_name} hat tastimming nedich om tagong te krijen ta dyn account. It giet om in tapassing fan in tredde partij.<strong>Asto dit net fertroust, moatsto gjin tastimming jaan.</strong>" prompt_html: "%{client_name} hat tastimming nedich om tagong te krijen ta jo account. It giet om in tapassing fan in tredde partij.<strong>As jo dit net fertrouwe, moatte jo gjin tastimming jaan.</strong>"
review_permissions: Tastimmingen beoardiele review_permissions: Tastimmingen beoardiele
title: Autorisaasje fereaske title: Autorisaasje fereaske
show: show:
@ -69,15 +69,15 @@ fy:
buttons: buttons:
revoke: Ynlûke revoke: Ynlûke
confirmations: confirmations:
revoke: Bisto wis? revoke: Binne jo wis?
index: index:
authorized_at: Autorisearre op %{date} authorized_at: Autorisearre op %{date}
description_html: Dit binne tapassingen dyt tagong hawwe ta dyn account fia de API. As der tapassingen tusken steane dytsto net werkenst of in tapassing harren misdraacht, kinsto de tagongsrjochten fan de tapassing ynlûke. description_html: Dit binne tapassingen dyt fia de API tagong hawwe ta jo account. As der tapassingen tusken steane dyt jo net werkenne of in tapassing harren misdraacht, kinne jo de tagongsrjochten fan de tapassing ynlûke.
last_used_at: Lêst brûkt op %{date} last_used_at: Lêst brûkt op %{date}
never_used: Nea brûkt never_used: Nea brûkt
scopes: Tastimmingen scopes: Tastimmingen
superapp: Yntern superapp: Yntern
title: Dyn autorisearre tapassingen title: Jo autorisearre tapassingen
errors: errors:
messages: messages:
access_denied: De boarne-eigener of autorisaasjeserver hat it fersyk wegere. access_denied: De boarne-eigener of autorisaasjeserver hat it fersyk wegere.
@ -165,22 +165,22 @@ fy:
admin:write:reports: moderaasjemaatregelen nimme yn rapportaazjes admin:write:reports: moderaasjemaatregelen nimme yn rapportaazjes
crypto: ein-ta-ein-fersifering brûke crypto: ein-ta-ein-fersifering brûke
follow: relaasjes tusken accounts bewurkje follow: relaasjes tusken accounts bewurkje
push: dyn pushmeldingen ûntfange push: jo pushmeldingen ûntfange
read: alle gegevens fan dyn account lêze read: alle gegevens fan jo account lêze
read:accounts: accountynformaasje besjen read:accounts: accountynformaasje besjen
read:blocks: dyn blokkearre brûkers besjen read:blocks: jo blokkearre brûkers besjen
read:bookmarks: dyn blêdwizers besjen read:bookmarks: jo blêdwizers besjen
read:favourites: jo favoriten besjen read:favourites: jo favoriten besjen
read:filters: dyn filters besjen read:filters: jo filters besjen
read:follows: de accounts dytsto folgest besjen read:follows: de accounts dytsto folgest besjen
read:lists: dyn listen besjen read:lists: jo listen besjen
read:mutes: dyn negearre brûkers besjen read:mutes: jo negearre brûkers besjen
read:notifications: dyn meldingen besjen read:notifications: jo meldingen besjen
read:reports: dyn rapportearre berjochten besjen read:reports: jo rapportearre berjochten besjen
read:search: út dyn namme sykje read:search: út jo namme sykje
read:statuses: alle berjochten besjen read:statuses: alle berjochten besjen
write: alle gegevens fan dyn account bewurkje write: alle gegevens fan jo account bewurkje
write:accounts: dyn profyl bewurkje write:accounts: jo profyl bewurkje
write:blocks: accounts en domeinen blokkearje write:blocks: accounts en domeinen blokkearje
write:bookmarks: berjochten oan blêdwizers tafoegje write:bookmarks: berjochten oan blêdwizers tafoegje
write:conversations: petearen negearre en fuortsmite write:conversations: petearen negearre en fuortsmite

View File

@ -570,7 +570,7 @@ nn:
enabled: Skrudd på enabled: Skrudd på
inbox_url: Overførings-URL inbox_url: Overførings-URL
pending: Avventer overgangens godkjenning pending: Avventer overgangens godkjenning
save_and_enable: Lagr og slå på save_and_enable: Lagre og slå på
setup: Sett opp en overgangsforbindelse setup: Sett opp en overgangsforbindelse
signatures_not_enabled: Overgangar fungerer ikkje så lenge sikker- eller kvitlistingsmodus er aktivert signatures_not_enabled: Overgangar fungerer ikkje så lenge sikker- eller kvitlistingsmodus er aktivert
status: Status status: Status
@ -1280,7 +1280,7 @@ nn:
deselect: Vel ingen deselect: Vel ingen
none: Ingen none: Ingen
order_by: Sorter etter order_by: Sorter etter
save_changes: Lagr endringar save_changes: Lagre endringar
select_all_matching_items: select_all_matching_items:
one: Vel %{count} element som passar til søket ditt. one: Vel %{count} element som passar til søket ditt.
other: Vel %{count} element som passar til søket ditt. other: Vel %{count} element som passar til søket ditt.

View File

@ -86,7 +86,7 @@ fy:
media_cache_retention_period: Mediabestannen dyt fan oare servers download binne wurde nei it opjûne oantal dagen fuortsmiten en wurde op fersyk opnij download. media_cache_retention_period: Mediabestannen dyt fan oare servers download binne wurde nei it opjûne oantal dagen fuortsmiten en wurde op fersyk opnij download.
peers_api_enabled: In list mei domeinnammen, dêrt dizze server yn fediverse kontakt hân mei hat. Hjir wurdt gjin data dield, oft jo mei in bepaalde server federearrest, mar alinnich, dat jo server dat wit. Dit wurdt foar tsjinsten brûkt, dyt statistiken oer federaasje yn algemiene sin sammelet. peers_api_enabled: In list mei domeinnammen, dêrt dizze server yn fediverse kontakt hân mei hat. Hjir wurdt gjin data dield, oft jo mei in bepaalde server federearrest, mar alinnich, dat jo server dat wit. Dit wurdt foar tsjinsten brûkt, dyt statistiken oer federaasje yn algemiene sin sammelet.
profile_directory: De brûkersgids befettet in list fan alle brûkers dy¥t derfoar keazen hawwe om ûntdekt wurde te kinnen. profile_directory: De brûkersgids befettet in list fan alle brûkers dy¥t derfoar keazen hawwe om ûntdekt wurde te kinnen.
require_invite_text: Meitsje it ynfoljen fan "Wêrom wolle jo jo hjir registrearje?" ferplicht yn stee fan opsjoneel, wanneart registraasjes hânmjittich goedkard wurde moatte require_invite_text: Meitsje it ynfoljen fan Wêrom wolle jo jo hjir registrearje? ferplicht yn stee fan opsjoneel, wanneart registraasjes hânmjittich goedkard wurde moatte
site_contact_email: Hoe minsken jo berikke kinne foar juridyske fragen of stipe. site_contact_email: Hoe minsken jo berikke kinne foar juridyske fragen of stipe.
site_contact_username: Hoe minsken jo op Mastodon berikke kinne. site_contact_username: Hoe minsken jo op Mastodon berikke kinne.
site_extended_description: Alle oanfoljende ynformaasje dyt nuttich wêze kin foar besikers en jo brûkers. Kin opmakke wurde mei Markdown. site_extended_description: Alle oanfoljende ynformaasje dyt nuttich wêze kin foar besikers en jo brûkers. Kin opmakke wurde mei Markdown.

View File

@ -40,7 +40,7 @@ zh-CN:
current_email: 当前的电子邮箱 current_email: 当前的电子邮箱
label: 更改电子邮箱 label: 更改电子邮箱
new_email: 新的电子邮箱 new_email: 新的电子邮箱
submit: 更改电子邮件地址 submit: 更改电子邮
title: 更改 %{username} 的电子邮箱 title: 更改 %{username} 的电子邮箱
change_role: change_role:
changed_msg: 已成功更改角色! changed_msg: 已成功更改角色!
@ -68,8 +68,8 @@ zh-CN:
enable_sign_in_token_auth: 启用电子邮件令牌认证 enable_sign_in_token_auth: 启用电子邮件令牌认证
enabled: 已启用 enabled: 已启用
enabled_msg: 成功解冻 %{username} 的账号 enabled_msg: 成功解冻 %{username} 的账号
followers: 关注者 followers: 粉丝
follows: 正在关注 follows: 关注
header: 个人资料页横幅图片 header: 个人资料页横幅图片
inbox_url: 收件箱InboxURL inbox_url: 收件箱InboxURL
invite_request_text: 加入理由 invite_request_text: 加入理由

View File

@ -34,6 +34,40 @@ RSpec.describe ActivityPub::LinkedDataSignature do
end end
end end
context 'when local account record is missing a public key' do
let(:raw_signature) do
{
'creator' => 'http://example.com/alice',
'created' => '2017-09-23T20:21:34Z',
}
end
let(:signature) { raw_signature.merge('type' => 'RsaSignature2017', 'signatureValue' => sign(sender, raw_signature, raw_json)) }
let(:service_stub) { instance_double(ActivityPub::FetchRemoteKeyService) }
before do
# Ensure signature is computed with the old key
signature
# Unset key
old_key = sender.public_key
sender.update!(private_key: '', public_key: '')
allow(ActivityPub::FetchRemoteKeyService).to receive(:new).and_return(service_stub)
allow(service_stub).to receive(:call).with('http://example.com/alice', id: false) do
sender.update!(public_key: old_key)
sender
end
end
it 'fetches key and returns creator' do
expect(subject.verify_actor!).to eq sender
expect(service_stub).to have_received(:call).with('http://example.com/alice', id: false).once
end
end
context 'when signature is missing' do context 'when signature is missing' do
let(:signature) { nil } let(:signature) { nil }

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'rails_helper'
describe PreviewCard do
describe 'validations' do
describe 'urls' do
it 'allows http schemes' do
record = described_class.new(url: 'http://example.host/path')
expect(record).to be_valid
end
it 'allows https schemes' do
record = described_class.new(url: 'https://example.host/path')
expect(record).to be_valid
end
it 'does not allow javascript: schemes' do
record = described_class.new(url: 'javascript:alert()')
expect(record).to_not be_valid
expect(record).to model_have_error_on_field(:url)
end
end
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'admin/trends/links/_preview_card.html.haml' do
it 'correctly escapes user supplied url values' do
form = instance_double(ActionView::Helpers::FormHelper, check_box: nil)
trend = PreviewCardTrend.new(allowed: false)
preview_card = Fabricate.build(
:preview_card,
url: 'https://host.example/path?query=<script>',
trend: trend,
title: 'Fun'
)
render partial: 'admin/trends/links/preview_card', locals: { preview_card: preview_card, f: form }
expect(rendered).to include('<a href="https://host.example/path?query=&lt;script&gt;">Fun</a>')
end
end