mirror of https://github.com/mastodon/mastodon
Merge branch 'master' into patch-1
This commit is contained in:
commit
03fe20acf0
|
@ -17,7 +17,7 @@ Click on the screenshot to watch a demo of the UI:
|
||||||
|
|
||||||
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
|
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
|
||||||
|
|
||||||
Focus of the project on a clean REST API and a good user interface. Ruby on Rails is used for the back-end, while React.js and Redux are used for the dynamic front-end. A static front-end for public resources (profiles and statuses) is also provided.
|
The project focus is a clean REST API and a good user interface. Ruby on Rails is used for the back-end, while React.js and Redux are used for the dynamic front-end. A static front-end for public resources (profiles and statuses) is also provided.
|
||||||
|
|
||||||
If you would like, you can [support the development of this project on Patreon][patreon]. Alternatively, you can donate to this BTC address: `17j2g7vpgHhLuXhN4bueZFCvdxxieyRVWd`
|
If you would like, you can [support the development of this project on Patreon][patreon]. Alternatively, you can donate to this BTC address: `17j2g7vpgHhLuXhN4bueZFCvdxxieyRVWd`
|
||||||
|
|
||||||
|
|
4
app.json
4
app.json
|
@ -26,6 +26,10 @@
|
||||||
"description": "The secret key base",
|
"description": "The secret key base",
|
||||||
"generator": "secret"
|
"generator": "secret"
|
||||||
},
|
},
|
||||||
|
"OTP_SECRET": {
|
||||||
|
"description": "One-time password secret",
|
||||||
|
"generator": "secret"
|
||||||
|
},
|
||||||
"SINGLE_USER_MODE": {
|
"SINGLE_USER_MODE": {
|
||||||
"description": "Should the instance run in single user mode? (Disable registrations, redirect to front page)",
|
"description": "Should the instance run in single user mode? (Disable registrations, redirect to front page)",
|
||||||
"value": "false",
|
"value": "false",
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 1000 1000">
|
||||||
|
<path d="M527.194 543.7a28.362 28.362 0 0 0-56.723 0 25.73 25.73 0 0 0 2.67 11.674 26.42 26.42 0 0 0 5.672 8.34 28.2 28.2 0 0 0 40.04 0 31.87 31.87 0 0 0 6.006-8.34 28.8 28.8 0 0 0 2.336-11.674m-48.382-113.413a28.308 28.308 0 1 0 40.04 40.027 37.2 37.2 0 0 0 4.67-5.67 28.092 28.092 0 0 0 3.67-14.343 27.29 27.29 0 0 0-8.34-20.012 28.24 28.24 0 0 0-5.006-4 26.958 26.958 0 0 0-15.015-4.336 27.31 27.31 0 0 0-20.02 8.34m20.02-101.735a28.476 28.476 0 1 0 20.02 8.34 27.31 27.31 0 0 0-20.02-8.34M231.9 573.717a28.18 28.18 0 1 0 8.342 20.012 27.308 27.308 0 0 0-8.342-20.014m-40.04-93.4a28.352 28.352 0 0 0 20.02 48.366 26.958 26.958 0 0 0 15.015-4.336 28.255 28.255 0 0 0 5.005-4 27.29 27.29 0 0 0 8.342-20.013 28.09 28.09 0 0 0-3.67-14.343 37.21 37.21 0 0 0-4.67-5.67 28.2 28.2 0 0 0-40.04 0m40.04-93.4a28.2 28.2 0 0 0-40.04 0 26.425 26.425 0 0 0-5.673 8.34 25.73 25.73 0 0 0-2.67 11.673 28.315 28.315 0 0 0 48.38 20.018 27.29 27.29 0 0 0 8.342-20.012 28.8 28.8 0 0 0-2.336-11.674 31.87 31.87 0 0 0-6.006-8.34m550.55 178.453a28.476 28.476 0 1 0 20.02 8.34 27.31 27.31 0 0 0-20.02-8.34m20.02-85.057a28.2 28.2 0 0 0-40.04 0 37.2 37.2 0 0 0-4.672 5.67 28.092 28.092 0 0 0-3.67 14.343 27.29 27.29 0 0 0 8.342 20.013 28.248 28.248 0 0 0 5.005 4 26.96 26.96 0 0 0 15.015 4.336 28.3 28.3 0 0 0 20.02-48.366m-46.046-85.057a28.8 28.8 0 0 0-2.336 11.673 28.362 28.362 0 0 0 56.723 0 25.73 25.73 0 0 0-2.668-11.674 26.427 26.427 0 0 0-5.672-8.34 28.2 28.2 0 0 0-40.04 0 31.86 31.86 0 0 0-6.007 8.343z" fill="#2b90d9"/>
|
||||||
|
<path d="M853.52 146.764Q707.04 0 499.833 0 292.96 0 146.48 146.764 0 293.2 0 500q0 207.138 146.48 353.57T499.833 1000q207.207 0 353.687-146.43T1000 500q0-206.8-146.48-353.236zM213.547 708.806h-3.337q-43.043 0-73.407-30.02-30.03-30.354-30.03-73.382V395.93v-.666q1.335-41.027 30.03-69.713 30.364-30.35 73.407-30.35t73.073 30.354q29.363 29.02 30.364 70.38V615.41q2.336 55.037 46.713 93.4zM600.6 554.7q-1 41.36-30.364 70.38-30.03 30.353-73.073 30.354t-73.407-30.354q-28.7-28.686-30.03-69.713V345.23q0-43.03 30.03-73.382 30.364-30.02 73.407-30.02h150.15q-44.378 38.36-46.713 93.4zm286.954 50.7q0 43.03-30.03 73.382-30.364 30.02-73.407 30.02h-150.15q44.378-38.36 46.713-93.4v-219.47q1-41.362 30.364-70.38 30.03-30.355 73.073-30.355t73.407 30.354q28.7 28.687 30.03 69.714V605.4z" fill="#2b90d9"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -15,6 +15,7 @@ const ColumnCollapsable = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
icon: React.PropTypes.string.isRequired,
|
icon: React.PropTypes.string.isRequired,
|
||||||
|
title: React.PropTypes.string,
|
||||||
fullHeight: React.PropTypes.number.isRequired,
|
fullHeight: React.PropTypes.number.isRequired,
|
||||||
children: React.PropTypes.node,
|
children: React.PropTypes.node,
|
||||||
onCollapse: React.PropTypes.func
|
onCollapse: React.PropTypes.func
|
||||||
|
@ -39,13 +40,13 @@ const ColumnCollapsable = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { icon, fullHeight, children } = this.props;
|
const { icon, title, fullHeight, children } = this.props;
|
||||||
const { collapsed } = this.state;
|
const { collapsed } = this.state;
|
||||||
const collapsedClassName = collapsed ? 'collapsable-collapsed' : 'collapsable';
|
const collapsedClassName = collapsed ? 'collapsable-collapsed' : 'collapsable';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
<div style={{...iconStyle }} className={`column-icon ${collapsedClassName}`} onClick={this.handleToggleCollapsed}><i className={`fa fa-${icon}`} /></div>
|
<div title={`${title}`} style={{...iconStyle }} className={`column-icon ${collapsedClassName}`} onClick={this.handleToggleCollapsed}><i className={`fa fa-${icon}`} /></div>
|
||||||
|
|
||||||
<Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(collapsed ? 0 : 100), height: spring(collapsed ? 0 : fullHeight, collapsed ? undefined : { stiffness: 150, damping: 9 }) }}>
|
<Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(collapsed ? 0 : 100), height: spring(collapsed ? 0 : fullHeight, collapsed ? undefined : { stiffness: 150, damping: 9 }) }}>
|
||||||
{({ opacity, height }) =>
|
{({ opacity, height }) =>
|
||||||
|
|
|
@ -6,7 +6,8 @@ import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
import SettingText from './setting_text';
|
import SettingText from './setting_text';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' }
|
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
|
||||||
|
settings: { id: 'home.settings', defaultMessage: 'Column settings' }
|
||||||
});
|
});
|
||||||
|
|
||||||
const outerStyle = {
|
const outerStyle = {
|
||||||
|
@ -39,7 +40,7 @@ const ColumnSettings = React.createClass({
|
||||||
const { settings, onChange, onSave, intl } = this.props;
|
const { settings, onChange, onSave, intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColumnCollapsable icon='sliders' fullHeight={209} onCollapse={onSave}>
|
<ColumnCollapsable icon='sliders' title={intl.formatMessage(messages.settings)} fullHeight={209} onCollapse={onSave}>
|
||||||
<div className='column-settings--outer' style={outerStyle}>
|
<div className='column-settings--outer' style={outerStyle}>
|
||||||
<span className='column-settings--section' style={sectionStyle}><FormattedMessage id='home.column_settings.basic' defaultMessage='Basic' /></span>
|
<span className='column-settings--section' style={sectionStyle}><FormattedMessage id='home.column_settings.basic' defaultMessage='Basic' /></span>
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
clear: { id: 'notifications.clear', defaultMessage: 'Clear notifications' }
|
||||||
|
});
|
||||||
|
|
||||||
const iconStyle = {
|
const iconStyle = {
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
padding: '15px',
|
padding: '15px',
|
||||||
|
@ -8,14 +14,22 @@ const iconStyle = {
|
||||||
zIndex: '2'
|
zIndex: '2'
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClearColumnButton = ({ onClick }) => (
|
const ClearColumnButton = React.createClass({
|
||||||
<div className='column-icon' tabindex='0' style={iconStyle} onClick={onClick}>
|
|
||||||
<i className='fa fa-trash' />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
ClearColumnButton.propTypes = {
|
propTypes: {
|
||||||
onClick: React.PropTypes.func.isRequired
|
onClick: React.PropTypes.func.isRequired,
|
||||||
};
|
intl: React.PropTypes.object.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
export default ClearColumnButton;
|
render () {
|
||||||
|
const { intl } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div title={intl.formatMessage(messages.clear)} className='column-icon' tabIndex='0' style={iconStyle} onClick={this.onClick}>
|
||||||
|
<i className='fa fa-eraser' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default injectIntl(ClearColumnButton);
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ColumnCollapsable from '../../../components/column_collapsable';
|
import ColumnCollapsable from '../../../components/column_collapsable';
|
||||||
import SettingToggle from './setting_toggle';
|
import SettingToggle from './setting_toggle';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
settings: { id: 'notifications.settings', defaultMessage: 'Column settings' }
|
||||||
|
});
|
||||||
|
|
||||||
const outerStyle = {
|
const outerStyle = {
|
||||||
padding: '15px'
|
padding: '15px'
|
||||||
};
|
};
|
||||||
|
@ -30,14 +34,14 @@ const ColumnSettings = React.createClass({
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { settings, onChange, onSave } = this.props;
|
const { settings, intl, onChange, onSave } = this.props;
|
||||||
|
|
||||||
const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
|
const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
|
||||||
const showStr = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
|
const showStr = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
|
||||||
const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
|
const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColumnCollapsable icon='sliders' fullHeight={616} onCollapse={onSave}>
|
<ColumnCollapsable icon='sliders' title={intl.formatMessage(messages.settings)} fullHeight={616} onCollapse={onSave}>
|
||||||
<div className='column-settings--outer' style={outerStyle}>
|
<div className='column-settings--outer' style={outerStyle}>
|
||||||
<span className='column-settings--section' style={sectionStyle}><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
|
<span className='column-settings--section' style={sectionStyle}><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
|
||||||
|
|
||||||
|
@ -77,4 +81,4 @@ const ColumnSettings = React.createClass({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ColumnSettings;
|
export default injectIntl(ColumnSettings);
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
const en = {
|
const en = {
|
||||||
"column_back_button.label": "Zurück",
|
"column_back_button.label": "Zurück",
|
||||||
"lightbox.close": "Schließen",
|
"lightbox.close": "Schließen",
|
||||||
"loading_indicator.label": "Lade...",
|
"loading_indicator.label": "Lade…",
|
||||||
"status.mention": "Erwähnen",
|
"status.mention": "Erwähnen",
|
||||||
"status.delete": "Löschen",
|
"status.delete": "Löschen",
|
||||||
"status.reply": "Antworten",
|
"status.reply": "Antworten",
|
||||||
"status.reblog": "Teilen",
|
"status.reblog": "Teilen",
|
||||||
"status.favourite": "Favorisieren",
|
"status.favourite": "Favorisieren",
|
||||||
"status.reblogged_by": "{name} teilte",
|
"status.reblogged_by": "{name} teilte",
|
||||||
"status.sensitive_warning": "Sensible Inhalte",
|
"status.sensitive_warning": "Heikle Inhalte",
|
||||||
"status.sensitive_toggle": "Klicken um zu zeigen",
|
"status.sensitive_toggle": "Klicke, um sie zu sehen",
|
||||||
"status.open": "Öffnen",
|
"status.open": "Öffnen",
|
||||||
"video_player.toggle_sound": "Ton umschalten",
|
"video_player.toggle_sound": "Ton umschalten",
|
||||||
"account.mention": "Erwähnen",
|
"account.mention": "Erwähnen",
|
||||||
|
@ -20,17 +20,17 @@ const en = {
|
||||||
"account.follow": "Folgen",
|
"account.follow": "Folgen",
|
||||||
"account.posts": "Beiträge",
|
"account.posts": "Beiträge",
|
||||||
"account.follows": "Folgt",
|
"account.follows": "Folgt",
|
||||||
"account.followers": "Folger",
|
"account.followers": "Folgende",
|
||||||
"account.follows_you": "Folgt dir",
|
"account.follows_you": "Folgt dir",
|
||||||
"account.requested": "Warte auf Erlaubnis",
|
"account.requested": "Warte auf Erlaubnis",
|
||||||
"getting_started.heading": "Erste Schritte",
|
"getting_started.heading": "Erste Schritte",
|
||||||
"getting_started.about_addressing": "Du kannst Leuten folgen, falls du ihren Nutzernamen und ihre Domain kennst, in dem du eine e-mail-artige Addresse in das Suchfeld oben an der Seite eingibst.",
|
"getting_started.about_addressing": "Du kannst Leuten folgen, falls du ihren Nutzernamen und ihre Domain kennst, in dem du eine e-mail-artige Addresse in das Suchfeld oben auf der Seite eingibst.",
|
||||||
"getting_started.about_shortcuts": "Falls der Zielnutzer an derselben Domain ist wie du, funktioniert der Nutzername auch alleine. Das gilt auch für Erwähnungen in Beiträgen.",
|
"getting_started.about_shortcuts": "Falls die Person auf derselben Domain ist wie du, reicht auch ihr Nutzername alleine. Das gilt auch für Erwähnungen in Beiträgen.",
|
||||||
"getting_started.about_developer": "Der Entwickler des Projekts kann unter Gargron@mastodon.social gefunden werden",
|
"getting_started.about_developer": "Der Entwickler des Projekts kann unter Gargron@mastodon.social gefunden werden",
|
||||||
"getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf {github} dazu beitragen oder Probleme melden.",
|
"getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf {github} dazu beitragen oder Probleme melden.",
|
||||||
"column.home": "Home",
|
"column.home": "Home",
|
||||||
"column.mentions": "Erwähnungen",
|
"column.mentions": "Erwähnungen",
|
||||||
"column.public": "Gesamtes Bekanntes Netz",
|
"column.public": "Gesamtes bekanntes Netz",
|
||||||
"column.notifications": "Mitteilungen",
|
"column.notifications": "Mitteilungen",
|
||||||
"column.follow_requests": "Folgeanfragen",
|
"column.follow_requests": "Folgeanfragen",
|
||||||
"tabs_bar.compose": "Schreiben",
|
"tabs_bar.compose": "Schreiben",
|
||||||
|
@ -38,11 +38,11 @@ const en = {
|
||||||
"tabs_bar.mentions": "Erwähnungen",
|
"tabs_bar.mentions": "Erwähnungen",
|
||||||
"tabs_bar.public": "Gesamtes Netz",
|
"tabs_bar.public": "Gesamtes Netz",
|
||||||
"tabs_bar.notifications": "Mitteilungen",
|
"tabs_bar.notifications": "Mitteilungen",
|
||||||
"compose_form.placeholder": "Worüber möchstest du schreiben?",
|
"compose_form.placeholder": "Worüber möchtest du schreiben?",
|
||||||
"compose_form.publish": "Tröt",
|
"compose_form.publish": "Tröt",
|
||||||
"compose_form.sensitive": "Medien als sensitiv markieren",
|
"compose_form.sensitive": "Medien als heikel markieren",
|
||||||
"compose_form.unlisted": "Öffentlich nicht auflisten",
|
|
||||||
"compose_form.private": "Als privat markieren",
|
"compose_form.private": "Als privat markieren",
|
||||||
|
"compose_form.unlisted": "Nicht öffentlich auflisten",
|
||||||
"navigation_bar.edit_profile": "Profil bearbeiten",
|
"navigation_bar.edit_profile": "Profil bearbeiten",
|
||||||
"navigation_bar.preferences": "Einstellungen",
|
"navigation_bar.preferences": "Einstellungen",
|
||||||
"navigation_bar.public_timeline": "Öffentlich",
|
"navigation_bar.public_timeline": "Öffentlich",
|
||||||
|
@ -52,15 +52,15 @@ const en = {
|
||||||
"search.placeholder": "Suche",
|
"search.placeholder": "Suche",
|
||||||
"search.account": "Konto",
|
"search.account": "Konto",
|
||||||
"search.hashtag": "Hashtag",
|
"search.hashtag": "Hashtag",
|
||||||
"upload_button.label": "Media-Datei anfügen",
|
"upload_button.label": "Mediendatei hinzufügen",
|
||||||
"upload_form.undo": "Entfernen",
|
"upload_form.undo": "Entfernen",
|
||||||
"notification.follow": "{name} folgt dir",
|
"notification.follow": "{name} folgt dir",
|
||||||
"notification.favourite": "{name} favorisierte deinen Status",
|
"notification.favourite": "{name} favorisierte deinen Status",
|
||||||
"notification.reblog": "{name} teilte deinen Status",
|
"notification.reblog": "{name} teilte deinen Status",
|
||||||
"notification.mention": "{name} erwähnte dich",
|
"notification.mention": "{name} erwähnte dich",
|
||||||
"notifications.column_settings.alert": "Desktop-Benachrichtigunen",
|
"notifications.column_settings.alert": "Desktop-Benachrichtigungen",
|
||||||
"notifications.column_settings.show": "In der Spalte anzeigen",
|
"notifications.column_settings.show": "In der Spalte anzeigen",
|
||||||
"notifications.column_settings.follow": "Neue Folger:",
|
"notifications.column_settings.follow": "Neue Folgende:",
|
||||||
"notifications.column_settings.favourite": "Favorisierungen:",
|
"notifications.column_settings.favourite": "Favorisierungen:",
|
||||||
"notifications.column_settings.mention": "Erwähnungen:",
|
"notifications.column_settings.mention": "Erwähnungen:",
|
||||||
"notifications.column_settings.reblog": "Geteilte Beiträge:",
|
"notifications.column_settings.reblog": "Geteilte Beiträge:",
|
||||||
|
|
|
@ -10,6 +10,10 @@ const en = {
|
||||||
"status.reblogged_by": "{name} boosted",
|
"status.reblogged_by": "{name} boosted",
|
||||||
"status.sensitive_warning": "Sensitive content",
|
"status.sensitive_warning": "Sensitive content",
|
||||||
"status.sensitive_toggle": "Click to view",
|
"status.sensitive_toggle": "Click to view",
|
||||||
|
"status.show_more": "Show more",
|
||||||
|
"status.show_less": "Show less",
|
||||||
|
"status.open": "Expand this status",
|
||||||
|
"status.report": "Report @{name}",
|
||||||
"video_player.toggle_sound": "Toggle sound",
|
"video_player.toggle_sound": "Toggle sound",
|
||||||
"account.mention": "Mention @{name}",
|
"account.mention": "Mention @{name}",
|
||||||
"account.edit_profile": "Edit profile",
|
"account.edit_profile": "Edit profile",
|
||||||
|
|
|
@ -10,6 +10,10 @@ const fr = {
|
||||||
"status.reblogged_by": "{name} a partagé :",
|
"status.reblogged_by": "{name} a partagé :",
|
||||||
"status.sensitive_warning": "Contenu délicat",
|
"status.sensitive_warning": "Contenu délicat",
|
||||||
"status.sensitive_toggle": "Cliquer pour dévoiler",
|
"status.sensitive_toggle": "Cliquer pour dévoiler",
|
||||||
|
"status.show_more": "Déplier",
|
||||||
|
"status.show_less": "Replier",
|
||||||
|
"status.open": "Déplier ce status",
|
||||||
|
"status.report": "Signaler @{name}",
|
||||||
"video_player.toggle_sound": "Mettre/Couper le son",
|
"video_player.toggle_sound": "Mettre/Couper le son",
|
||||||
"account.mention": "Mentionner",
|
"account.mention": "Mentionner",
|
||||||
"account.edit_profile": "Modifier le profil",
|
"account.edit_profile": "Modifier le profil",
|
||||||
|
@ -35,7 +39,6 @@ const fr = {
|
||||||
"column.community": "Fil public local",
|
"column.community": "Fil public local",
|
||||||
"column.public": "Fil public global",
|
"column.public": "Fil public global",
|
||||||
"column.notifications": "Notifications",
|
"column.notifications": "Notifications",
|
||||||
"column.public": "Fil public",
|
|
||||||
"column.blocks": "Utilisateurs bloqués",
|
"column.blocks": "Utilisateurs bloqués",
|
||||||
"column.favourites": "Favoris",
|
"column.favourites": "Favoris",
|
||||||
"tabs_bar.compose": "Composer",
|
"tabs_bar.compose": "Composer",
|
||||||
|
@ -44,9 +47,9 @@ const fr = {
|
||||||
"tabs_bar.public": "Fil public global",
|
"tabs_bar.public": "Fil public global",
|
||||||
"tabs_bar.notifications": "Notifications",
|
"tabs_bar.notifications": "Notifications",
|
||||||
"compose_form.placeholder": "Qu’avez-vous en tête ?",
|
"compose_form.placeholder": "Qu’avez-vous en tête ?",
|
||||||
"compose_form.publish": "Pouet ",
|
"compose_form.publish": "Pouet",
|
||||||
"compose_form.sensitive": "Marquer le média comme délicat",
|
"compose_form.sensitive": "Marquer le média comme délicat",
|
||||||
"compose_form.spoiler": "Masquer le texte par un avertissement",
|
"compose_form.spoiler": "Masquer le texte derrière un avertissement",
|
||||||
"compose_form.private": "Rendre privé",
|
"compose_form.private": "Rendre privé",
|
||||||
"compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodons. Si {domains} {domainsCount, plural, one {n'est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n'y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d'une autre manière à d'autres personnes imprévues",
|
"compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodons. Si {domains} {domainsCount, plural, one {n'est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n'y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d'une autre manière à d'autres personnes imprévues",
|
||||||
"compose_form.unlisted": "Ne pas afficher dans les fils publics",
|
"compose_form.unlisted": "Ne pas afficher dans les fils publics",
|
||||||
|
@ -58,7 +61,6 @@ const fr = {
|
||||||
"navigation_bar.blocks": "Utilisateurs bloqués",
|
"navigation_bar.blocks": "Utilisateurs bloqués",
|
||||||
"navigation_bar.favourites": "Favoris",
|
"navigation_bar.favourites": "Favoris",
|
||||||
"navigation_bar.info": "Plus d'informations",
|
"navigation_bar.info": "Plus d'informations",
|
||||||
"notification.favourite": "{name} a ajouté à ses favoris :",
|
|
||||||
"navigation_bar.logout": "Déconnexion",
|
"navigation_bar.logout": "Déconnexion",
|
||||||
"reply_indicator.cancel": "Annuler",
|
"reply_indicator.cancel": "Annuler",
|
||||||
"search.placeholder": "Chercher",
|
"search.placeholder": "Chercher",
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
include Localized
|
|
||||||
|
|
||||||
# Prevent CSRF attacks by raising an exception.
|
# Prevent CSRF attacks by raising an exception.
|
||||||
# For APIs, you may want to use :null_session instead.
|
# For APIs, you may want to use :null_session instead.
|
||||||
protect_from_forgery with: :exception
|
protect_from_forgery with: :exception
|
||||||
|
|
||||||
force_ssl if: "Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true'"
|
force_ssl if: "Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true'"
|
||||||
|
|
||||||
|
include Localized
|
||||||
helper_method :current_account
|
helper_method :current_account
|
||||||
|
|
||||||
rescue_from ActionController::RoutingError, with: :not_found
|
rescue_from ActionController::RoutingError, with: :not_found
|
||||||
|
@ -41,7 +40,6 @@ class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
# If the sign in is after a two week break, we need to regenerate their feed
|
# If the sign in is after a two week break, we need to regenerate their feed
|
||||||
RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
|
RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_suspension
|
def check_suspension
|
||||||
|
|
|
@ -4,13 +4,25 @@ module Localized
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
before_action :set_locale
|
around_action :set_locale
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def set_locale
|
def set_locale
|
||||||
I18n.locale = current_user.try(:locale) || default_locale
|
locale = default_locale
|
||||||
rescue I18n::InvalidLocale
|
|
||||||
I18n.locale = default_locale
|
if user_signed_in?
|
||||||
|
begin
|
||||||
|
locale = current_user.try(:locale) || default_locale
|
||||||
|
rescue I18n::InvalidLocale
|
||||||
|
locale = default_locale
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
I18n.with_locale(locale) do
|
||||||
|
yield
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_locale
|
def default_locale
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
||||||
include Localized
|
|
||||||
|
|
||||||
skip_before_action :authenticate_resource_owner!
|
skip_before_action :authenticate_resource_owner!
|
||||||
|
|
||||||
before_action :store_current_location
|
before_action :store_current_location
|
||||||
before_action :authenticate_resource_owner!
|
before_action :authenticate_resource_owner!
|
||||||
|
|
||||||
|
include Localized
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def store_current_location
|
def store_current_location
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
|
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
|
||||||
include Localized
|
|
||||||
|
|
||||||
skip_before_action :authenticate_resource_owner!
|
skip_before_action :authenticate_resource_owner!
|
||||||
|
|
||||||
before_action :store_current_location
|
before_action :store_current_location
|
||||||
before_action :authenticate_resource_owner!
|
before_action :authenticate_resource_owner!
|
||||||
|
|
||||||
|
include Localized
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def store_current_location
|
def store_current_location
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module SiteTitleHelper
|
||||||
|
def site_title
|
||||||
|
Setting.site_title.to_s
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,8 +20,6 @@ class FollowRemoteAccountService < BaseService
|
||||||
|
|
||||||
Rails.logger.debug "Looking up webfinger for #{uri}"
|
Rails.logger.debug "Looking up webfinger for #{uri}"
|
||||||
|
|
||||||
account = Account.new(username: username, domain: domain)
|
|
||||||
|
|
||||||
data = Goldfinger.finger("acct:#{uri}")
|
data = Goldfinger.finger("acct:#{uri}")
|
||||||
|
|
||||||
raise Goldfinger::Error, 'Missing resource links' if data.link('http://schemas.google.com/g/2010#updates-from').nil? || data.link('salmon').nil? || data.link('http://webfinger.net/rel/profile-page').nil? || data.link('magic-public-key').nil?
|
raise Goldfinger::Error, 'Missing resource links' if data.link('http://schemas.google.com/g/2010#updates-from').nil? || data.link('salmon').nil? || data.link('http://webfinger.net/rel/profile-page').nil? || data.link('magic-public-key').nil?
|
||||||
|
@ -37,6 +35,7 @@ class FollowRemoteAccountService < BaseService
|
||||||
|
|
||||||
domain_block = DomainBlock.find_by(domain: domain)
|
domain_block = DomainBlock.find_by(domain: domain)
|
||||||
|
|
||||||
|
account = Account.new(username: confirmed_username, domain: confirmed_domain)
|
||||||
account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href
|
account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href
|
||||||
account.salmon_url = data.link('salmon').href
|
account.salmon_url = data.link('salmon').href
|
||||||
account.url = data.link('http://webfinger.net/rel/profile-page').href
|
account.url = data.link('http://webfinger.net/rel/profile-page').href
|
||||||
|
@ -51,8 +50,8 @@ class FollowRemoteAccountService < BaseService
|
||||||
account.uri = get_account_uri(xml)
|
account.uri = get_account_uri(xml)
|
||||||
account.hub_url = hubs.first.attribute('href').value
|
account.hub_url = hubs.first.attribute('href').value
|
||||||
|
|
||||||
get_profile(body, account)
|
|
||||||
account.save!
|
account.save!
|
||||||
|
get_profile(body, account)
|
||||||
|
|
||||||
account
|
account
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,14 +5,13 @@ class ProcessFeedService < BaseService
|
||||||
xml = Nokogiri::XML(body)
|
xml = Nokogiri::XML(body)
|
||||||
xml.encoding = 'utf-8'
|
xml.encoding = 'utf-8'
|
||||||
|
|
||||||
update_author(body, xml, account)
|
update_author(body, account)
|
||||||
process_entries(xml, account)
|
process_entries(xml, account)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def update_author(body, xml, account)
|
def update_author(body, account)
|
||||||
return if xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS).nil?
|
|
||||||
RemoteProfileUpdateWorker.perform_async(account.id, body.force_encoding('UTF-8'), true)
|
RemoteProfileUpdateWorker.perform_async(account.id, body.force_encoding('UTF-8'), true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UpdateRemoteProfileService < BaseService
|
class UpdateRemoteProfileService < BaseService
|
||||||
def call(xml, account, resubscribe = false)
|
def call(body, account, resubscribe = false)
|
||||||
|
xml = Nokogiri::XML(body)
|
||||||
|
xml.encoding = 'utf-8'
|
||||||
|
|
||||||
|
xml = xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS) || xml.at_xpath('/xmlns:entry', xmlns: TagManager::XMLNS)
|
||||||
|
|
||||||
return if xml.nil?
|
return if xml.nil?
|
||||||
|
|
||||||
author_xml = xml.at_xpath('./xmlns:author', xmlns: TagManager::XMLNS) || xml.at_xpath('./dfrn:owner', dfrn: TagManager::DFRN_XMLNS)
|
author_xml = xml.at_xpath('./xmlns:author', xmlns: TagManager::XMLNS) || xml.at_xpath('./dfrn:owner', dfrn: TagManager::DFRN_XMLNS)
|
||||||
|
@ -12,9 +17,9 @@ class UpdateRemoteProfileService < BaseService
|
||||||
account.note = author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).content unless author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).nil?
|
account.note = author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).content unless author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).nil?
|
||||||
account.locked = author_xml.at_xpath('./mastodon:scope', mastodon: TagManager::MTDN_XMLNS)&.content == 'private'
|
account.locked = author_xml.at_xpath('./mastodon:scope', mastodon: TagManager::MTDN_XMLNS)&.content == 'private'
|
||||||
|
|
||||||
unless account.suspended? || DomainBlock.find_by(domain: account.domain)&.reject_media?
|
if !account.suspended? && !DomainBlock.find_by(domain: account.domain)&.reject_media?
|
||||||
account.avatar_remote_url = author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS)['href'] unless author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS).nil? || author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS)['href'].blank?
|
account.avatar_remote_url = link_href_from_xml(author_xml, 'avatar') if link_has_href?(author_xml, 'avatar')
|
||||||
account.header_remote_url = author_xml.at_xpath('./xmlns:link[@rel="header"]', xmlns: TagManager::XMLNS)['href'] unless author_xml.at_xpath('./xmlns:link[@rel="header"]', xmlns: TagManager::XMLNS).nil? || author_xml.at_xpath('./xmlns:link[@rel="header"]', xmlns: TagManager::XMLNS)['href'].blank?
|
account.header_remote_url = link_href_from_xml(author_xml, 'header') if link_has_href?(author_xml, 'header')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,4 +30,14 @@ class UpdateRemoteProfileService < BaseService
|
||||||
|
|
||||||
SubscribeService.new.call(account) if resubscribe && (account.hub_url != old_hub_url)
|
SubscribeService.new.call(account) if resubscribe && (account.hub_url != old_hub_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def link_href_from_xml(xml, type)
|
||||||
|
xml.at_xpath('./xmlns:link[@rel="' + type + '"]', xmlns: TagManager::XMLNS)['href']
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_has_href?(xml, type)
|
||||||
|
!(xml.at_xpath('./xmlns:link[@rel="' + type + '"]', xmlns: TagManager::XMLNS).nil? || xml.at_xpath('./xmlns:link[@rel="' + type + '"]', xmlns: TagManager::XMLNS)['href'].blank?)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
= Rails.configuration.x.local_domain
|
= Rails.configuration.x.local_domain
|
||||||
|
|
||||||
- content_for :header_tags do
|
- content_for :header_tags do
|
||||||
%meta{ property: 'og:site_name', content: 'Mastodon' }/
|
%meta{ property: 'og:site_name', content: site_title }/
|
||||||
%meta{ property: 'og:type', content: 'website' }/
|
%meta{ property: 'og:type', content: 'website' }/
|
||||||
%meta{ property: 'og:title', content: Rails.configuration.x.local_domain }/
|
%meta{ property: 'og:title', content: Rails.configuration.x.local_domain }/
|
||||||
%meta{ property: 'og:description', content: @description.blank? ? "Mastodon is a free, open-source social network server. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly" : strip_tags(@description) }/
|
%meta{ property: 'og:description', content: @description.blank? ? "Mastodon is a free, open-source social network server. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly" : strip_tags(@description) }/
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
%link{ rel: 'salmon', href: api_salmon_url(@account.id) }/
|
%link{ rel: 'salmon', href: api_salmon_url(@account.id) }/
|
||||||
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/
|
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/
|
||||||
|
|
||||||
%meta{ property: 'og:site_name', content: 'Mastodon' }/
|
%meta{ property: 'og:site_name', content: site_title }/
|
||||||
%meta{ property: 'og:type', content: 'profile' }/
|
%meta{ property: 'og:type', content: 'profile' }/
|
||||||
%meta{ property: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
|
%meta{ property: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
|
||||||
%meta{ property: 'og:description', content: @account.note }/
|
%meta{ property: 'og:description', content: @account.note }/
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
- if content_for?(:page_title)
|
- if content_for?(:page_title)
|
||||||
= yield(:page_title)
|
= yield(:page_title)
|
||||||
= ' - '
|
= ' - '
|
||||||
= Setting.site_title
|
= site_title
|
||||||
|
|
||||||
= stylesheet_link_tag 'application', media: 'all'
|
= stylesheet_link_tag 'application', media: 'all'
|
||||||
= csrf_meta_tags
|
= csrf_meta_tags
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
|
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
|
||||||
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/
|
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/
|
||||||
|
|
||||||
%meta{ property: 'og:site_name', content: 'Mastodon' }/
|
%meta{ property: 'og:site_name', content: site_title }/
|
||||||
%meta{ property: 'og:type', content: 'article' }/
|
%meta{ property: 'og:type', content: 'article' }/
|
||||||
%meta{ property: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
|
%meta{ property: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<p>Bienvenue <%= @resource.email %> !</p>
|
||||||
|
|
||||||
|
<p>Vous pouvez confirmer l'email de votre compte Mastodon en cliquant sur le lien ci-dessous :</p>
|
||||||
|
|
||||||
|
<p><%= link_to 'Confirmer mon compte', confirmation_url(@resource, confirmation_token: @token) %></p>
|
|
@ -0,0 +1,5 @@
|
||||||
|
Bienvenue <%= @resource.email %> !
|
||||||
|
|
||||||
|
Vous pouvez confirmer l'email de votre compte Mastodon en cliquant sur le lien ci-dessous :
|
||||||
|
|
||||||
|
<%= confirmation_url(@resource, confirmation_token: @token) %>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<p>Bonjour <%= @resource.email %> !</p>
|
||||||
|
|
||||||
|
<p>Nous vous contactons pour vous informer que votre mot de passe sur Mastodon a bien été modifié.</p>
|
|
@ -0,0 +1,3 @@
|
||||||
|
Bonjour <%= @resource.email %> !
|
||||||
|
|
||||||
|
Nous vous contactons pour vous informer que votre mot de passe sur Mastodon a bien été modifié.
|
|
@ -0,0 +1,8 @@
|
||||||
|
<p>Bonjour <%= @resource.email %> !</p>
|
||||||
|
|
||||||
|
<p>Quelqu'un a demandé à réinitialiser votre mot de passe sur Mastodon. Vous pouvez effectuer la réinitialisation en cliquant sur le lien ci-dessous.</p>
|
||||||
|
|
||||||
|
<p><%= link_to 'Modifier mon mot de passe', edit_password_url(@resource, reset_password_token: @token) %></p>
|
||||||
|
|
||||||
|
<p>Si vous n'êtes pas à l'origine de cette demande, vous pouvez ignorer ce message.</p>
|
||||||
|
<p>Votre mot de passe ne sera pas modifié tant que vous n'accéderez pas au lien ci-dessus et n'en choisirez pas un nouveau.</p>
|
|
@ -0,0 +1,8 @@
|
||||||
|
Bonjour <%= @resource.email %> !
|
||||||
|
|
||||||
|
Quelqu'un a demandé à réinitialiser votre mot de passe sur Mastodon. Vous pouvez effectuer la réinitialisation en cliquant sur le lien ci-dessous.
|
||||||
|
|
||||||
|
<%= edit_password_url(@resource, reset_password_token: @token) %>
|
||||||
|
|
||||||
|
Si vous n'êtes pas à l'origine de cette demande, vous pouvez ignorer ce message.
|
||||||
|
Votre mot de passe ne sera pas modifié tant que vous n'accéderez pas au lien ci-dessus et n'en choisirez pas un nouveau.
|
|
@ -6,14 +6,7 @@ class RemoteProfileUpdateWorker
|
||||||
sidekiq_options queue: 'pull'
|
sidekiq_options queue: 'pull'
|
||||||
|
|
||||||
def perform(account_id, body, resubscribe)
|
def perform(account_id, body, resubscribe)
|
||||||
account = Account.find(account_id)
|
UpdateRemoteProfileService.new.call(body, Account.find(account_id), resubscribe)
|
||||||
|
|
||||||
xml = Nokogiri::XML(body)
|
|
||||||
xml.encoding = 'utf-8'
|
|
||||||
|
|
||||||
author_container = xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS) || xml.at_xpath('/xmlns:entry', xmlns: TagManager::XMLNS)
|
|
||||||
|
|
||||||
UpdateRemoteProfileService.new.call(author_container, account, resubscribe)
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,8 +7,8 @@ de:
|
||||||
terms: AGB
|
terms: AGB
|
||||||
accounts:
|
accounts:
|
||||||
follow: Folgen
|
follow: Folgen
|
||||||
followers: Follower
|
followers: Folger
|
||||||
following: Gefolgt
|
following: Folgt
|
||||||
nothing_here: Hier gibt es nichts!
|
nothing_here: Hier gibt es nichts!
|
||||||
people_followed_by: Nutzer, denen %{name} folgt
|
people_followed_by: Nutzer, denen %{name} folgt
|
||||||
people_who_follow: Nutzer, die %{name} folgen
|
people_who_follow: Nutzer, die %{name} folgen
|
||||||
|
|
|
@ -7,7 +7,7 @@ fi:
|
||||||
business_email: 'Business e-mail:'
|
business_email: 'Business e-mail:'
|
||||||
contact: Ota yhteyttä
|
contact: Ota yhteyttä
|
||||||
description_headline: Mikä on %{domain}?
|
description_headline: Mikä on %{domain}?
|
||||||
domain_count_after: muut palvelimet
|
domain_count_after: muuhun palvelimeen
|
||||||
domain_count_before: Yhdistyneenä
|
domain_count_before: Yhdistyneenä
|
||||||
features:
|
features:
|
||||||
api: Avoin API ohjelmille ja palveluille
|
api: Avoin API ohjelmille ja palveluille
|
||||||
|
|
|
@ -5,13 +5,14 @@ fr:
|
||||||
about_this: À propos de cette instance
|
about_this: À propos de cette instance
|
||||||
apps: Applications
|
apps: Applications
|
||||||
business_email: E-mail professionnel
|
business_email: E-mail professionnel
|
||||||
closed_registrations: Les inscriptions sont actuellement fermées sur cette instance. .
|
closed_registrations: Les inscriptions sont actuellement fermées sur cette instance.
|
||||||
|
contact: Contact
|
||||||
description_headline: Qu'est-ce que %{domain} ?
|
description_headline: Qu'est-ce que %{domain} ?
|
||||||
domain_count_after: autres instances
|
domain_count_after: autres instances
|
||||||
domain_count_before: Connectés à
|
domain_count_before: Connectés à
|
||||||
features:
|
features:
|
||||||
api: API ouverte aux apps et services
|
api: API ouverte aux apps et services
|
||||||
blocking: Outils complets de bloquage et masquage
|
blocks: Outils complets de bloquage et masquage
|
||||||
characters: 500 caractères par post
|
characters: 500 caractères par post
|
||||||
chronology: Fil chronologique
|
chronology: Fil chronologique
|
||||||
ethics: 'Pas de pubs, pas de pistage'
|
ethics: 'Pas de pubs, pas de pistage'
|
||||||
|
@ -21,6 +22,7 @@ fr:
|
||||||
features_headline: Ce qui rend Mastodon différent
|
features_headline: Ce qui rend Mastodon différent
|
||||||
get_started: Rejoindre le réseau
|
get_started: Rejoindre le réseau
|
||||||
links: Liens
|
links: Liens
|
||||||
|
other_instances: Autres instances
|
||||||
source_code: Code source
|
source_code: Code source
|
||||||
status_count_after: posts
|
status_count_after: posts
|
||||||
status_count_before: Ayant publié
|
status_count_before: Ayant publié
|
||||||
|
@ -54,9 +56,24 @@ fr:
|
||||||
reset_password: Réinitialiser le mot de passe
|
reset_password: Réinitialiser le mot de passe
|
||||||
set_new_password: Définir le nouveau mot de passe
|
set_new_password: Définir le nouveau mot de passe
|
||||||
authorize_follow:
|
authorize_follow:
|
||||||
|
error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant
|
||||||
follow: Suivre
|
follow: Suivre
|
||||||
prompt_html: 'Vous (<strong>%{self}</strong>) avez demandé à suivre:'
|
prompt_html: 'Vous (<strong>%{self}</strong>) avez demandé à suivre:'
|
||||||
title: Suivre %{acct}
|
title: Suivre %{acct}
|
||||||
|
datetime:
|
||||||
|
distance_in_words:
|
||||||
|
about_x_hours: "%{count}h"
|
||||||
|
about_x_months: "%{count}mo"
|
||||||
|
about_x_years: "%{count}y"
|
||||||
|
almost_x_years: "%{count}y"
|
||||||
|
half_a_minute: A l'instant
|
||||||
|
less_than_x_minutes: "%{count}m"
|
||||||
|
less_than_x_seconds: A l'instant
|
||||||
|
over_x_years: "%{count}y"
|
||||||
|
x_days: "%{count}d"
|
||||||
|
x_minutes: "%{count}m"
|
||||||
|
x_months: "%{count}mo"
|
||||||
|
x_seconds: "%{count}s"
|
||||||
exports:
|
exports:
|
||||||
blocks: Vous bloquez
|
blocks: Vous bloquez
|
||||||
csv: CSV
|
csv: CSV
|
||||||
|
@ -93,6 +110,9 @@ fr:
|
||||||
follow:
|
follow:
|
||||||
body: "%{name} vous suit !"
|
body: "%{name} vous suit !"
|
||||||
subject: "%{name} vous suit"
|
subject: "%{name} vous suit"
|
||||||
|
follow_request:
|
||||||
|
body: "%{name} a demandé à vous suivre"
|
||||||
|
subject: 'Abonné⋅es en attente : %{name}'
|
||||||
mention:
|
mention:
|
||||||
body: "%{name} vous a mentionné⋅e dans :"
|
body: "%{name} vous a mentionné⋅e dans :"
|
||||||
subject: "%{name} vous a mentionné⋅e"
|
subject: "%{name} vous a mentionné⋅e"
|
||||||
|
@ -132,7 +152,7 @@ fr:
|
||||||
formats:
|
formats:
|
||||||
default: '%d %b %Y, %H:%M'
|
default: '%d %b %Y, %H:%M'
|
||||||
two_factor_auth:
|
two_factor_auth:
|
||||||
description_html: Si vous activez <strong>l'identification à deux facteurs</strong> vous devrez être en posession de votre téléphone afin de générer un code de connexion.
|
description_html: Si vous activez <strong>l'identification à deux facteurs</strong>, vous devrez être en possession de votre téléphone afin de générer un code de connexion.
|
||||||
disable: Désactiver
|
disable: Désactiver
|
||||||
enable: Activer
|
enable: Activer
|
||||||
instructions_html: "<strong>Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone</strong>. Désormais, cette application générera des jetons que vous devrez saisir à chaque connexion."
|
instructions_html: "<strong>Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone</strong>. Désormais, cette application générera des jetons que vous devrez saisir à chaque connexion."
|
||||||
|
|
|
@ -3,7 +3,7 @@ de:
|
||||||
simple_form:
|
simple_form:
|
||||||
hints:
|
hints:
|
||||||
defaults:
|
defaults:
|
||||||
locked: Erlaubt dir, Folger zu überprüfen, bevor sie dir folgen können
|
locked: Erlaubt dir, Nutzer zu überprüfen, bevor sie dir folgen können
|
||||||
labels:
|
labels:
|
||||||
defaults:
|
defaults:
|
||||||
avatar: Avatar
|
avatar: Avatar
|
||||||
|
@ -11,16 +11,16 @@ de:
|
||||||
confirm_password: Passwort bestätigen
|
confirm_password: Passwort bestätigen
|
||||||
current_password: Derzeitiges Passwort
|
current_password: Derzeitiges Passwort
|
||||||
display_name: Anzeigename
|
display_name: Anzeigename
|
||||||
email: E-mail-Addresse
|
email: E-Mail-Addresse
|
||||||
header: Kopfbild
|
header: Kopfbild
|
||||||
locale: Sprache
|
locale: Sprache
|
||||||
locked: Gesperrter Profil
|
locked: Gesperrtes Profil
|
||||||
new_password: Neues Passwort
|
new_password: Neues Passwort
|
||||||
note: Über mich
|
note: Über mich
|
||||||
password: Passwort
|
password: Passwort
|
||||||
username: Nutzername
|
username: Nutzername
|
||||||
interactions:
|
interactions:
|
||||||
must_be_follower: Benachrichtigungen von nicht-Folgern blockieren
|
must_be_follower: Benachrichtigungen von Nicht-Folgern blockieren
|
||||||
must_be_following: Benachrichtigungen von Nutzern blockieren, denen ich nicht folge
|
must_be_following: Benachrichtigungen von Nutzern blockieren, denen ich nicht folge
|
||||||
notification_emails:
|
notification_emails:
|
||||||
favourite: E-mail senden, wenn jemand meinen Beitrag favorisiert
|
favourite: E-mail senden, wenn jemand meinen Beitrag favorisiert
|
||||||
|
|
|
@ -33,7 +33,7 @@ fr:
|
||||||
must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas
|
must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas
|
||||||
must_be_following: Masquer les notifications des personnes que vous ne suivez pas
|
must_be_following: Masquer les notifications des personnes que vous ne suivez pas
|
||||||
notification_emails:
|
notification_emails:
|
||||||
digest: Envoyer des emails récapitulatifs
|
digest: Envoyer des courriels récapitulatifs
|
||||||
favourite: Envoyer un courriel lorsque quelqu’un ajoute mes statuts à ses favoris
|
favourite: Envoyer un courriel lorsque quelqu’un ajoute mes statuts à ses favoris
|
||||||
follow: Envoyer un courriel lorsque quelqu’un me suit
|
follow: Envoyer un courriel lorsque quelqu’un me suit
|
||||||
follow_request: Envoyer un courriel lorsque quelqu'un demande à me suivre
|
follow_request: Envoyer un courriel lorsque quelqu'un demande à me suivre
|
||||||
|
|
|
@ -10,7 +10,7 @@ These people make the development of Mastodon possible through [Patreon](https:/
|
||||||
- [Kurtis Rainbolt-Greene](https://mastodon.social/users/krainboltgreene)
|
- [Kurtis Rainbolt-Greene](https://mastodon.social/users/krainboltgreene)
|
||||||
- [Kit Redgrave](https://socially.constructed.space/users/KitRedgrave)
|
- [Kit Redgrave](https://socially.constructed.space/users/KitRedgrave)
|
||||||
- [Zeipher](https://mastodon.social/users/Zeipher)
|
- [Zeipher](https://mastodon.social/users/Zeipher)
|
||||||
- [Effy Elden](https://toot.zone/users/effy)
|
- [Effy Elden](https://mastodon.social/users/effy)
|
||||||
- [Zoë Quinn](https://mastodon.social/users/zoequinn)
|
- [Zoë Quinn](https://mastodon.social/users/zoequinn)
|
||||||
|
|
||||||
**Thank you to the following people**
|
**Thank you to the following people**
|
||||||
|
|
|
@ -39,6 +39,40 @@ You will want Amazon S3 for file storage. The only exception is for development
|
||||||
purposes, where you may not care if files are not saved. Follow a guide online
|
purposes, where you may not care if files are not saved. Follow a guide online
|
||||||
for creating a free Amazon S3 bucket and Access Key, then enter the details.
|
for creating a free Amazon S3 bucket and Access Key, then enter the details.
|
||||||
|
|
||||||
|
If you deploy from the web, the format for all the S3 bits use Paperclip conventions:
|
||||||
|
|
||||||
|
S3 Bucket is just the name of the bucket, e.g. `bucketname` not the full ARN.
|
||||||
|
|
||||||
|
S3 Region is the AWS code for the region e.g. `ap-northeast-1` not the name of the city displayed on the AWS Dashboard.
|
||||||
|
|
||||||
|
To protect the privacy of the users of the your instance, you should have permissons on the your S3 bucket set to no-read and no-write for the public and non-application-specific AWS users, with only one authorized IAM user or group set up to be able to upload or display content. This is an example of an IAM policy used for the S3 bucket used Mastadon instance hentai.loan:
|
||||||
|
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"s3:ListAllMyBuckets"
|
||||||
|
],
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:s3:::*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"s3:*"
|
||||||
|
],
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:s3:::hentailoan”,
|
||||||
|
"arn:aws:s3:::hentailoan/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
You can deploy from the Heroku web interface or from the command line. Run:
|
You can deploy from the Heroku web interface or from the command line. Run:
|
||||||
|
|
|
@ -90,7 +90,9 @@ It is recommended to create a special user for mastodon on the server (you could
|
||||||
|
|
||||||
sudo apt-get install imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev nodejs file git curl
|
sudo apt-get install imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev nodejs file git curl
|
||||||
curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
|
curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
|
||||||
apt-get install nodejs
|
|
||||||
|
sudo apt-get install nodejs
|
||||||
|
|
||||||
sudo npm install -g yarn
|
sudo npm install -g yarn
|
||||||
|
|
||||||
## Redis
|
## Redis
|
||||||
|
|
|
@ -7,7 +7,7 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
|
||||||
| -------------|-------------|---|---|
|
| -------------|-------------|---|---|
|
||||||
| [mastodon.social](https://mastodon.social) |Flagship, quick updates|No|No|
|
| [mastodon.social](https://mastodon.social) |Flagship, quick updates|No|No|
|
||||||
| [securitymastod.one](https://securitymastod.one/) |Information security enthusiasts and pros|Yes|Yes|
|
| [securitymastod.one](https://securitymastod.one/) |Information security enthusiasts and pros|Yes|Yes|
|
||||||
| [mastodon.nuzgo.net](https://mastodon.nuzgo.net/) |Mastodon instance hosted in Paris |Yes|No|
|
| [mastodon.nuzgo.net](https://mastodon.nuzgo.net/) |Mastodon instance hosted in Paris |Yes|Yes|
|
||||||
| [mastodon.cx](https://mastodon.cx/) |Alternative Mastodon instance hosted in France|Yes|Yes|
|
| [mastodon.cx](https://mastodon.cx/) |Alternative Mastodon instance hosted in France|Yes|Yes|
|
||||||
| [mastodon.network](https://mastodon.network) |N/A|Yes|Yes|
|
| [mastodon.network](https://mastodon.network) |N/A|Yes|Yes|
|
||||||
| [awoo.space](https://awoo.space) |Intentionally moderated, only federates with mastodon.social|Yes|No|
|
| [awoo.space](https://awoo.space) |Intentionally moderated, only federates with mastodon.social|Yes|No|
|
||||||
|
@ -69,7 +69,7 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
|
||||||
| [meow.social](https://meow.social)|A furry fandom focused instance|Yes|No|
|
| [meow.social](https://meow.social)|A furry fandom focused instance|Yes|No|
|
||||||
| [neumastodon.com](https://neumastodon.com/)|Northeastern University Mastodon |Yes|No|
|
| [neumastodon.com](https://neumastodon.com/)|Northeastern University Mastodon |Yes|No|
|
||||||
| [dancingbanana.party](https://dancingbanana.party)|La banane qui danse.|Yes|No|
|
| [dancingbanana.party](https://dancingbanana.party)|La banane qui danse.|Yes|No|
|
||||||
| [mastodon.brussels.fr](https://mastodon.brussels/)|Le mastodon pour les belges, si vous aimez la bonne ambiance venez nous rejoindre !|Yes|Yes|
|
| [mastodon.brussels](https://mastodon.brussels/)|Le mastodon pour les belges, si vous aimez la bonne ambiance venez nous rejoindre !|Yes|Yes|
|
||||||
| [mastodon.llamasweet.tech](https://mastodon.llamasweet.tech/)|Mastodon about Android developement|Yes|No|
|
| [mastodon.llamasweet.tech](https://mastodon.llamasweet.tech/)|Mastodon about Android developement|Yes|No|
|
||||||
| [manx.social](https://manx.social/)|Instance for the Isle of Man|Yes|Yes|
|
| [manx.social](https://manx.social/)|Instance for the Isle of Man|Yes|Yes|
|
||||||
| [mastodon.host](https://mastodon.host/)|Lightly moderated, federates everywhere and has a follow bot ( Huge federated timeline )|Yes|No|
|
| [mastodon.host](https://mastodon.host/)|Lightly moderated, federates everywhere and has a follow bot ( Huge federated timeline )|Yes|No|
|
||||||
|
@ -77,6 +77,7 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
|
||||||
| [oulipo.social](https://oulipo.social/)|An Oulipo Mastodon in which that fifth symbol in Latin script is taboo|Yes|No|
|
| [oulipo.social](https://oulipo.social/)|An Oulipo Mastodon in which that fifth symbol in Latin script is taboo|Yes|No|
|
||||||
| [indigo.zone](https://indigo.zone)|Open Registrations, General Purpose|Yes|No|
|
| [indigo.zone](https://indigo.zone)|Open Registrations, General Purpose|Yes|No|
|
||||||
| [mastodones.club](https://mastodones.club)|Mastodon en español|Yes|Yes|
|
| [mastodones.club](https://mastodones.club)|Mastodon en español|Yes|Yes|
|
||||||
|
| [mst3k.interlinked.me](https://mst3k.interlinked.me)|Open registrations, general purpose|Yes|Yes|
|
||||||
|
|
||||||
|
|
||||||
Let me know if you start running one so I can add it to the list! (Alternatively, add it yourself as a pull request).
|
We are no longer maintaining this list as instances are popping up too quickly for using GitHub to be a tenable system for tracking them. Please standby while we work on another solution
|
||||||
|
|
|
@ -43,6 +43,7 @@ ___
|
||||||
- [For Python](https://github.com/halcy/Mastodon.py)
|
- [For Python](https://github.com/halcy/Mastodon.py)
|
||||||
- [For JavaScript](https://github.com/Zatnosk/libodonjs)
|
- [For JavaScript](https://github.com/Zatnosk/libodonjs)
|
||||||
- [For JavaScript (Node.js)](https://github.com/jessicahayley/node-mastodon)
|
- [For JavaScript (Node.js)](https://github.com/jessicahayley/node-mastodon)
|
||||||
|
- [For Elixir](https://github.com/milmazz/hunter)
|
||||||
|
|
||||||
___
|
___
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe "site_title" do
|
||||||
|
it "Uses the Setting.site_title value when it exists" do
|
||||||
|
Setting.site_title = "New site title"
|
||||||
|
|
||||||
|
expect(helper.site_title).to eq "New site title"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns empty string when Setting.site_title is nil" do
|
||||||
|
Setting.site_title = nil
|
||||||
|
|
||||||
|
expect(helper.site_title).to eq ""
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,7 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe UpdateRemoteProfileService do
|
RSpec.describe UpdateRemoteProfileService do
|
||||||
let(:xml) { Nokogiri::XML(File.read(File.join(Rails.root, 'spec', 'fixtures', 'push', 'feed.atom'))).at_xpath('//xmlns:feed') }
|
let(:xml) { File.read(File.join(Rails.root, 'spec', 'fixtures', 'push', 'feed.atom')) }
|
||||||
|
|
||||||
subject { UpdateRemoteProfileService.new }
|
subject { UpdateRemoteProfileService.new }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue