From 9110db41c53a2f3f22affc23b364362133997d3e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 4 Mar 2018 09:19:11 +0100 Subject: Federate pinned statuses over ActivityPub (#6610) * Federate pinned statuses over ActivityPub * Display pinned toots in web UI Fix #6117 * Fix migration * Fix tests * Update outbox_serializer.rb * Update remove_serializer.rb * Update add_serializer.rb * Update fetch_featured_collection_service.rb --- app/models/account.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'app/models') diff --git a/app/models/account.rb b/app/models/account.rb index 13692d0d7..c1347fe65 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -43,6 +43,7 @@ # protocol :integer default("ostatus"), not null # memorial :boolean default(FALSE), not null # moved_to_account_id :integer +# featured_collection_url :string # class Account < ApplicationRecord -- cgit From c110fa62ac0f475efd64572026835a7514c410ae Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 5 Mar 2018 04:28:24 +0900 Subject: Provide default OTP_SECRET value for development environment (#6617) --- .env.test | 1 - app/models/user.rb | 2 +- config/environments/development.rb | 2 ++ config/environments/production.rb | 2 ++ config/environments/test.rb | 2 ++ 5 files changed, 7 insertions(+), 2 deletions(-) (limited to 'app/models') diff --git a/.env.test b/.env.test index e25c040ac..b57f52e30 100644 --- a/.env.test +++ b/.env.test @@ -1,4 +1,3 @@ # Federation LOCAL_DOMAIN=cb6e6126.ngrok.io LOCAL_HTTPS=true -OTP_SECRET=100c7faeef00caa29242f6b04156742bf76065771fd4117990c4282b8748ff3d99f8fdae97c982ab5bd2e6756a159121377cce4421f4a8ecd2d67bd7749a3fb4 diff --git a/app/models/user.rb b/app/models/user.rb index 2995d6d54..b716c13fd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -44,7 +44,7 @@ class User < ApplicationRecord ACTIVE_DURATION = 14.days devise :two_factor_authenticatable, - otp_secret_encryption_key: ENV.fetch('OTP_SECRET') + otp_secret_encryption_key: Rails.configuration.x.otp_secret devise :two_factor_backupable, otp_number_of_backup_codes: 10 diff --git a/config/environments/development.rb b/config/environments/development.rb index 2da407c32..285fea8b8 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -82,6 +82,8 @@ Rails.application.configure do Bullet.add_whitelist type: :n_plus_one_query, class_name: 'User', association: :account end + + config.x.otp_secret = ENV.fetch('OTP_SECRET', '1fc2b87989afa6351912abeebe31ffc5c476ead9bf8b3d74cbc4a302c7b69a45b40b1bbef3506ddad73e942e15ed5ca4b402bf9a66423626051104f4b5f05109') end ActiveRecordQueryTrace.enabled = ENV.fetch('QUERY_TRACE_ENABLED') { false } diff --git a/config/environments/production.rb b/config/environments/production.rb index 51288bc39..3136a40fc 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -96,4 +96,6 @@ Rails.application.configure do 'X-Content-Type-Options' => 'nosniff', 'X-XSS-Protection' => '1; mode=block', } + + config.x.otp_secret = ENV.fetch('OTP_SECRET') end diff --git a/config/environments/test.rb b/config/environments/test.rb index 74e7fa694..7d77a170e 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -44,6 +44,8 @@ Rails.application.configure do # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr + config.x.otp_secret = '100c7faeef00caa29242f6b04156742bf76065771fd4117990c4282b8748ff3d99f8fdae97c982ab5bd2e6756a159121377cce4421f4a8ecd2d67bd7749a3fb4' + # Generate random VAPID keys vapid_key = Webpush.generate_key config.x.vapid_private_key = vapid_key.private_key -- cgit From 78d772af862c536b2e985977b6ba549efe668fe0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 6 Mar 2018 06:29:01 +0100 Subject: Fix #3807: Increase avatars to 400x400 max (#6651) But do not upscale when they are smaller --- app/models/concerns/account_avatar.rb | 4 ++-- lib/paperclip/lazy_thumbnail.rb | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'app/models') diff --git a/app/models/concerns/account_avatar.rb b/app/models/concerns/account_avatar.rb index 7712a29fd..9e34a9461 100644 --- a/app/models/concerns/account_avatar.rb +++ b/app/models/concerns/account_avatar.rb @@ -7,8 +7,8 @@ module AccountAvatar class_methods do def avatar_styles(file) - styles = { original: { geometry: '120x120#', file_geometry_parser: FastGeometryParser } } - styles[:static] = { geometry: '120x120#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif' + styles = { original: { geometry: '400x400#', file_geometry_parser: FastGeometryParser } } + styles[:static] = { geometry: '400x400#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif' styles end diff --git a/lib/paperclip/lazy_thumbnail.rb b/lib/paperclip/lazy_thumbnail.rb index 42f9a557a..aafa21343 100644 --- a/lib/paperclip/lazy_thumbnail.rb +++ b/lib/paperclip/lazy_thumbnail.rb @@ -4,6 +4,10 @@ module Paperclip class LazyThumbnail < Paperclip::Thumbnail def make return File.open(@file.path) unless needs_convert? + + min_side = [@current_geometry.width, @current_geometry.height].min + options[:geometry] = "#{min_side.to_i}x#{min_side.to_i}#" if @target_geometry.square? && min_side < @target_geometry.width + Paperclip::Thumbnail.make(file, options, attachment) end -- cgit From dd9d00d293c127fd84183e4fc30690aea4913e02 Mon Sep 17 00:00:00 2001 From: Effy Elden Date: Wed, 7 Mar 2018 16:19:10 +1100 Subject: Add additional first_name and last_name SAML attribute statement options, and modify Omniauthable concern to use full_name or first_name + last_name if not available (#6669) --- .env.production.sample | 4 +++- app/models/concerns/omniauthable.rb | 3 ++- config/initializers/omniauth.rb | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'app/models') diff --git a/.env.production.sample b/.env.production.sample index b96754e78..587b56757 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -204,7 +204,9 @@ STREAMING_CLUSTER_NUM=1 # SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true # SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1" # SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" -# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.5.4.42" +# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241" +# SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42" +# SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4" # SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1" # SAML_ATTRIBUTES_STATEMENTS_VERIFIED= # SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL= diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb index 87d93c1fd..50288e700 100644 --- a/app/models/concerns/omniauthable.rb +++ b/app/models/concerns/omniauthable.rb @@ -58,13 +58,14 @@ module Omniauthable email_is_verified = auth.info.verified || auth.info.verified_email || assume_verified email = auth.info.verified_email || auth.info.email email = email_is_verified && !User.exists?(email: auth.info.email) && email + display_name = auth.info.full_name || [auth.info.first_name, auth.info.last_name].join(' ') { email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com", password: Devise.friendly_token[0, 20], account_attributes: { username: ensure_unique_username(auth.uid), - display_name: [auth.info.first_name, auth.info.last_name].join(' '), + display_name: display_name, }, } end diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 92a73d82a..85fb81250 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -55,6 +55,8 @@ Devise.setup do |config| saml_options[:attribute_statements][:uid] = [ENV['SAML_ATTRIBUTES_STATEMENTS_UID']] if ENV['SAML_ATTRIBUTES_STATEMENTS_UID'] saml_options[:attribute_statements][:email] = [ENV['SAML_ATTRIBUTES_STATEMENTS_EMAIL']] if ENV['SAML_ATTRIBUTES_STATEMENTS_EMAIL'] saml_options[:attribute_statements][:full_name] = [ENV['SAML_ATTRIBUTES_STATEMENTS_FULL_NAME']] if ENV['SAML_ATTRIBUTES_STATEMENTS_FULL_NAME'] + saml_options[:attribute_statements][:first_name] = [ENV['SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME']] if ENV['SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME'] + saml_options[:attribute_statements][:last_name] = [ENV['SAML_ATTRIBUTES_STATEMENTS_LAST_NAME']] if ENV['SAML_ATTRIBUTES_STATEMENTS_LAST_NAME'] saml_options[:attribute_statements][:verified] = [ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED']] if ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED'] saml_options[:attribute_statements][:verified_email] = [ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL']] if ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL'] saml_options[:uid_attribute] = ENV['SAML_UID_ATTRIBUTE'] if ENV['SAML_UID_ATTRIBUTE'] -- cgit From cfa9b6e13ab3c434f3901df6f614d0aa94a3d1ed Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 7 Mar 2018 08:28:52 +0100 Subject: Remove text requirement when media attached from statuses (#6672) --- app/javascript/mastodon/actions/compose.js | 5 +++-- app/javascript/mastodon/components/status_content.js | 11 ++++++++++- .../mastodon/features/compose/components/compose_form.js | 7 ++++--- .../features/compose/containers/compose_form_container.js | 1 + app/javascript/mastodon/reducers/compose.js | 3 --- app/lib/formatter.rb | 2 ++ app/models/status.rb | 8 ++++++-- app/services/post_status_service.rb | 9 ++------- 8 files changed, 28 insertions(+), 18 deletions(-) (limited to 'app/models') diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 0fe480022..130b4af23 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -96,8 +96,9 @@ export function mentionCompose(account, router) { export function submitCompose() { return function (dispatch, getState) { const status = getState().getIn(['compose', 'text'], ''); + const media = getState().getIn(['compose', 'media_attachments']); - if (!status || !status.length) { + if ((!status || !status.length) && media.size === 0) { return; } @@ -106,7 +107,7 @@ export function submitCompose() { api(getState).post('/api/v1/statuses', { status, in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), - media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')), + media_ids: media.map(item => item.get('id')), sensitive: getState().getIn(['compose', 'sensitive']), spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''), visibility: getState().getIn(['compose', 'privacy']), diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index 3b8155632..701b5702c 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -24,7 +24,12 @@ export default class StatusContent extends React.PureComponent { }; _updateStatusLinks () { - const node = this.node; + const node = this.node; + + if (!node) { + return; + } + const links = node.querySelectorAll('a'); for (var i = 0; i < links.length; ++i) { @@ -115,6 +120,10 @@ export default class StatusContent extends React.PureComponent { render () { const { status } = this.props; + if (status.get('content').length === 0) { + return null; + } + const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; const content = { __html: status.get('contentHtml') }; diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index a876c5197..663ccfb8e 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -50,6 +50,7 @@ export default class ComposeForm extends ImmutablePureComponent { onPaste: PropTypes.func.isRequired, onPickEmoji: PropTypes.func.isRequired, showSearch: PropTypes.bool, + anyMedia: PropTypes.bool, }; static defaultProps = { @@ -142,10 +143,10 @@ export default class ComposeForm extends ImmutablePureComponent { } render () { - const { intl, onPaste, showSearch } = this.props; + const { intl, onPaste, showSearch, anyMedia } = this.props; const disabled = this.props.is_submitting; const text = [this.props.spoiler_text, countableText(this.props.text)].join(''); - + const disabledButton = disabled || this.props.is_uploading || length(text) > 500 || (text.length !== 0 && text.trim().length === 0 && !anyMedia); let publishText = ''; if (this.props.privacy === 'private' || this.props.privacy === 'direct') { @@ -203,7 +204,7 @@ export default class ComposeForm extends ImmutablePureComponent {
-
+
); diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js index 5f5509dbe..ede23d361 100644 --- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js +++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js @@ -23,6 +23,7 @@ const mapStateToProps = state => ({ is_submitting: state.getIn(['compose', 'is_submitting']), is_uploading: state.getIn(['compose', 'is_uploading']), showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']), + anyMedia: state.getIn(['compose', 'media_attachments']).size > 0, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index dc88390df..532f4b2a7 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -90,7 +90,6 @@ function appendMedia(state, media) { map.update('media_attachments', list => list.push(media)); map.set('is_uploading', false); map.set('resetFileKey', Math.floor((Math.random() * 0x10000))); - map.update('text', oldText => `${oldText.trim()} ${media.get('text_url')}`); map.set('focusDate', new Date()); map.set('idempotencyKey', uuid()); @@ -101,12 +100,10 @@ function appendMedia(state, media) { }; function removeMedia(state, mediaId) { - const media = state.get('media_attachments').find(item => item.get('id') === mediaId); const prevSize = state.get('media_attachments').size; return state.withMutations(map => { map.update('media_attachments', list => list.filterNot(item => item.get('id') === mediaId)); - map.update('text', text => text.replace(media.get('text_url'), '').trim()); map.set('idempotencyKey', uuid()); if (prevSize === 1) { diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 8c0f8cebc..1df4ff8d4 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -19,6 +19,8 @@ class Formatter raw_content = status.text + return '' if raw_content.blank? + unless status.local? html = reformat(raw_content) html = encode_custom_emojis(html, status.emojis) if options[:custom_emojify] diff --git a/app/models/status.rb b/app/models/status.rb index f806a59fc..60fa7a22e 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -57,7 +57,7 @@ class Status < ApplicationRecord has_one :stream_entry, as: :activity, inverse_of: :status validates :uri, uniqueness: true, presence: true, unless: :local? - validates :text, presence: true, unless: :reblog? + validates :text, presence: true, unless: -> { with_media? || reblog? } validates_with StatusLengthValidator validates :reblog, uniqueness: { scope: :account }, if: :reblog? @@ -150,8 +150,12 @@ class Status < ApplicationRecord private_visibility? || direct_visibility? end + def with_media? + media_attachments.any? + end + def non_sensitive_with_media? - !sensitive? && media_attachments.any? + !sensitive? && with_media? end def emojis diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 92d868afe..df38d16a6 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -21,17 +21,17 @@ class PostStatusService < BaseService media = validate_media!(options[:media_ids]) status = nil + text = options.delete(:spoiler_text) if text.blank? && options[:spoiler_text].present? ApplicationRecord.transaction do status = account.statuses.create!(text: text, + media_attachments: media || [], thread: in_reply_to, sensitive: options[:sensitive], spoiler_text: options[:spoiler_text] || '', visibility: options[:visibility] || account.user&.setting_default_privacy, language: LanguageDetector.instance.detect(text, account), application: options[:application]) - - attach_media(status, media) end process_mentions_service.call(status) @@ -64,11 +64,6 @@ class PostStatusService < BaseService media end - def attach_media(status, media) - return if media.nil? - media.update(status_id: status.id) - end - def process_mentions_service ProcessMentionsService.new end -- cgit From 9aba44ea79c4c1bd21e9232b65f3cf1a9736c6cc Mon Sep 17 00:00:00 2001 From: "Renato \"Lond\" Cerqueira" Date: Thu, 8 Mar 2018 02:25:10 +0100 Subject: Rescue when there's no extension in the remotable (#6358) * Rescue when there's no extension in the remotable Sometimes the remotable is pointing to a directory with no file extension. Maybe it should not be expecting to identify based on extensions to begin with, but since it's the case, it should be ready for it. * Fix codeclimate issue * Check if filename is nil instead of rescueing exception Suggestion made in the PR * Avoid concatenation issue if filename is nil If filename is nil, extname was undefined * Invert condition Address PR comments --- app/models/concerns/remotable.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'app/models') diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb index 990035b34..020303a2f 100644 --- a/app/models/concerns/remotable.rb +++ b/app/models/concerns/remotable.rb @@ -28,7 +28,11 @@ module Remotable matches = response.headers['content-disposition']&.match(/filename="([^"]*)"/) filename = matches.nil? ? parsed_url.path.split('/').last : matches[1] basename = SecureRandom.hex(8) - extname = File.extname(filename) + extname = if filename.nil? + '' + else + File.extname(filename) + end send("#{attachment_name}=", StringIO.new(response.to_s)) send("#{attachment_name}_file_name=", basename + extname) -- cgit