Merge tag 'v2.7.1' into instance_only_statuses
This commit is contained in:
commit
e8012c10be
14
CHANGELOG.md
14
CHANGELOG.md
@ -3,6 +3,20 @@ Changelog
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [2.7.1] - 2019-01-28
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix SSO authentication not working due to missing agreement boolean ([Gargron](https://github.com/tootsuite/mastodon/pull/9915))
|
||||||
|
- Fix slow fallback of CopyAccountStats migration setting stats to 0 ([Gargron](https://github.com/tootsuite/mastodon/pull/9930))
|
||||||
|
- Fix wrong command in migration error message ([angristan](https://github.com/tootsuite/mastodon/pull/9877))
|
||||||
|
- Fix initial value of volume slider in video player and handle volume changes ([ThibG](https://github.com/tootsuite/mastodon/pull/9929))
|
||||||
|
- Fix missing hotkeys for notifications ([ThibG](https://github.com/tootsuite/mastodon/pull/9927))
|
||||||
|
- Fix being able to attach unattached media created by other users ([ThibG](https://github.com/tootsuite/mastodon/pull/9921))
|
||||||
|
- Fix unrescued SSL error during link verification ([renatolond](https://github.com/tootsuite/mastodon/pull/9914))
|
||||||
|
- Fix Firefox scrollbar color regression ([trwnh](https://github.com/tootsuite/mastodon/pull/9908))
|
||||||
|
- Fix scheduled status with media immediately creating a status ([ThibG](https://github.com/tootsuite/mastodon/pull/9894))
|
||||||
|
- Fix missing strong style for landing page description ([Kjwon15](https://github.com/tootsuite/mastodon/pull/9892))
|
||||||
|
|
||||||
## [2.7.0] - 2019-01-20
|
## [2.7.0] - 2019-01-20
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ You can contribute in the following ways:
|
|||||||
- Contributing code to Mastodon by fixing bugs or implementing features
|
- Contributing code to Mastodon by fixing bugs or implementing features
|
||||||
- Improving the documentation
|
- Improving the documentation
|
||||||
|
|
||||||
|
If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
||||||
|
|
||||||
## Bug reports
|
## Bug reports
|
||||||
|
|
||||||
Bug reports and feature suggestions can be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected in the past using the search function. Please also use descriptive, concise titles.
|
Bug reports and feature suggestions can be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected in the past using the search function. Please also use descriptive, concise titles.
|
||||||
|
2
Gemfile
2
Gemfile
@ -23,7 +23,7 @@ gem 'paperclip-av-transcoder', '~> 0.6'
|
|||||||
gem 'streamio-ffmpeg', '~> 3.0'
|
gem 'streamio-ffmpeg', '~> 3.0'
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
gem 'addressable', '~> 2.5'
|
gem 'addressable', '~> 2.6'
|
||||||
gem 'bootsnap', '~> 1.3', require: false
|
gem 'bootsnap', '~> 1.3', require: false
|
||||||
gem 'browser'
|
gem 'browser'
|
||||||
gem 'charlock_holmes', '~> 0.7.6'
|
gem 'charlock_holmes', '~> 0.7.6'
|
||||||
|
18
Gemfile.lock
18
Gemfile.lock
@ -62,7 +62,7 @@ GEM
|
|||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.5.2)
|
addressable (2.6.0)
|
||||||
public_suffix (>= 2.0.2, < 4.0)
|
public_suffix (>= 2.0.2, < 4.0)
|
||||||
airbrussh (1.3.0)
|
airbrussh (1.3.0)
|
||||||
sshkit (>= 1.6.1, != 1.7.0)
|
sshkit (>= 1.6.1, != 1.7.0)
|
||||||
@ -290,8 +290,8 @@ GEM
|
|||||||
json-ld (3.0.2)
|
json-ld (3.0.2)
|
||||||
multi_json (~> 1.12)
|
multi_json (~> 1.12)
|
||||||
rdf (>= 2.2.8, < 4.0)
|
rdf (>= 2.2.8, < 4.0)
|
||||||
json-ld-preloaded (3.0.0)
|
json-ld-preloaded (3.0.2)
|
||||||
json-ld (>= 2.2, < 4.0)
|
json-ld (~> 3.0)
|
||||||
multi_json (~> 1.12)
|
multi_json (~> 1.12)
|
||||||
rdf (~> 3.0)
|
rdf (~> 3.0)
|
||||||
jsonapi-renderer (0.2.0)
|
jsonapi-renderer (0.2.0)
|
||||||
@ -363,7 +363,7 @@ GEM
|
|||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
sidekiq (>= 3.5)
|
sidekiq (>= 3.5)
|
||||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||||
oj (3.7.7)
|
oj (3.7.8)
|
||||||
omniauth (1.9.0)
|
omniauth (1.9.0)
|
||||||
hashie (>= 3.4.6, < 3.7.0)
|
hashie (>= 3.4.6, < 3.7.0)
|
||||||
rack (>= 1.6.2, < 3)
|
rack (>= 1.6.2, < 3)
|
||||||
@ -389,7 +389,7 @@ GEM
|
|||||||
paperclip-av-transcoder (0.6.4)
|
paperclip-av-transcoder (0.6.4)
|
||||||
av (~> 0.9.0)
|
av (~> 0.9.0)
|
||||||
paperclip (>= 2.5.2)
|
paperclip (>= 2.5.2)
|
||||||
parallel (1.12.1)
|
parallel (1.13.0)
|
||||||
parallel_tests (2.27.1)
|
parallel_tests (2.27.1)
|
||||||
parallel
|
parallel
|
||||||
parser (2.6.0.0)
|
parser (2.6.0.0)
|
||||||
@ -420,7 +420,7 @@ GEM
|
|||||||
pry (>= 0.10.4)
|
pry (>= 0.10.4)
|
||||||
public_suffix (3.0.3)
|
public_suffix (3.0.3)
|
||||||
puma (3.12.0)
|
puma (3.12.0)
|
||||||
pundit (2.0.0)
|
pundit (2.0.1)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
raabro (1.1.6)
|
raabro (1.1.6)
|
||||||
rack (2.0.6)
|
rack (2.0.6)
|
||||||
@ -513,7 +513,7 @@ GEM
|
|||||||
rspec-mocks (3.8.0)
|
rspec-mocks (3.8.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.8.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-rails (3.8.1)
|
rspec-rails (3.8.2)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
railties (>= 3.0)
|
railties (>= 3.0)
|
||||||
@ -525,7 +525,7 @@ GEM
|
|||||||
rspec-core (~> 3.0, >= 3.0.0)
|
rspec-core (~> 3.0, >= 3.0.0)
|
||||||
sidekiq (>= 2.4.0)
|
sidekiq (>= 2.4.0)
|
||||||
rspec-support (3.8.0)
|
rspec-support (3.8.0)
|
||||||
rubocop (0.63.0)
|
rubocop (0.63.1)
|
||||||
jaro_winkler (~> 1.5.1)
|
jaro_winkler (~> 1.5.1)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 2.5, != 2.5.1.1)
|
parser (>= 2.5, != 2.5.1.1)
|
||||||
@ -655,7 +655,7 @@ PLATFORMS
|
|||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
active_model_serializers (~> 0.10)
|
active_model_serializers (~> 0.10)
|
||||||
active_record_query_trace (~> 1.5)
|
active_record_query_trace (~> 1.5)
|
||||||
addressable (~> 2.5)
|
addressable (~> 2.6)
|
||||||
annotate (~> 2.7)
|
annotate (~> 2.7)
|
||||||
aws-sdk-s3 (~> 1.30)
|
aws-sdk-s3 (~> 1.30)
|
||||||
better_errors (~> 2.5)
|
better_errors (~> 2.5)
|
||||||
|
@ -80,7 +80,7 @@ A **Vagrant** configuration is included for development purposes.
|
|||||||
|
|
||||||
Mastodon is **free, open source software** licensed under **AGPLv3**.
|
Mastodon is **free, open source software** licensed under **AGPLv3**.
|
||||||
|
|
||||||
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Weblate. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md)
|
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Weblate. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
||||||
|
|
||||||
**IRC channel**: #mastodon on irc.freenode.net
|
**IRC channel**: #mastodon on irc.freenode.net
|
||||||
|
|
||||||
|
@ -29,6 +29,10 @@ class Notification extends ImmutablePureComponent {
|
|||||||
onMoveUp: PropTypes.func.isRequired,
|
onMoveUp: PropTypes.func.isRequired,
|
||||||
onMoveDown: PropTypes.func.isRequired,
|
onMoveDown: PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention: PropTypes.func.isRequired,
|
||||||
|
onFavourite: PropTypes.func.isRequired,
|
||||||
|
onReblog: PropTypes.func.isRequired,
|
||||||
|
onToggleHidden: PropTypes.func.isRequired,
|
||||||
|
status: PropTypes.option,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,14 +68,32 @@ class Notification extends ImmutablePureComponent {
|
|||||||
onMention(notification.get('account'), this.context.router.history);
|
onMention(notification.get('account'), this.context.router.history);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleHotkeyFavourite = () => {
|
||||||
|
const { status } = this.props;
|
||||||
|
if (status) this.props.onFavourite(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHotkeyBoost = e => {
|
||||||
|
const { status } = this.props;
|
||||||
|
if (status) this.props.onReblog(status, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHotkeyToggleHidden = () => {
|
||||||
|
const { status } = this.props;
|
||||||
|
if (status) this.props.onToggleHidden(status);
|
||||||
|
}
|
||||||
|
|
||||||
getHandlers () {
|
getHandlers () {
|
||||||
return {
|
return {
|
||||||
moveUp: this.handleMoveUp,
|
reply: this.handleMention,
|
||||||
moveDown: this.handleMoveDown,
|
favourite: this.handleHotkeyFavourite,
|
||||||
|
boost: this.handleHotkeyBoost,
|
||||||
|
mention: this.handleMention,
|
||||||
open: this.handleOpen,
|
open: this.handleOpen,
|
||||||
openProfile: this.handleOpenProfile,
|
openProfile: this.handleOpenProfile,
|
||||||
mention: this.handleMention,
|
moveUp: this.handleMoveUp,
|
||||||
reply: this.handleMention,
|
moveDown: this.handleMoveDown,
|
||||||
|
toggleHidden: this.handleHotkeyToggleHidden,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,31 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeGetNotification } from '../../../selectors';
|
import { makeGetNotification, makeGetStatus } from '../../../selectors';
|
||||||
import Notification from '../components/notification';
|
import Notification from '../components/notification';
|
||||||
|
import { openModal } from '../../../actions/modal';
|
||||||
import { mentionCompose } from '../../../actions/compose';
|
import { mentionCompose } from '../../../actions/compose';
|
||||||
|
import {
|
||||||
|
reblog,
|
||||||
|
favourite,
|
||||||
|
unreblog,
|
||||||
|
unfavourite,
|
||||||
|
} from '../../../actions/interactions';
|
||||||
|
import {
|
||||||
|
hideStatus,
|
||||||
|
revealStatus,
|
||||||
|
} from '../../../actions/statuses';
|
||||||
|
import { boostModal } from '../../../initial_state';
|
||||||
|
|
||||||
const makeMapStateToProps = () => {
|
const makeMapStateToProps = () => {
|
||||||
const getNotification = makeGetNotification();
|
const getNotification = makeGetNotification();
|
||||||
|
const getStatus = makeGetStatus();
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => {
|
||||||
notification: getNotification(state, props.notification, props.accountId),
|
const notification = getNotification(state, props.notification, props.accountId);
|
||||||
});
|
return {
|
||||||
|
notification: notification,
|
||||||
|
status: notification.get('status') ? getStatus(state, { id: notification.get('status') }) : null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
return mapStateToProps;
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
@ -17,6 +34,38 @@ const mapDispatchToProps = dispatch => ({
|
|||||||
onMention: (account, router) => {
|
onMention: (account, router) => {
|
||||||
dispatch(mentionCompose(account, router));
|
dispatch(mentionCompose(account, router));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onModalReblog (status) {
|
||||||
|
dispatch(reblog(status));
|
||||||
|
},
|
||||||
|
|
||||||
|
onReblog (status, e) {
|
||||||
|
if (status.get('reblogged')) {
|
||||||
|
dispatch(unreblog(status));
|
||||||
|
} else {
|
||||||
|
if (e.shiftKey || !boostModal) {
|
||||||
|
this.onModalReblog(status);
|
||||||
|
} else {
|
||||||
|
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onFavourite (status) {
|
||||||
|
if (status.get('favourited')) {
|
||||||
|
dispatch(unfavourite(status));
|
||||||
|
} else {
|
||||||
|
dispatch(favourite(status));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onToggleHidden (status) {
|
||||||
|
if (status.get('hidden')) {
|
||||||
|
dispatch(revealStatus(status.get('id')));
|
||||||
|
} else {
|
||||||
|
dispatch(hideStatus(status.get('id')));
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeMapStateToProps, mapDispatchToProps)(Notification);
|
export default connect(makeMapStateToProps, mapDispatchToProps)(Notification);
|
||||||
|
@ -136,6 +136,9 @@ class Video extends React.PureComponent {
|
|||||||
|
|
||||||
setVideoRef = c => {
|
setVideoRef = c => {
|
||||||
this.video = c;
|
this.video = c;
|
||||||
|
if (this.video) {
|
||||||
|
this.setState({ volume: this.video.volume, muted: this.video.muted });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSeekRef = c => {
|
setSeekRef = c => {
|
||||||
@ -302,6 +305,10 @@ class Video extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleVolumeChange = () => {
|
||||||
|
this.setState({ volume: this.video.volume, muted: this.video.muted });
|
||||||
|
}
|
||||||
|
|
||||||
handleOpenVideo = () => {
|
handleOpenVideo = () => {
|
||||||
const { src, preview, width, height, alt } = this.props;
|
const { src, preview, width, height, alt } = this.props;
|
||||||
const media = fromJS({
|
const media = fromJS({
|
||||||
@ -387,6 +394,7 @@ class Video extends React.PureComponent {
|
|||||||
onTimeUpdate={this.handleTimeUpdate}
|
onTimeUpdate={this.handleTimeUpdate}
|
||||||
onLoadedData={this.handleLoadedData}
|
onLoadedData={this.handleLoadedData}
|
||||||
onProgress={this.handleProgress}
|
onProgress={this.handleProgress}
|
||||||
|
onVolumeChange={this.handleVolumeChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button type='button' className={classNames('video-player__spoiler', { active: !revealed })} onClick={this.toggleReveal}>
|
<button type='button' className={classNames('video-player__spoiler', { active: !revealed })} onClick={this.toggleReveal}>
|
||||||
@ -409,7 +417,7 @@ class Video extends React.PureComponent {
|
|||||||
<div className='video-player__buttons-bar'>
|
<div className='video-player__buttons-bar'>
|
||||||
<div className='video-player__buttons left'>
|
<div className='video-player__buttons left'>
|
||||||
<button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><i className={classNames('fa fa-fw', { 'fa-play': paused, 'fa-pause': !paused })} /></button>
|
<button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><i className={classNames('fa fa-fw', { 'fa-play': paused, 'fa-pause': !paused })} /></button>
|
||||||
<button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onMouseEnter={this.volumeSlider} onMouseLeave={this.volumeSlider} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button>
|
<button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button>
|
||||||
<div className='video-player__volume' onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
<div className='video-player__volume' onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
||||||
<div className='video-player__volume__current' style={{ width: `${volumeWidth}px` }} />
|
<div className='video-player__volume__current' style={{ width: `${volumeWidth}px` }} />
|
||||||
<span
|
<span
|
||||||
|
@ -845,6 +845,18 @@ $small-breakpoint: 960px;
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
display: inline;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: 700;
|
||||||
|
background: transparent;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
color: lighten($darker-text-color, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
.account {
|
.account {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -54,7 +54,7 @@ table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scrollbar-color: lighten($ui-base-color, 4%) transparent;
|
scrollbar-color: lighten($ui-base-color, 4%) rgba($base-overlay-background, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
@ -63,6 +63,7 @@ module Omniauthable
|
|||||||
{
|
{
|
||||||
email: email || "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
|
email: email || "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
|
||||||
password: Devise.friendly_token[0, 20],
|
password: Devise.friendly_token[0, 20],
|
||||||
|
agreement: true,
|
||||||
account_attributes: {
|
account_attributes: {
|
||||||
username: ensure_unique_username(auth.uid),
|
username: ensure_unique_username(auth.uid),
|
||||||
display_name: display_name,
|
display_name: display_name,
|
||||||
|
@ -295,6 +295,7 @@ class User < ApplicationRecord
|
|||||||
|
|
||||||
def self.pam_get_user(attributes = {})
|
def self.pam_get_user(attributes = {})
|
||||||
return nil unless attributes[:email]
|
return nil unless attributes[:email]
|
||||||
|
|
||||||
resource =
|
resource =
|
||||||
if Devise.check_at_sign && !attributes[:email].index('@')
|
if Devise.check_at_sign && !attributes[:email].index('@')
|
||||||
joins(:account).find_by(accounts: { username: attributes[:email] })
|
joins(:account).find_by(accounts: { username: attributes[:email] })
|
||||||
@ -304,6 +305,7 @@ class User < ApplicationRecord
|
|||||||
|
|
||||||
if resource.blank?
|
if resource.blank?
|
||||||
resource = new(email: attributes[:email], agreement: true)
|
resource = new(email: attributes[:email], agreement: true)
|
||||||
|
|
||||||
if Devise.check_at_sign && !resource[:email].index('@')
|
if Devise.check_at_sign && !resource[:email].index('@')
|
||||||
resource[:email] = Rpam2.getenv(resource.find_pam_service, attributes[:email], attributes[:password], 'email', false)
|
resource[:email] = Rpam2.getenv(resource.find_pam_service, attributes[:email], attributes[:password], 'email', false)
|
||||||
resource[:email] = "#{attributes[:email]}@#{resource.find_pam_suffix}" unless resource[:email]
|
resource[:email] = "#{attributes[:email]}@#{resource.find_pam_suffix}" unless resource[:email]
|
||||||
|
@ -66,7 +66,10 @@ class PostStatusService < BaseService
|
|||||||
end
|
end
|
||||||
|
|
||||||
def schedule_status!
|
def schedule_status!
|
||||||
if @account.statuses.build(status_attributes).valid?
|
status_for_validation = @account.statuses.build(status_attributes)
|
||||||
|
if status_for_validation.valid?
|
||||||
|
status_for_validation.destroy
|
||||||
|
|
||||||
# The following transaction block is needed to wrap the UPDATEs to
|
# The following transaction block is needed to wrap the UPDATEs to
|
||||||
# the media attachments when the scheduled status is created
|
# the media attachments when the scheduled status is created
|
||||||
|
|
||||||
@ -99,7 +102,7 @@ class PostStatusService < BaseService
|
|||||||
|
|
||||||
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.too_many') if @options[:media_ids].size > 4
|
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.too_many') if @options[:media_ids].size > 4
|
||||||
|
|
||||||
@media = MediaAttachment.where(status_id: nil).where(id: @options[:media_ids].take(4).map(&:to_i))
|
@media = @account.media_attachments.where(status_id: nil).where(id: @options[:media_ids].take(4).map(&:to_i))
|
||||||
|
|
||||||
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.images_and_video') if @media.size > 1 && @media.find(&:video?)
|
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.images_and_video') if @media.size > 1 && @media.find(&:video?)
|
||||||
end
|
end
|
||||||
|
@ -10,7 +10,7 @@ class VerifyLinkService < BaseService
|
|||||||
return unless link_back_present?
|
return unless link_back_present?
|
||||||
|
|
||||||
field.mark_verified!
|
field.mark_verified!
|
||||||
rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
|
rescue OpenSSL::SSL::SSLError, HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
|
||||||
Rails.logger.debug "Error fetching link #{@url}: #{e}"
|
Rails.logger.debug "Error fetching link #{@url}: #{e}"
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
@ -44,7 +44,7 @@ class CopyAccountStats < ActiveRecord::Migration[5.2]
|
|||||||
# uniqueness violations that we need to skip over
|
# uniqueness violations that we need to skip over
|
||||||
Account.unscoped.select('id, statuses_count, following_count, followers_count, created_at, updated_at').find_each do |account|
|
Account.unscoped.select('id, statuses_count, following_count, followers_count, created_at, updated_at').find_each do |account|
|
||||||
begin
|
begin
|
||||||
params = [[nil, account.id], [nil, account.statuses_count], [nil, account.following_count], [nil, account.followers_count], [nil, account.created_at], [nil, account.updated_at]]
|
params = [[nil, account.id], [nil, account[:statuses_count]], [nil, account[:following_count]], [nil, account[:followers_count]], [nil, account.created_at], [nil, account.updated_at]]
|
||||||
exec_insert('INSERT INTO account_stats (account_id, statuses_count, following_count, followers_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)', nil, params)
|
exec_insert('INSERT INTO account_stats (account_id, statuses_count, following_count, followers_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)', nil, params)
|
||||||
rescue ActiveRecord::RecordNotUnique
|
rescue ActiveRecord::RecordNotUnique
|
||||||
next
|
next
|
||||||
|
@ -889,7 +889,7 @@ table #{table}.
|
|||||||
If you are using PostgreSQL you can solve this by logging in to the GitLab
|
If you are using PostgreSQL you can solve this by logging in to the GitLab
|
||||||
database (#{dbname}) using a super user and running:
|
database (#{dbname}) using a super user and running:
|
||||||
|
|
||||||
ALTER #{user} WITH SUPERUSER
|
ALTER USER #{user} WITH SUPERUSER
|
||||||
|
|
||||||
For MySQL you instead need to run:
|
For MySQL you instead need to run:
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ module Mastodon
|
|||||||
end
|
end
|
||||||
|
|
||||||
def patch
|
def patch
|
||||||
0
|
1
|
||||||
end
|
end
|
||||||
|
|
||||||
def pre
|
def pre
|
||||||
|
@ -36,6 +36,20 @@ RSpec.describe PostStatusService, type: :service do
|
|||||||
expect(status.params['text']).to eq 'Hi future!'
|
expect(status.params['text']).to eq 'Hi future!'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'does not immediately create a status when scheduling a status' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
media = Fabricate(:media_attachment)
|
||||||
|
future = Time.now.utc + 2.hours
|
||||||
|
|
||||||
|
status = subject.call(account, text: 'Hi future!', media_ids: [media.id], scheduled_at: future)
|
||||||
|
|
||||||
|
expect(status).to be_a ScheduledStatus
|
||||||
|
expect(status.scheduled_at).to eq future
|
||||||
|
expect(status.params['text']).to eq 'Hi future!'
|
||||||
|
expect(media.reload.status).to be_nil
|
||||||
|
expect(Status.where(text: 'Hi future!').exists?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
it 'creates response to the original status of boost' do
|
it 'creates response to the original status of boost' do
|
||||||
boosted_status = Fabricate(:status)
|
boosted_status = Fabricate(:status)
|
||||||
in_reply_to_status = Fabricate(:status, reblog: boosted_status)
|
in_reply_to_status = Fabricate(:status, reblog: boosted_status)
|
||||||
@ -153,7 +167,7 @@ RSpec.describe PostStatusService, type: :service do
|
|||||||
|
|
||||||
it 'attaches the given media to the created status' do
|
it 'attaches the given media to the created status' do
|
||||||
account = Fabricate(:account)
|
account = Fabricate(:account)
|
||||||
media = Fabricate(:media_attachment)
|
media = Fabricate(:media_attachment, account: account)
|
||||||
|
|
||||||
status = subject.call(
|
status = subject.call(
|
||||||
account,
|
account,
|
||||||
@ -164,6 +178,19 @@ RSpec.describe PostStatusService, type: :service do
|
|||||||
expect(media.reload.status).to eq status
|
expect(media.reload.status).to eq status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'does not attach media from another account to the created status' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
media = Fabricate(:media_attachment, account: Fabricate(:account))
|
||||||
|
|
||||||
|
status = subject.call(
|
||||||
|
account,
|
||||||
|
text: "test status update",
|
||||||
|
media_ids: [media.id],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(media.reload.status).to eq nil
|
||||||
|
end
|
||||||
|
|
||||||
it 'does not allow attaching more than 4 files' do
|
it 'does not allow attaching more than 4 files' do
|
||||||
account = Fabricate(:account)
|
account = Fabricate(:account)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user