mirror of https://github.com/Siphonay/mastodon
Merge changes from upstream with the CSS reload fix
This commit is contained in:
commit
d69fa9e1f4
|
@ -12,9 +12,11 @@ EXPOSE 3000 4000
|
|||
WORKDIR /mastodon
|
||||
|
||||
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
|
||||
&& echo "@edge https://nl.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \
|
||||
&& apk -U upgrade \
|
||||
&& apk add -t build-dependencies \
|
||||
build-base \
|
||||
icu-dev \
|
||||
libidn-dev \
|
||||
libxml2-dev \
|
||||
libxslt-dev \
|
||||
|
@ -26,7 +28,7 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
|
|||
ffmpeg \
|
||||
file \
|
||||
git \
|
||||
icu-dev \
|
||||
icu-libs \
|
||||
imagemagick@edge \
|
||||
libidn \
|
||||
libpq \
|
||||
|
@ -37,7 +39,7 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
|
|||
protobuf \
|
||||
su-exec \
|
||||
tini \
|
||||
&& npm install -g npm@3 && npm install -g yarn \
|
||||
yarn@edge \
|
||||
&& update-ca-certificates \
|
||||
&& rm -rf /tmp/* /var/cache/apk/*
|
||||
|
||||
|
|
|
@ -35,9 +35,10 @@ sudo apt-get install \
|
|||
postgresql-contrib \
|
||||
protobuf-compiler \
|
||||
yarn \
|
||||
libicu-dev \
|
||||
libidn11-dev \
|
||||
libprotobuf-dev \
|
||||
libreadline-dev \
|
||||
libicu-dev \
|
||||
-y
|
||||
|
||||
# Install rvm
|
||||
|
|
|
@ -13,7 +13,7 @@ class AccountsController < ApplicationController
|
|||
|
||||
format.atom do
|
||||
@entries = @account.stream_entries.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
|
||||
render xml: Ostatus::AtomSerializer.render(Ostatus::AtomSerializer.new.feed(@account, @entries.to_a))
|
||||
end
|
||||
|
||||
format.json do
|
||||
|
|
|
@ -5,7 +5,14 @@ module Admin
|
|||
include Authorization
|
||||
|
||||
before_action :set_report
|
||||
before_action :set_status
|
||||
before_action :set_status, only: [:update, :destroy]
|
||||
|
||||
def create
|
||||
@form = Form::StatusBatch.new(form_status_batch_params)
|
||||
flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
|
||||
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
def update
|
||||
@status.update(status_params)
|
||||
|
@ -15,7 +22,7 @@ module Admin
|
|||
def destroy
|
||||
authorize @status, :destroy?
|
||||
RemovalWorker.perform_async(@status.id)
|
||||
redirect_to admin_report_path(@report)
|
||||
render json: @status
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -24,6 +31,10 @@ module Admin
|
|||
params.require(:status).permit(:sensitive)
|
||||
end
|
||||
|
||||
def form_status_batch_params
|
||||
params.require(:form_status_batch).permit(:action, status_ids: [])
|
||||
end
|
||||
|
||||
def set_report
|
||||
@report = Report.find(params[:report_id])
|
||||
end
|
||||
|
|
|
@ -8,7 +8,9 @@ module Admin
|
|||
@reports = filtered_reports.page(params[:page])
|
||||
end
|
||||
|
||||
def show; end
|
||||
def show
|
||||
@form = Form::StatusBatch.new
|
||||
end
|
||||
|
||||
def update
|
||||
process_report
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class StatusesController < BaseController
|
||||
include Authorization
|
||||
|
||||
helper_method :current_params
|
||||
|
||||
before_action :set_account
|
||||
before_action :set_status, only: [:update, :destroy]
|
||||
|
||||
PAR_PAGE = 20
|
||||
|
||||
def index
|
||||
@statuses = @account.statuses
|
||||
if params[:media]
|
||||
account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||
@statuses.merge!(Status.where(id: account_media_status_ids))
|
||||
end
|
||||
@statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PAR_PAGE)
|
||||
|
||||
@form = Form::StatusBatch.new
|
||||
end
|
||||
|
||||
def create
|
||||
@form = Form::StatusBatch.new(form_status_batch_params)
|
||||
flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
|
||||
|
||||
redirect_to admin_account_statuses_path(@account.id, current_params)
|
||||
end
|
||||
|
||||
def update
|
||||
@status.update(status_params)
|
||||
redirect_to admin_account_statuses_path(@account.id, current_params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @status, :destroy?
|
||||
RemovalWorker.perform_async(@status.id)
|
||||
render json: @status
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_params
|
||||
params.require(:status).permit(:sensitive)
|
||||
end
|
||||
|
||||
def form_status_batch_params
|
||||
params.require(:form_status_batch).permit(:action, status_ids: [])
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = @account.statuses.find(params[:id])
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def current_params
|
||||
page = (params[:page] || 1).to_i
|
||||
{
|
||||
media: params[:media],
|
||||
page: page > 1 && page,
|
||||
}.select { |_, value| value.present? }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -35,6 +35,7 @@ class Settings::PreferencesController < ApplicationController
|
|||
params.require(:user).permit(
|
||||
:setting_default_privacy,
|
||||
:setting_default_sensitive,
|
||||
:setting_unfollow_modal,
|
||||
:setting_boost_modal,
|
||||
:setting_delete_modal,
|
||||
:setting_auto_play_gif,
|
||||
|
|
|
@ -19,7 +19,7 @@ class StreamEntriesController < ApplicationController
|
|||
end
|
||||
|
||||
format.atom do
|
||||
render xml: AtomSerializer.render(AtomSerializer.new.entry(@stream_entry, true))
|
||||
render xml: Ostatus::AtomSerializer.render(Ostatus::AtomSerializer.new.entry(@stream_entry, true))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -186,6 +186,12 @@ export default class MediaGallery extends React.PureComponent {
|
|||
visible: !this.props.sensitive,
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.sensitive !== this.props.sensitive) {
|
||||
this.setState({ visible: !nextProps.sensitive });
|
||||
}
|
||||
}
|
||||
|
||||
handleOpen = () => {
|
||||
this.setState({ visible: !this.state.visible });
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { makeGetAccount } from '../selectors';
|
||||
import Account from '../components/account';
|
||||
import {
|
||||
|
@ -9,6 +11,11 @@ import {
|
|||
muteAccount,
|
||||
unmuteAccount,
|
||||
} from '../actions/accounts';
|
||||
import { openModal } from '../actions/modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
|
||||
});
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getAccount = makeGetAccount();
|
||||
|
@ -16,15 +23,25 @@ const makeMapStateToProps = () => {
|
|||
const mapStateToProps = (state, props) => ({
|
||||
account: getAccount(state, props.id),
|
||||
me: state.getIn(['meta', 'me']),
|
||||
unfollowModal: state.getIn(['meta', 'unfollow_modal']),
|
||||
});
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
|
||||
onFollow (account) {
|
||||
if (account.getIn(['relationship', 'following'])) {
|
||||
dispatch(unfollowAccount(account.get('id')));
|
||||
if (this.unfollowModal) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
|
||||
confirm: intl.formatMessage(messages.unfollowConfirm),
|
||||
onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
|
||||
}));
|
||||
} else {
|
||||
dispatch(unfollowAccount(account.get('id')));
|
||||
}
|
||||
} else {
|
||||
dispatch(followAccount(account.get('id')));
|
||||
}
|
||||
|
@ -45,6 +62,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
dispatch(muteAccount(account.get('id')));
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default connect(makeMapStateToProps, mapDispatchToProps)(Account);
|
||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account));
|
||||
|
|
|
@ -17,6 +17,7 @@ import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
|
|||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
|
||||
blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
|
||||
muteConfirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' },
|
||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||
|
@ -28,15 +29,25 @@ const makeMapStateToProps = () => {
|
|||
const mapStateToProps = (state, { accountId }) => ({
|
||||
account: getAccount(state, Number(accountId)),
|
||||
me: state.getIn(['meta', 'me']),
|
||||
unfollowModal: state.getIn(['meta', 'unfollow_modal']),
|
||||
});
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
|
||||
onFollow (account) {
|
||||
if (account.getIn(['relationship', 'following'])) {
|
||||
dispatch(unfollowAccount(account.get('id')));
|
||||
if (this.unfollowModal) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
|
||||
confirm: intl.formatMessage(messages.unfollowConfirm),
|
||||
onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
|
||||
}));
|
||||
} else {
|
||||
dispatch(unfollowAccount(account.get('id')));
|
||||
}
|
||||
} else {
|
||||
dispatch(followAccount(account.get('id')));
|
||||
}
|
||||
|
@ -85,6 +96,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
onUnblockDomain (domain, accountId) {
|
||||
dispatch(unblockDomain(domain, accountId));
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
|
||||
|
|
|
@ -87,7 +87,7 @@ export default class ModalRoot extends React.PureComponent {
|
|||
>
|
||||
{interpolatedStyles =>
|
||||
<div className='modal-root'>
|
||||
{interpolatedStyles.map(({ key, data: { type }, style }) => (
|
||||
{interpolatedStyles.map(({ key, data: { type, props }, style }) => (
|
||||
<div key={key} style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
||||
<div role='presentation' className='modal-root__overlay' style={{ opacity: style.opacity }} onClick={onClose} />
|
||||
<div className='modal-root__container' style={{ opacity: style.opacity, transform: `translateZ(0px) scale(${style.scale})` }}>
|
||||
|
|
|
@ -10,31 +10,36 @@ const makeGetStatusIds = () => createSelector([
|
|||
(state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()),
|
||||
(state) => state.get('statuses'),
|
||||
(state) => state.getIn(['meta', 'me']),
|
||||
], (columnSettings, statusIds, statuses, me) => statusIds.filter(id => {
|
||||
const statusForId = statuses.get(id);
|
||||
let showStatus = true;
|
||||
], (columnSettings, statusIds, statuses, me) => {
|
||||
const rawRegex = columnSettings.getIn(['regex', 'body'], '').trim();
|
||||
let regex = null;
|
||||
|
||||
if (columnSettings.getIn(['shows', 'reblog']) === false) {
|
||||
showStatus = showStatus && statusForId.get('reblog') === null;
|
||||
try {
|
||||
regex = rawRegex && new RegExp(rawRegex, 'i');
|
||||
} catch (e) {
|
||||
// Bad regex, don't affect filters
|
||||
}
|
||||
|
||||
if (columnSettings.getIn(['shows', 'reply']) === false) {
|
||||
showStatus = showStatus && (statusForId.get('in_reply_to_id') === null || statusForId.get('in_reply_to_account_id') === me);
|
||||
}
|
||||
return statusIds.filter(id => {
|
||||
const statusForId = statuses.get(id);
|
||||
let showStatus = true;
|
||||
|
||||
if (columnSettings.getIn(['regex', 'body'], '').trim().length > 0) {
|
||||
try {
|
||||
if (showStatus) {
|
||||
const regex = new RegExp(columnSettings.getIn(['regex', 'body']).trim(), 'i');
|
||||
showStatus = !regex.test(statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'search_index']) : statusForId.get('search_index'));
|
||||
}
|
||||
} catch(e) {
|
||||
// Bad regex, don't affect filters
|
||||
if (columnSettings.getIn(['shows', 'reblog']) === false) {
|
||||
showStatus = showStatus && statusForId.get('reblog') === null;
|
||||
}
|
||||
}
|
||||
|
||||
return showStatus;
|
||||
}));
|
||||
if (columnSettings.getIn(['shows', 'reply']) === false) {
|
||||
showStatus = showStatus && (statusForId.get('in_reply_to_id') === null || statusForId.get('in_reply_to_account_id') === me);
|
||||
}
|
||||
|
||||
if (showStatus && regex && statusForId.get('account') !== me) {
|
||||
const searchIndex = statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'search_index']) : statusForId.get('search_index');
|
||||
showStatus = !regex.test(searchIndex);
|
||||
}
|
||||
|
||||
return showStatus;
|
||||
});
|
||||
});
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getStatusIds = makeGetStatusIds();
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
|
||||
"confirmations.mute.confirm": "أكتم",
|
||||
"confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟",
|
||||
"confirmations.unfollow.confirm": "Unfollow",
|
||||
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
|
||||
"emoji_button.activity": "الأنشطة",
|
||||
"emoji_button.flags": "الأعلام",
|
||||
"emoji_button.food": "الطعام والشراب",
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
|
||||
"confirmations.mute.confirm": "Mute",
|
||||
"confirmations.mute.message": "Are you sure you want to mute {name}?",
|
||||
"confirmations.unfollow.confirm": "Unfollow",
|
||||
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
|
||||
"emoji_button.activity": "Activity",
|
||||
"emoji_button.flags": "Flags",
|
||||
"emoji_button.food": "Food & Drink",
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
"confirmations.domain_block.message": "Estàs realment, realment segur que vols bloquejar totalment {domain}? En la majoria dels casos bloquejar o silenciar és suficient i preferible.",
|
||||
"confirmations.mute.confirm": "Silenciar",
|
||||
"confirmations.mute.message": "Estàs segur que vols silenciar {name}?",
|
||||
"confirmations.unfollow.confirm": "Unfollow",
|
||||
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
|
||||