diff options
Diffstat (limited to 'app')
26 files changed, 200 insertions, 66 deletions
diff --git a/app/assets/javascripts/components/actions/compose.jsx b/app/assets/javascripts/components/actions/compose.jsx index b97cb7b12..c2a7909f6 100644 --- a/app/assets/javascripts/components/actions/compose.jsx +++ b/app/assets/javascripts/components/actions/compose.jsx @@ -23,6 +23,7 @@ export const COMPOSE_MOUNT = 'COMPOSE_MOUNT'; export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT'; export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE'; +export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE'; export function changeCompose(text) { return { @@ -65,7 +66,8 @@ export function submitCompose() { status: getState().getIn(['compose', 'text'], ''), in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')), - sensitive: getState().getIn(['compose', 'sensitive']) + sensitive: getState().getIn(['compose', 'sensitive']), + unlisted: getState().getIn(['compose', 'unlisted']) }).then(function (response) { dispatch(submitComposeSuccess(response.data)); dispatch(updateTimeline('home', response.data)); @@ -207,3 +209,10 @@ export function changeComposeSensitivity(checked) { checked }; }; + +export function changeComposeVisibility(checked) { + return { + type: COMPOSE_VISIBILITY_CHANGE, + checked + }; +}; diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index b16731c05..4688f39d3 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -70,6 +70,7 @@ const ComposeForm = React.createClass({ suggestion_token: React.PropTypes.string, suggestions: React.PropTypes.array, sensitive: React.PropTypes.bool, + unlisted: React.PropTypes.bool, is_submitting: React.PropTypes.bool, is_uploading: React.PropTypes.bool, in_reply_to: ImmutablePropTypes.map, @@ -79,7 +80,8 @@ const ComposeForm = React.createClass({ onClearSuggestions: React.PropTypes.func.isRequired, onFetchSuggestions: React.PropTypes.func.isRequired, onSuggestionSelected: React.PropTypes.func.isRequired, - onChangeSensitivity: React.PropTypes.func.isRequired + onChangeSensitivity: React.PropTypes.func.isRequired, + onChangeVisibility: React.PropTypes.func.isRequired }, mixins: [PureRenderMixin], @@ -147,6 +149,10 @@ const ComposeForm = React.createClass({ this.props.onChangeSensitivity(e.target.checked); }, + handleChangeVisibility (e) { + this.props.onChangeVisibility(e.target.checked); + }, + render () { const { intl } = this.props; let replyArea = ''; @@ -187,7 +193,12 @@ const ComposeForm = React.createClass({ <UploadButtonContainer style={{ paddingTop: '4px' }} /> </div> - <label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle', marginTop: '10px', borderTop: '1px solid #616b86', paddingTop: '10px' }}> + <label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle', marginTop: '10px', borderTop: '1px solid #282c37', paddingTop: '10px' }}> + <Toggle checked={this.props.unlisted} onChange={this.handleChangeVisibility} /> + <span style={{ display: 'inline-block', verticalAlign: 'middle', marginBottom: '14px', marginLeft: '8px', color: '#9baec8' }}><FormattedMessage id='compose_form.unlisted' defaultMessage='Unlisted mode' /></span> + </label> + + <label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle' }}> <Toggle checked={this.props.sensitive} onChange={this.handleChangeSensitivity} /> <span style={{ display: 'inline-block', verticalAlign: 'middle', marginBottom: '14px', marginLeft: '8px', color: '#9baec8' }}><FormattedMessage id='compose_form.sensitive' defaultMessage='Mark content as sensitive' /></span> </label> diff --git a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx b/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx index 9897f6505..8aa719476 100644 --- a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx +++ b/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx @@ -7,7 +7,8 @@ import { clearComposeSuggestions, fetchComposeSuggestions, selectComposeSuggestion, - changeComposeSensitivity + changeComposeSensitivity, + changeComposeVisibility } from '../../../actions/compose'; import { makeGetStatus } from '../../../selectors'; @@ -20,6 +21,7 @@ const makeMapStateToProps = () => { suggestion_token: state.getIn(['compose', 'suggestion_token']), suggestions: state.getIn(['compose', 'suggestions']).toJS(), sensitive: state.getIn(['compose', 'sensitive']), + unlisted: state.getIn(['compose', 'unlisted']), is_submitting: state.getIn(['compose', 'is_submitting']), is_uploading: state.getIn(['compose', 'is_uploading']), in_reply_to: getStatus(state, state.getIn(['compose', 'in_reply_to'])) @@ -57,6 +59,10 @@ const mapDispatchToProps = function (dispatch) { onChangeSensitivity (checked) { dispatch(changeComposeSensitivity(checked)); + }, + + onChangeVisibility (checked) { + dispatch(changeComposeVisibility(checked)); } } }; diff --git a/app/assets/javascripts/components/locales/de.jsx b/app/assets/javascripts/components/locales/de.jsx index 4e2a70edb..17b74e15d 100644 --- a/app/assets/javascripts/components/locales/de.jsx +++ b/app/assets/javascripts/components/locales/de.jsx @@ -34,6 +34,8 @@ const en = { "tabs_bar.notifications": "Mitteilungen", "compose_form.placeholder": "Worüber möchstest du schreiben?", "compose_form.publish": "Veröffentlichen", + "compose_form.sensitive": "Medien als sensitiv markieren", + "compose_form.unlisted": "Öffentlich nicht auflisten", "navigation_bar.settings": "Einstellungen", "navigation_bar.public_timeline": "Öffentlich", "navigation_bar.logout": "Abmelden", diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx index 41a44e3dc..b17e623cb 100644 --- a/app/assets/javascripts/components/locales/en.jsx +++ b/app/assets/javascripts/components/locales/en.jsx @@ -38,6 +38,7 @@ const en = { "compose_form.placeholder": "What is on your mind?", "compose_form.publish": "Toot", "compose_form.sensitive": "Mark content as sensitive", + "compose_form.unlisted": "Unlisted mode", "navigation_bar.settings": "Settings", "navigation_bar.public_timeline": "Public timeline", "navigation_bar.logout": "Logout", diff --git a/app/assets/javascripts/components/locales/es.jsx b/app/assets/javascripts/components/locales/es.jsx index d4434bba7..d6e61fd9e 100644 --- a/app/assets/javascripts/components/locales/es.jsx +++ b/app/assets/javascripts/components/locales/es.jsx @@ -35,6 +35,8 @@ const es = { "tabs_bar.notifications": "Notificaciones", "compose_form.placeholder": "¿En qué estás pensando?", "compose_form.publish": "Publicar", + "compose_form.sensitive": null, + "compose_form.unlisted": "No listado", "navigation_bar.settings": "Ajustes", "navigation_bar.public_timeline": "Público", "navigation_bar.logout": "Cerrar sesión", diff --git a/app/assets/javascripts/components/locales/fr.jsx b/app/assets/javascripts/components/locales/fr.jsx index c4458a145..968c3f8c3 100644 --- a/app/assets/javascripts/components/locales/fr.jsx +++ b/app/assets/javascripts/components/locales/fr.jsx @@ -36,7 +36,8 @@ const fr = { "tabs_bar.notifications": "Notifications", "compose_form.placeholder": "Qu’avez-vous en tête ?", "compose_form.publish": "Pouet", - "compose_form.sensitive": "Marquer le contenu comme délicat", + "compose_form.sensitive": "Marquer le contenu comme délicat", + "compose_form.unlisted": "Ne pas apparaître dans le fil public", "navigation_bar.settings": "Paramètres", "navigation_bar.public_timeline": "Public", "navigation_bar.logout": "Déconnexion", diff --git a/app/assets/javascripts/components/locales/hu.jsx b/app/assets/javascripts/components/locales/hu.jsx index 4a446965c..606fc830f 100644 --- a/app/assets/javascripts/components/locales/hu.jsx +++ b/app/assets/javascripts/components/locales/hu.jsx @@ -37,6 +37,7 @@ const hu = { "compose_form.placeholder": "Mire gondolsz?", "compose_form.publish": "Tülk!", "compose_form.sensitive": "Tartalom érzékenynek jelölése", + "compose_form.unlisted": "Listázatlan mód", "navigation_bar.settings": "Beállítások", "navigation_bar.public_timeline": "Nyilvános időfolyam", "navigation_bar.logout": "Kijelentkezés", diff --git a/app/assets/javascripts/components/locales/pt.jsx b/app/assets/javascripts/components/locales/pt.jsx index e67bd80ac..0fba15f2c 100644 --- a/app/assets/javascripts/components/locales/pt.jsx +++ b/app/assets/javascripts/components/locales/pt.jsx @@ -33,6 +33,8 @@ const pt = { "tabs_bar.public": "Público", "compose_form.placeholder": "Que estás pensando?", "compose_form.publish": "Publicar", + "compose_form.sensitive": null, + "compose_form.unlisted": null, "navigation_bar.settings": "Configurações", "navigation_bar.public_timeline": "Timeline Pública", "navigation_bar.logout": "Logout", diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/assets/javascripts/components/reducers/compose.jsx index 4abc3e6aa..9d1d53083 100644 --- a/app/assets/javascripts/components/reducers/compose.jsx +++ b/app/assets/javascripts/components/reducers/compose.jsx @@ -16,7 +16,8 @@ import { COMPOSE_SUGGESTIONS_CLEAR, COMPOSE_SUGGESTIONS_READY, COMPOSE_SUGGESTION_SELECT, - COMPOSE_SENSITIVITY_CHANGE + COMPOSE_SENSITIVITY_CHANGE, + COMPOSE_VISIBILITY_CHANGE } from '../actions/compose'; import { TIMELINE_DELETE } from '../actions/timelines'; import { ACCOUNT_SET_SELF } from '../actions/accounts'; @@ -25,6 +26,7 @@ import Immutable from 'immutable'; const initialState = Immutable.Map({ mounted: false, sensitive: false, + unlisted: false, text: '', in_reply_to: null, is_submitting: false, @@ -91,6 +93,8 @@ export default function compose(state = initialState, action) { return state.set('mounted', false); case COMPOSE_SENSITIVITY_CHANGE: return state.set('sensitive', action.checked); + case COMPOSE_VISIBILITY_CHANGE: + return state.set('unlisted', action.checked); case COMPOSE_CHANGE: return state.set('text', action.text); case COMPOSE_REPLY: diff --git a/app/controllers/api/oembed_controller.rb b/app/controllers/api/oembed_controller.rb new file mode 100644 index 000000000..4a591dc22 --- /dev/null +++ b/app/controllers/api/oembed_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Api::OembedController < ApiController + respond_to :json + + def show + @stream_entry = stream_entry_from_url(params[:url]) + @width = [300, params[:maxwidth].to_i].min + @height = [200, params[:maxheight].to_i].min + end + + private + + def stream_entry_from_url(url) + params = Rails.application.routes.recognize_path(url) + + raise ActiveRecord::NotFound unless params[:controller] == 'stream_entries' && params[:action] == 'show' + + StreamEntry.find(params[:id]) + end +end diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index a0b15cfbc..8b7690850 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -52,7 +52,7 @@ class Api::V1::StatusesController < ApiController end def create - @status = PostStatusService.new.call(current_user.account, params[:status], params[:in_reply_to_id].blank? ? nil : Status.find(params[:in_reply_to_id]), media_ids: params[:media_ids], sensitive: params[:sensitive]) + @status = PostStatusService.new.call(current_user.account, params[:status], params[:in_reply_to_id].blank? ? nil : Status.find(params[:in_reply_to_id]), media_ids: params[:media_ids], sensitive: params[:sensitive], unlisted: params[:unlisted]) render action: :show end @@ -73,7 +73,7 @@ class Api::V1::StatusesController < ApiController @reblogged_map = { @status.id => false } RemovalWorker.perform_async(reblog.id) - + render action: :show end diff --git a/app/models/concerns/obfuscate_filename.rb b/app/controllers/concerns/obfuscate_filename.rb index dc25cdbc2..9f12cb7e9 100644 --- a/app/models/concerns/obfuscate_filename.rb +++ b/app/controllers/concerns/obfuscate_filename.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true module ObfuscateFilename extend ActiveSupport::Concern @@ -11,6 +12,6 @@ module ObfuscateFilename file = params.dig(*path) return if file.nil? - file.original_filename = "media" + File.extname(file.original_filename) + file.original_filename = 'media' + File.extname(file.original_filename) end end diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 21fbba2af..0276f5fed 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -24,7 +24,7 @@ class Settings::ProfilesController < ApplicationController private def account_params - params.require(:account).permit(:display_name, :note, :avatar, :header, :silenced) + params.require(:account).permit(:display_name, :note, :avatar, :header) end def set_account diff --git a/app/helpers/api/oembed_helper.rb b/app/helpers/api/oembed_helper.rb new file mode 100644 index 000000000..05d5ca216 --- /dev/null +++ b/app/helpers/api/oembed_helper.rb @@ -0,0 +1,2 @@ +module Api::OembedHelper +end diff --git a/app/helpers/atom_builder_helper.rb b/app/helpers/atom_builder_helper.rb index 13faaa261..40bbe0491 100644 --- a/app/helpers/atom_builder_helper.rb +++ b/app/helpers/atom_builder_helper.rb @@ -38,7 +38,7 @@ module AtomBuilderHelper end def verb(xml, verb) - xml['activity'].send('verb', "http://activitystrea.ms/schema/1.0/#{verb}") + xml['activity'].send('verb', TagManager::VERBS[verb]) end def content(xml, content) @@ -62,7 +62,7 @@ module AtomBuilderHelper end def object_type(xml, type) - xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{type}") + xml['activity'].send('object-type', TagManager::TYPES[type]) end def uri(xml, uri) @@ -108,7 +108,7 @@ module AtomBuilderHelper end def link_mention(xml, account) - xml.link(rel: 'mentioned', href: TagManager.instance.uri_for(account)) + xml.link(:rel => 'mentioned', :href => TagManager.instance.uri_for(account), 'ostatus:object-type' => TagManager::TYPES[:person]) end def link_enclosure(xml, media) @@ -139,6 +139,11 @@ module AtomBuilderHelper end end + def link_visibility(xml, item) + return unless item.respond_to?(:visibility) && item.public_visibility? + xml.link(:rel => 'mentioned', :href => TagManager::COLLECTIONS[:public], 'ostatus:object-type' => TagManager::TYPES[:collection]) + end + def include_author(xml, account) object_type xml, :person uri xml, TagManager.instance.uri_for(account) @@ -189,6 +194,8 @@ module AtomBuilderHelper include_author xml, stream_entry.target.account end + link_visibility xml, stream_entry.target + stream_entry.target.mentions.each do |mention| link_mention xml, mention.account end @@ -204,25 +211,34 @@ module AtomBuilderHelper end end + link_visibility xml, stream_entry.activity + stream_entry.mentions.each do |mentioned| link_mention xml, mentioned end - if stream_entry.activity.is_a?(Status) - stream_entry.activity.media_attachments.each do |media| - link_enclosure xml, media - end + return unless stream_entry.activity.is_a?(Status) - stream_entry.activity.tags.each do |tag| - category xml, tag - end + stream_entry.activity.media_attachments.each do |media| + link_enclosure xml, media + end + + stream_entry.activity.tags.each do |tag| + category xml, tag end end private def root_tag(xml, tag, &block) - xml.send(tag, { :xmlns => 'http://www.w3.org/2005/Atom', 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:media' => 'http://purl.org/syndication/atommedia' }, &block) + xml.send(tag, { + 'xmlns' => TagManager::XMLNS, + 'xmlns:thr' => TagManager::THR_XMLNS, + 'xmlns:activity' => TagManager::AS_XMLNS, + 'xmlns:poco' => TagManager::POCO_XMLNS, + 'xmlns:media' => TagManager::MEDIA_XMLNS, + 'xmlns:ostatus' => TagManager::OS_XMLNS, + }, &block) end def single_link_avatar(xml, account, size, px) diff --git a/app/lib/tag_manager.rb b/app/lib/tag_manager.rb index fcc4b3259..bd4cbbbb0 100644 --- a/app/lib/tag_manager.rb +++ b/app/lib/tag_manager.rb @@ -6,6 +6,37 @@ class TagManager include Singleton include RoutingHelper + VERBS = { + post: 'http://activitystrea.ms/schema/1.0/post', + share: 'http://activitystrea.ms/schema/1.0/share', + favorite: 'http://activitystrea.ms/schema/1.0/favorite', + unfavorite: 'http://activitystrea.ms/schema/1.0/unfavorite', + delete: 'delete', + follow: 'http://activitystrea.ms/schema/1.0/follow', + unfollow: 'http://ostatus.org/schema/1.0/unfollow', + }.freeze + + TYPES = { + activity: 'http://activitystrea.ms/schema/1.0/activity', + note: 'http://activitystrea.ms/schema/1.0/note', + comment: 'http://activitystrea.ms/schema/1.0/comment', + person: 'http://activitystrea.ms/schema/1.0/person', + collection: 'http://activitystrea.ms/schema/1.0/collection', + group: 'http://activitystrea.ms/schema/1.0/group', + }.freeze + + COLLECTIONS = { + public: 'http://activityschema.org/collection/public', + }.freeze + + XMLNS = 'http://www.w3.org/2005/Atom' + MEDIA_XMLNS = 'http://purl.org/syndication/atommedia' + AS_XMLNS = 'http://activitystrea.ms/spec/1.0/' + THR_XMLNS = 'http://purl.org/syndication/thread/1.0' + POCO_XMLNS = 'http://portablecontacts.net/spec/1.0' + DFRN_XMLNS = 'http://purl.org/macgirvin/dfrn/1.0' + OS_XMLNS = 'http://ostatus.org/schema/1.0' + def unique_tag(date, id, type) "tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" end diff --git a/app/models/status.rb b/app/models/status.rb index 8f65a3ecc..87d8249b1 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -5,6 +5,8 @@ class Status < ApplicationRecord include Streamable include Cacheable + enum visibility: [:public, :unlisted], _suffix: :visibility + belongs_to :account, inverse_of: :statuses belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :replies @@ -100,6 +102,7 @@ class Status < ApplicationRecord def as_public_timeline(account = nil) query = joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id') + .where(visibility: :public) .where('accounts.silenced = FALSE') .where('statuses.in_reply_to_id IS NULL') .where('statuses.reblog_of_id IS NULL') @@ -110,6 +113,7 @@ class Status < ApplicationRecord def as_tag_timeline(tag, account = nil) query = tag.statuses .joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id') + .where(visibility: :public) .where('accounts.silenced = FALSE') .where('statuses.in_reply_to_id IS NULL') .where('statuses.reblog_of_id IS NULL') diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 40d8a0fee..ea9588bec 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -8,7 +8,7 @@ class FanOutOnWriteService < BaseService deliver_to_followers(status) deliver_to_mentioned(status) - return if status.account.silenced? + return if status.account.silenced? || !status.public_visibility? deliver_to_hashtags(status) deliver_to_public(status) diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 979a157e9..9e0ced129 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -10,7 +10,7 @@ class PostStatusService < BaseService # @option [Enumerable] :media_ids Optional array of media IDs to attach # @return [Status] def call(account, text, in_reply_to = nil, options = {}) - status = account.statuses.create!(text: text, thread: in_reply_to, sensitive: options[:sensitive]) + status = account.statuses.create!(text: text, thread: in_reply_to, sensitive: options[:sensitive], visibility: options[:unlisted] ? :unlisted : :public) attach_media(status, options[:media_ids]) process_mentions_service.call(status) process_hashtags_service.call(status) diff --git a/app/services/process_feed_service.rb b/app/services/process_feed_service.rb index a7a4cb2b0..3aa0ceff8 100644 --- a/app/services/process_feed_service.rb +++ b/app/services/process_feed_service.rb @@ -1,9 +1,6 @@ # frozen_string_literal: true class ProcessFeedService < BaseService - ACTIVITY_NS = 'http://activitystrea.ms/spec/1.0/' - THREAD_NS = 'http://purl.org/syndication/thread/1.0' - def call(body, account) xml = Nokogiri::XML(body) xml.encoding = 'utf-8' @@ -15,12 +12,12 @@ class ProcessFeedService < BaseService private def update_author(xml, account) - return if xml.at_xpath('/xmlns:feed').nil? - UpdateRemoteProfileService.new.call(xml.at_xpath('/xmlns:feed'), account, true) + return if xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS).nil? + UpdateRemoteProfileService.new.call(xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS), account, true) end def process_entries(xml, account) - xml.xpath('//xmlns:entry').reverse_each.map { |entry| ProcessEntry.new.call(entry, account) }.compact + xml.xpath('//xmlns:entry', xmlns: TagManager::XMLNS).reverse_each.map { |entry| ProcessEntry.new.call(entry, account) }.compact end class ProcessEntry @@ -48,7 +45,7 @@ class ProcessFeedService < BaseService status = status_from_xml(@xml) if verb == :share - original_status = status_from_xml(@xml.at_xpath('.//activity:object', activity: ACTIVITY_NS)) + original_status = status_from_xml(@xml.at_xpath('.//activity:object', activity: TagManager::AS_XMLNS)) status.reblog = original_status if original_status.nil? @@ -138,9 +135,15 @@ class ProcessFeedService < BaseService def mentions_from_xml(parent, xml) processed_account_ids = [] + public_visibility = false - xml.xpath('./xmlns:link[@rel="mentioned"]').each do |link| - next if link['href'] == 'http://activityschema.org/collection/public' + xml.xpath('./xmlns:link[@rel="mentioned"]', xmlns: TagManager::XMLNS).each do |link| + if link['ostatus:object-type'] == TagManager::TYPES[:collection] && link['href'] == TagManager::COLLECTIONS[:public] + public_visibility = true + next + elsif link['ostatus:object-type'] == TagManager::TYPES[:group] + next + end url = Addressable::URI.parse(link['href']) @@ -160,15 +163,18 @@ class ProcessFeedService < BaseService # So we can skip duplicate mentions processed_account_ids << mentioned_account.id end + + parent.visibility = public_visibility ? :public : :unlisted + parent.save! end def hashtags_from_xml(parent, xml) - tags = xml.xpath('./xmlns:category').map { |category| category['term'] }.select { |t| !t.blank? } + tags = xml.xpath('./xmlns:category', xmlns: TagManager::XMLNS).map { |category| category['term'] }.select { |t| !t.blank? } ProcessHashtagsService.new.call(parent, tags) end def media_from_xml(parent, xml) - xml.xpath('./xmlns:link[@rel="enclosure"]').each do |link| + xml.xpath('./xmlns:link[@rel="enclosure"]', xmlns: TagManager::XMLNS).each do |link| next unless link['href'] media = MediaAttachment.where(status: parent, remote_url: link['href']).first_or_initialize(account: parent.account, status: parent, remote_url: link['href']) @@ -183,52 +189,52 @@ class ProcessFeedService < BaseService end def id(xml = @xml) - xml.at_xpath('./xmlns:id').content + xml.at_xpath('./xmlns:id', xmlns: TagManager::XMLNS).content end def verb(xml = @xml) - raw = xml.at_xpath('./activity:verb', activity: ACTIVITY_NS).content - raw.gsub('http://activitystrea.ms/schema/1.0/', '').gsub('http://ostatus.org/schema/1.0/', '').to_sym + raw = xml.at_xpath('./activity:verb', activity: TagManager::AS_XMLNS).content + TagManager::VERBS.key(raw) rescue :post end def type(xml = @xml) - raw = xml.at_xpath('./activity:object-type', activity: ACTIVITY_NS).content - raw.gsub('http://activitystrea.ms/schema/1.0/', '').gsub('http://ostatus.org/schema/1.0/', '').to_sym + raw = xml.at_xpath('./activity:object-type', activity: TagManager::AS_XMLNS).content + TagManager::TYPES.key(raw) rescue :activity end def url(xml = @xml) - link = xml.at_xpath('./xmlns:link[@rel="alternate"]') + link = xml.at_xpath('./xmlns:link[@rel="alternate"]', xmlns: TagManager::XMLNS) link.nil? ? nil : link['href'] end def content(xml = @xml) - xml.at_xpath('./xmlns:content').content + xml.at_xpath('./xmlns:content', xmlns: TagManager::XMLNS).content end def published(xml = @xml) - xml.at_xpath('./xmlns:published').content + xml.at_xpath('./xmlns:published', xmlns: TagManager::XMLNS).content end def thread?(xml = @xml) - !xml.at_xpath('./thr:in-reply-to', thr: THREAD_NS).nil? + !xml.at_xpath('./thr:in-reply-to', thr: TagManager::THR_XMLNS).nil? end def thread(xml = @xml) - thr = xml.at_xpath('./thr:in-reply-to', thr: THREAD_NS) + thr = xml.at_xpath('./thr:in-reply-to', thr: TagManager::THR_XMLNS) [thr['ref'], thr['href']] end def account?(xml = @xml) - !xml.at_xpath('./xmlns:author').nil? + !xml.at_xpath('./xmlns:author', xmlns: TagManager::XMLNS).nil? end def acct(xml = @xml) - username = xml.at_xpath('./xmlns:author/xmlns:name').content - url = xml.at_xpath('./xmlns:author/xmlns:uri').content + username = xml.at_xpath('./xmlns:author/xmlns:name', xmlns: TagManager::XMLNS).content + url = xml.at_xpath('./xmlns:author/xmlns:uri', xmlns: TagManager::XMLNS).content domain = Addressable::URI.parse(url).host "#{username}@#{domain}" diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb index 6b2f6e2d2..129b2a2be 100644 --- a/app/services/process_interaction_service.rb +++ b/app/services/process_interaction_service.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class ProcessInteractionService < BaseService - ACTIVITY_NS = 'http://activitystrea.ms/spec/1.0/' - # Record locally the remote interaction with our user # @param [String] envelope Salmon envelope # @param [Account] target_account Account the Salmon was addressed to @@ -14,8 +12,8 @@ class ProcessInteractionService < BaseService return unless contains_author?(xml) - username = xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:name').content - url = xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:uri').content + username = xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:name', xmlns: TagManager::XMLNS).content + url = xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:uri', xmlns: TagManager::XMLNS).content domain = Addressable::URI.parse(url).host account = Account.find_by(username: username, domain: domain) @@ -26,7 +24,7 @@ class ProcessInteractionService < BaseService end if salmon.verify(envelope, account.keypair) - update_remote_profile_service.call(xml.at_xpath('/xmlns:entry'), account, true) + update_remote_profile_service.call(xml.at_xpath('/xmlns:entry', xmlns: TagManager::XMLNS), account, true) case verb(xml) when :follow @@ -50,16 +48,17 @@ class ProcessInteractionService < BaseService private def contains_author?(xml) - !(xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:name').nil? || xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:uri').nil?) + !(xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:name', xmlns: TagManager::XMLNS).nil? || xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:uri', xmlns: TagManager::XMLNS).nil?) end def mentions_account?(xml, account) - xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == TagManager.instance.url_for(account) } + xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]', xmlns: TagManager::XMLNS).each { |mention_link| return true if mention_link.attribute('href').value == TagManager.instance.url_for(account) } false end def verb(xml) - xml.at_xpath('//activity:verb', activity: ACTIVITY_NS).content.gsub('http://activitystrea.ms/schema/1.0/', '').gsub('http://ostatus.org/schema/1.0/', '').to_sym + raw = xml.at_xpath('//activity:verb', activity: TagManager::AS_XMLNS).content + TagManager::VERBS.key(raw) rescue :post end @@ -74,7 +73,7 @@ class ProcessInteractionService < BaseService end def delete_post!(xml, account) - status = Status.find(xml.at_xpath('//xmlns:id').content) + status = Status.find(xml.at_xpath('//xmlns:id', xmlns: TagManager::XMLNS).content) return if status.nil? @@ -96,7 +95,7 @@ class ProcessInteractionService < BaseService end def activity_id(xml) - xml.at_xpath('//activity:object', activity: ACTIVITY_NS).at_xpath('./xmlns:id').content + xml.at_xpath('//activity:object', activity: TagManager::AS_XMLNS).at_xpath('./xmlns:id', xmlns: TagManager::XMLNS).content end def salmon diff --git a/app/services/update_remote_profile_service.rb b/app/services/update_remote_profile_service.rb index 56b25816f..66d25dfeb 100644 --- a/app/services/update_remote_profile_service.rb +++ b/app/services/update_remote_profile_service.rb @@ -1,19 +1,16 @@ # frozen_string_literal: true class UpdateRemoteProfileService < BaseService - POCO_NS = 'http://portablecontacts.net/spec/1.0' - DFRN_NS = 'http://purl.org/macgirvin/dfrn/1.0' - def call(xml, account, resubscribe = false) return if xml.nil? - author_xml = xml.at_xpath('./xmlns:author') || xml.at_xpath('./dfrn:owner', dfrn: DFRN_NS) - hub_link = xml.at_xpath('./xmlns:link[@rel="hub"]') + author_xml = xml.at_xpath('./xmlns:author', xmlns: TagManager::XMLNS) || xml.at_xpath('./dfrn:owner', dfrn: TagManager::DFRN_XMLNS) + hub_link = xml.at_xpath('./xmlns:link[@rel="hub"]', xmlns: TagManager::XMLNS) unless author_xml.nil? - account.display_name = author_xml.at_xpath('./poco:displayName', poco: POCO_NS).content unless author_xml.at_xpath('./poco:displayName', poco: POCO_NS).nil? - account.note = author_xml.at_xpath('./poco:note', poco: POCO_NS).content unless author_xml.at_xpath('./poco:note').nil? - account.avatar_remote_url = author_xml.at_xpath('./xmlns:link[@rel="avatar"]')['href'] unless author_xml.at_xpath('./xmlns:link[@rel="avatar"]').nil? || author_xml.at_xpath('./xmlns:link[@rel="avatar"]')['href'].blank? + account.display_name = author_xml.at_xpath('./poco:displayName', poco: TagManager::POCO_XMLNS).content unless author_xml.at_xpath('./poco:displayName', 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.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? end old_hub_url = account.hub_url diff --git a/app/views/api/oembed/show.json.rabl b/app/views/api/oembed/show.json.rabl new file mode 100644 index 000000000..e035bc13c --- /dev/null +++ b/app/views/api/oembed/show.json.rabl @@ -0,0 +1,14 @@ +# frozen_string_literal: true +object @stream_entry + +node(:type) { 'rich' } +node(:version) { '1.0' } +node(:title, &:title) +node(:author_name) { |entry| entry.account.display_name.blank? ? entry.account.username : entry.account.display_name } +node(:author_url) { |entry| account_url(entry.account) } +node(:provider_name) { Rails.configuration.x.local_domain } +node(:provider_url) { root_url } +node(:cache_age) { 86_400 } +node(:html, &:content) +node(:width) { @width } +node(:height) { @height } diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml index e5f8a46c4..c2f1adb12 100644 --- a/app/views/settings/profiles/show.html.haml +++ b/app/views/settings/profiles/show.html.haml @@ -8,7 +8,6 @@ = f.input :note, placeholder: t('simple_form.labels.defaults.note') = f.input :avatar, wrapper: :with_label = f.input :header, wrapper: :with_label - = f.input :silenced, as: :boolean, wrapper: :with_label .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml index 2c6de32d9..e628dd99d 100644 --- a/app/views/stream_entries/show.html.haml +++ b/app/views/stream_entries/show.html.haml @@ -1,8 +1,13 @@ - content_for :header_tags do %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') }/ + %meta{ name: 'og:site_name', content: 'Mastodon' }/ %meta{ name: 'og:type', content: 'article' }/ + %meta{ name: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/ %meta{ name: 'og:article:author', content: @account.username }/ + %meta{ name: 'og:description', content: @stream_entry.activity.content }/ + %meta{ name: 'og:image', content: @stream_entry.activity.is_a?(Status) && @stream_entry.activity.media_attachments.size > 0 ? full_asset_url(@stream_entry.activity.media_attachments.first.file.url(:preview)) : full_asset_url(@account.avatar.url(:large)) }/ .activity-stream.activity-stream-headless = render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true } |