Per-status control for unlisted mode, also federation for unlisted mode
Fix #233, fix #268
This commit is contained in:
		
							parent
							
								
									1b447c190e
								
							
						
					
					
						commit
						14bd46946d
					
				
							
								
								
									
										12
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Gemfile.lock
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| GIT | GIT | ||||||
|   remote: https://github.com/rails/rails.git |   remote: https://github.com/rails/rails.git | ||||||
|   revision: ecb394a31420f6fd1d0ab692c79f2dd44176e2c9 |   revision: c7a716aa5a692cae301d56b345faa5d79cbfc320 | ||||||
|   branch: 5-0-stable |   branch: 5-0-stable | ||||||
|   specs: |   specs: | ||||||
|     actioncable (5.0.0.1) |     actioncable (5.0.0.1) | ||||||
| @ -110,7 +110,7 @@ GEM | |||||||
|     coffee-script-source (1.10.0) |     coffee-script-source (1.10.0) | ||||||
|     colorize (0.8.1) |     colorize (0.8.1) | ||||||
|     concurrent-ruby (1.0.2) |     concurrent-ruby (1.0.2) | ||||||
|     connection_pool (2.2.0) |     connection_pool (2.2.1) | ||||||
|     crack (0.4.3) |     crack (0.4.3) | ||||||
|       safe_yaml (~> 1.0.0) |       safe_yaml (~> 1.0.0) | ||||||
|     debug_inspector (0.0.2) |     debug_inspector (0.0.2) | ||||||
| @ -287,7 +287,7 @@ GEM | |||||||
|       execjs |       execjs | ||||||
|       railties (>= 3.2) |       railties (>= 3.2) | ||||||
|       tilt |       tilt | ||||||
|     redis (3.3.1) |     redis (3.3.2) | ||||||
|     redis-actionpack (5.0.0) |     redis-actionpack (5.0.0) | ||||||
|       actionpack (>= 4.0.0, < 6) |       actionpack (>= 4.0.0, < 6) | ||||||
|       redis-rack (~> 2.0.0.pre) |       redis-rack (~> 2.0.0.pre) | ||||||
| @ -348,10 +348,10 @@ GEM | |||||||
|     sdoc (0.4.1) |     sdoc (0.4.1) | ||||||
|       json (~> 1.7, >= 1.7.7) |       json (~> 1.7, >= 1.7.7) | ||||||
|       rdoc (~> 4.0) |       rdoc (~> 4.0) | ||||||
|     sidekiq (4.2.1) |     sidekiq (4.2.7) | ||||||
|       concurrent-ruby (~> 1.0) |       concurrent-ruby (~> 1.0) | ||||||
|       connection_pool (~> 2.2, >= 2.2.0) |       connection_pool (~> 2.2, >= 2.2.0) | ||||||
|       rack-protection (~> 1.5) |       rack-protection (>= 1.5.0) | ||||||
|       redis (~> 3.2, >= 3.2.1) |       redis (~> 3.2, >= 3.2.1) | ||||||
|     simple_form (3.2.1) |     simple_form (3.2.1) | ||||||
|       actionpack (> 4, < 5.1) |       actionpack (> 4, < 5.1) | ||||||
| @ -374,7 +374,7 @@ GEM | |||||||
|       tins (~> 1.0) |       tins (~> 1.0) | ||||||
|     terminal-table (1.7.0) |     terminal-table (1.7.0) | ||||||
|       unicode-display_width (~> 1.1) |       unicode-display_width (~> 1.1) | ||||||
|     thor (0.19.1) |     thor (0.19.4) | ||||||
|     thread (0.2.2) |     thread (0.2.2) | ||||||
|     thread_safe (0.3.5) |     thread_safe (0.3.5) | ||||||
|     tilt (2.0.5) |     tilt (2.0.5) | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ export const COMPOSE_MOUNT   = 'COMPOSE_MOUNT'; | |||||||
| export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT'; | export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT'; | ||||||
| 
 | 
 | ||||||
| export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE'; | export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE'; | ||||||
|  | export const COMPOSE_VISIBILITY_CHANGE  = 'COMPOSE_VISIBILITY_CHANGE'; | ||||||
| 
 | 
 | ||||||
| export function changeCompose(text) { | export function changeCompose(text) { | ||||||
|   return { |   return { | ||||||
| @ -65,7 +66,8 @@ export function submitCompose() { | |||||||
|       status: getState().getIn(['compose', 'text'], ''), |       status: getState().getIn(['compose', 'text'], ''), | ||||||
|       in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), |       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: 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) { |     }).then(function (response) { | ||||||
|       dispatch(submitComposeSuccess(response.data)); |       dispatch(submitComposeSuccess(response.data)); | ||||||
|       dispatch(updateTimeline('home', response.data)); |       dispatch(updateTimeline('home', response.data)); | ||||||
| @ -207,3 +209,10 @@ export function changeComposeSensitivity(checked) { | |||||||
|     checked |     checked | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | export function changeComposeVisibility(checked) { | ||||||
|  |   return { | ||||||
|  |     type: COMPOSE_VISIBILITY_CHANGE, | ||||||
|  |     checked | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | |||||||
| @ -70,6 +70,7 @@ const ComposeForm = React.createClass({ | |||||||
|     suggestion_token: React.PropTypes.string, |     suggestion_token: React.PropTypes.string, | ||||||
|     suggestions: React.PropTypes.array, |     suggestions: React.PropTypes.array, | ||||||
|     sensitive: React.PropTypes.bool, |     sensitive: React.PropTypes.bool, | ||||||
|  |     unlisted: React.PropTypes.bool, | ||||||
|     is_submitting: React.PropTypes.bool, |     is_submitting: React.PropTypes.bool, | ||||||
|     is_uploading: React.PropTypes.bool, |     is_uploading: React.PropTypes.bool, | ||||||
|     in_reply_to: ImmutablePropTypes.map, |     in_reply_to: ImmutablePropTypes.map, | ||||||
| @ -79,7 +80,8 @@ const ComposeForm = React.createClass({ | |||||||
|     onClearSuggestions: React.PropTypes.func.isRequired, |     onClearSuggestions: React.PropTypes.func.isRequired, | ||||||
|     onFetchSuggestions: React.PropTypes.func.isRequired, |     onFetchSuggestions: React.PropTypes.func.isRequired, | ||||||
|     onSuggestionSelected: 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], |   mixins: [PureRenderMixin], | ||||||
| @ -147,6 +149,10 @@ const ComposeForm = React.createClass({ | |||||||
|     this.props.onChangeSensitivity(e.target.checked); |     this.props.onChangeSensitivity(e.target.checked); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|  |   handleChangeVisibility (e) { | ||||||
|  |     this.props.onChangeVisibility(e.target.checked); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { intl } = this.props; |     const { intl } = this.props; | ||||||
|     let replyArea  = ''; |     let replyArea  = ''; | ||||||
| @ -187,7 +193,12 @@ const ComposeForm = React.createClass({ | |||||||
|           <UploadButtonContainer style={{ paddingTop: '4px' }} /> |           <UploadButtonContainer style={{ paddingTop: '4px' }} /> | ||||||
|         </div> |         </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} /> |           <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> |           <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> |         </label> | ||||||
|  | |||||||
| @ -7,7 +7,8 @@ import { | |||||||
|   clearComposeSuggestions, |   clearComposeSuggestions, | ||||||
|   fetchComposeSuggestions, |   fetchComposeSuggestions, | ||||||
|   selectComposeSuggestion, |   selectComposeSuggestion, | ||||||
|   changeComposeSensitivity |   changeComposeSensitivity, | ||||||
|  |   changeComposeVisibility | ||||||
| } from '../../../actions/compose'; | } from '../../../actions/compose'; | ||||||
| import { makeGetStatus } from '../../../selectors'; | import { makeGetStatus } from '../../../selectors'; | ||||||
| 
 | 
 | ||||||
| @ -20,6 +21,7 @@ const makeMapStateToProps = () => { | |||||||
|       suggestion_token: state.getIn(['compose', 'suggestion_token']), |       suggestion_token: state.getIn(['compose', 'suggestion_token']), | ||||||
|       suggestions: state.getIn(['compose', 'suggestions']).toJS(), |       suggestions: state.getIn(['compose', 'suggestions']).toJS(), | ||||||
|       sensitive: state.getIn(['compose', 'sensitive']), |       sensitive: state.getIn(['compose', 'sensitive']), | ||||||
|  |       unlisted: state.getIn(['compose', 'unlisted']), | ||||||
|       is_submitting: state.getIn(['compose', 'is_submitting']), |       is_submitting: state.getIn(['compose', 'is_submitting']), | ||||||
|       is_uploading: state.getIn(['compose', 'is_uploading']), |       is_uploading: state.getIn(['compose', 'is_uploading']), | ||||||
|       in_reply_to: getStatus(state, state.getIn(['compose', 'in_reply_to'])) |       in_reply_to: getStatus(state, state.getIn(['compose', 'in_reply_to'])) | ||||||
| @ -57,6 +59,10 @@ const mapDispatchToProps = function (dispatch) { | |||||||
| 
 | 
 | ||||||
|     onChangeSensitivity (checked) { |     onChangeSensitivity (checked) { | ||||||
|       dispatch(changeComposeSensitivity(checked)); |       dispatch(changeComposeSensitivity(checked)); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     onChangeVisibility (checked) { | ||||||
|  |       dispatch(changeComposeVisibility(checked)); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -34,6 +34,8 @@ const en = { | |||||||
|   "tabs_bar.notifications": "Mitteilungen", |   "tabs_bar.notifications": "Mitteilungen", | ||||||
|   "compose_form.placeholder": "Worüber möchstest du schreiben?", |   "compose_form.placeholder": "Worüber möchstest du schreiben?", | ||||||
|   "compose_form.publish": "Veröffentlichen", |   "compose_form.publish": "Veröffentlichen", | ||||||
|  |   "compose_form.sensitive": "Medien als sensitiv markieren", | ||||||
|  |   "compose_form.unlisted": "Öffentlich nicht auflisten", | ||||||
|   "navigation_bar.settings": "Einstellungen", |   "navigation_bar.settings": "Einstellungen", | ||||||
|   "navigation_bar.public_timeline": "Öffentlich", |   "navigation_bar.public_timeline": "Öffentlich", | ||||||
|   "navigation_bar.logout": "Abmelden", |   "navigation_bar.logout": "Abmelden", | ||||||
|  | |||||||
| @ -38,6 +38,7 @@ const en = { | |||||||
|   "compose_form.placeholder": "What is on your mind?", |   "compose_form.placeholder": "What is on your mind?", | ||||||
|   "compose_form.publish": "Toot", |   "compose_form.publish": "Toot", | ||||||
|   "compose_form.sensitive": "Mark content as sensitive", |   "compose_form.sensitive": "Mark content as sensitive", | ||||||
|  |   "compose_form.unlisted": "Unlisted mode", | ||||||
|   "navigation_bar.settings": "Settings", |   "navigation_bar.settings": "Settings", | ||||||
|   "navigation_bar.public_timeline": "Public timeline", |   "navigation_bar.public_timeline": "Public timeline", | ||||||
|   "navigation_bar.logout": "Logout", |   "navigation_bar.logout": "Logout", | ||||||
|  | |||||||
| @ -35,6 +35,8 @@ const es = { | |||||||
|   "tabs_bar.notifications": "Notificaciones", |   "tabs_bar.notifications": "Notificaciones", | ||||||
|   "compose_form.placeholder": "¿En qué estás pensando?", |   "compose_form.placeholder": "¿En qué estás pensando?", | ||||||
|   "compose_form.publish": "Publicar", |   "compose_form.publish": "Publicar", | ||||||
|  |   "compose_form.sensitive": null, | ||||||
|  |   "compose_form.unlisted": "No listado", | ||||||
|   "navigation_bar.settings": "Ajustes", |   "navigation_bar.settings": "Ajustes", | ||||||
|   "navigation_bar.public_timeline": "Público", |   "navigation_bar.public_timeline": "Público", | ||||||
|   "navigation_bar.logout": "Cerrar sesión", |   "navigation_bar.logout": "Cerrar sesión", | ||||||
|  | |||||||
| @ -36,7 +36,8 @@ const fr = { | |||||||
|   "tabs_bar.notifications": "Notifications", |   "tabs_bar.notifications": "Notifications", | ||||||
|   "compose_form.placeholder": "Qu’avez-vous en tête ?", |   "compose_form.placeholder": "Qu’avez-vous en tête ?", | ||||||
|   "compose_form.publish": "Pouet", |   "compose_form.publish": "Pouet", | ||||||
|   "compose_form.sensitive": "Marquer le 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.settings": "Paramètres", | ||||||
|   "navigation_bar.public_timeline": "Public", |   "navigation_bar.public_timeline": "Public", | ||||||
|   "navigation_bar.logout": "Déconnexion", |   "navigation_bar.logout": "Déconnexion", | ||||||
|  | |||||||
| @ -37,6 +37,7 @@ const hu = { | |||||||
|   "compose_form.placeholder": "Mire gondolsz?", |   "compose_form.placeholder": "Mire gondolsz?", | ||||||
|   "compose_form.publish": "Tülk!", |   "compose_form.publish": "Tülk!", | ||||||
|   "compose_form.sensitive": "Tartalom érzékenynek jelölése", |   "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.settings": "Beállítások", | ||||||
|   "navigation_bar.public_timeline": "Nyilvános időfolyam", |   "navigation_bar.public_timeline": "Nyilvános időfolyam", | ||||||
|   "navigation_bar.logout": "Kijelentkezés", |   "navigation_bar.logout": "Kijelentkezés", | ||||||
|  | |||||||
| @ -33,6 +33,8 @@ const pt = { | |||||||
|   "tabs_bar.public": "Público", |   "tabs_bar.public": "Público", | ||||||
|   "compose_form.placeholder": "Que estás pensando?", |   "compose_form.placeholder": "Que estás pensando?", | ||||||
|   "compose_form.publish": "Publicar", |   "compose_form.publish": "Publicar", | ||||||
|  |   "compose_form.sensitive": null, | ||||||
|  |   "compose_form.unlisted": null, | ||||||
|   "navigation_bar.settings": "Configurações", |   "navigation_bar.settings": "Configurações", | ||||||
|   "navigation_bar.public_timeline": "Timeline Pública", |   "navigation_bar.public_timeline": "Timeline Pública", | ||||||
|   "navigation_bar.logout": "Logout", |   "navigation_bar.logout": "Logout", | ||||||
|  | |||||||
| @ -16,7 +16,8 @@ import { | |||||||
|   COMPOSE_SUGGESTIONS_CLEAR, |   COMPOSE_SUGGESTIONS_CLEAR, | ||||||
|   COMPOSE_SUGGESTIONS_READY, |   COMPOSE_SUGGESTIONS_READY, | ||||||
|   COMPOSE_SUGGESTION_SELECT, |   COMPOSE_SUGGESTION_SELECT, | ||||||
|   COMPOSE_SENSITIVITY_CHANGE |   COMPOSE_SENSITIVITY_CHANGE, | ||||||
|  |   COMPOSE_VISIBILITY_CHANGE | ||||||
| } from '../actions/compose'; | } from '../actions/compose'; | ||||||
| import { TIMELINE_DELETE } from '../actions/timelines'; | import { TIMELINE_DELETE } from '../actions/timelines'; | ||||||
| import { ACCOUNT_SET_SELF } from '../actions/accounts'; | import { ACCOUNT_SET_SELF } from '../actions/accounts'; | ||||||
| @ -25,6 +26,7 @@ import Immutable from 'immutable'; | |||||||
| const initialState = Immutable.Map({ | const initialState = Immutable.Map({ | ||||||
|   mounted: false, |   mounted: false, | ||||||
|   sensitive: false, |   sensitive: false, | ||||||
|  |   unlisted: false, | ||||||
|   text: '', |   text: '', | ||||||
|   in_reply_to: null, |   in_reply_to: null, | ||||||
|   is_submitting: false, |   is_submitting: false, | ||||||
| @ -91,6 +93,8 @@ export default function compose(state = initialState, action) { | |||||||
|       return state.set('mounted', false); |       return state.set('mounted', false); | ||||||
|     case COMPOSE_SENSITIVITY_CHANGE: |     case COMPOSE_SENSITIVITY_CHANGE: | ||||||
|       return state.set('sensitive', action.checked); |       return state.set('sensitive', action.checked); | ||||||
|  |     case COMPOSE_VISIBILITY_CHANGE: | ||||||
|  |       return state.set('unlisted', action.checked); | ||||||
|     case COMPOSE_CHANGE: |     case COMPOSE_CHANGE: | ||||||
|       return state.set('text', action.text); |       return state.set('text', action.text); | ||||||
|     case COMPOSE_REPLY: |     case COMPOSE_REPLY: | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ class Api::V1::StatusesController < ApiController | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def create |   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 |     render action: :show | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
| @ -73,7 +73,7 @@ class Api::V1::StatusesController < ApiController | |||||||
|     @reblogged_map = { @status.id => false } |     @reblogged_map = { @status.id => false } | ||||||
| 
 | 
 | ||||||
|     RemovalWorker.perform_async(reblog.id) |     RemovalWorker.perform_async(reblog.id) | ||||||
|      | 
 | ||||||
|     render action: :show |     render action: :show | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
| module ObfuscateFilename | module ObfuscateFilename | ||||||
|   extend ActiveSupport::Concern |   extend ActiveSupport::Concern | ||||||
| 
 | 
 | ||||||
| @ -11,6 +12,6 @@ module ObfuscateFilename | |||||||
|     file = params.dig(*path) |     file = params.dig(*path) | ||||||
|     return if file.nil? |     return if file.nil? | ||||||
| 
 | 
 | ||||||
|     file.original_filename = "media" + File.extname(file.original_filename) |     file.original_filename = 'media' + File.extname(file.original_filename) | ||||||
|   end |   end | ||||||
| end | end | ||||||
| @ -24,7 +24,7 @@ class Settings::ProfilesController < ApplicationController | |||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def account_params |   def account_params | ||||||
|     params.require(:account).permit(:display_name, :note, :avatar, :header, :silenced) |     params.require(:account).permit(:display_name, :note, :avatar, :header) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def set_account |   def set_account | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ module AtomBuilderHelper | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def verb(xml, verb) |   def verb(xml, verb) | ||||||
|     xml['activity'].send('verb', "http://activitystrea.ms/schema/1.0/#{verb}") |     xml['activity'].send('verb', TagManager::VERBS[verb]) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def content(xml, content) |   def content(xml, content) | ||||||
| @ -62,7 +62,7 @@ module AtomBuilderHelper | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def object_type(xml, type) |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def uri(xml, uri) |   def uri(xml, uri) | ||||||
| @ -108,7 +108,7 @@ module AtomBuilderHelper | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def link_mention(xml, account) |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def link_enclosure(xml, media) |   def link_enclosure(xml, media) | ||||||
| @ -139,6 +139,11 @@ module AtomBuilderHelper | |||||||
|     end |     end | ||||||
|   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) |   def include_author(xml, account) | ||||||
|     object_type      xml, :person |     object_type      xml, :person | ||||||
|     uri              xml, TagManager.instance.uri_for(account) |     uri              xml, TagManager.instance.uri_for(account) | ||||||
| @ -189,6 +194,8 @@ module AtomBuilderHelper | |||||||
|             include_author xml, stream_entry.target.account |             include_author xml, stream_entry.target.account | ||||||
|           end |           end | ||||||
| 
 | 
 | ||||||
|  |           link_visibility xml, stream_entry.target | ||||||
|  | 
 | ||||||
|           stream_entry.target.mentions.each do |mention| |           stream_entry.target.mentions.each do |mention| | ||||||
|             link_mention xml, mention.account |             link_mention xml, mention.account | ||||||
|           end |           end | ||||||
| @ -204,25 +211,34 @@ module AtomBuilderHelper | |||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |     link_visibility xml, stream_entry.activity | ||||||
|  | 
 | ||||||
|     stream_entry.mentions.each do |mentioned| |     stream_entry.mentions.each do |mentioned| | ||||||
|       link_mention xml, mentioned |       link_mention xml, mentioned | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     if stream_entry.activity.is_a?(Status) |     return unless stream_entry.activity.is_a?(Status) | ||||||
|       stream_entry.activity.media_attachments.each do |media| |  | ||||||
|         link_enclosure xml, media |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       stream_entry.activity.tags.each do |tag| |     stream_entry.activity.media_attachments.each do |media| | ||||||
|         category xml, tag |       link_enclosure xml, media | ||||||
|       end |     end | ||||||
|  | 
 | ||||||
|  |     stream_entry.activity.tags.each do |tag| | ||||||
|  |       category xml, tag | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def root_tag(xml, tag, &block) |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def single_link_avatar(xml, account, size, px) |   def single_link_avatar(xml, account, size, px) | ||||||
|  | |||||||
| @ -6,6 +6,37 @@ class TagManager | |||||||
|   include Singleton |   include Singleton | ||||||
|   include RoutingHelper |   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) |   def unique_tag(date, id, type) | ||||||
|     "tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" |     "tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" | ||||||
|   end |   end | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ class Status < ApplicationRecord | |||||||
|   include Streamable |   include Streamable | ||||||
|   include Cacheable |   include Cacheable | ||||||
| 
 | 
 | ||||||
|  |   enum visibility: [:public, :unlisted], _suffix: :visibility | ||||||
|  | 
 | ||||||
|   belongs_to :account, inverse_of: :statuses |   belongs_to :account, inverse_of: :statuses | ||||||
| 
 | 
 | ||||||
|   belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :replies |   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) |     def as_public_timeline(account = nil) | ||||||
|       query = joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id') |       query = joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id') | ||||||
|  |               .where(visibility: :public) | ||||||
|               .where('accounts.silenced = FALSE') |               .where('accounts.silenced = FALSE') | ||||||
|               .where('statuses.in_reply_to_id IS NULL') |               .where('statuses.in_reply_to_id IS NULL') | ||||||
|               .where('statuses.reblog_of_id IS NULL') |               .where('statuses.reblog_of_id IS NULL') | ||||||
| @ -110,6 +113,7 @@ class Status < ApplicationRecord | |||||||
|     def as_tag_timeline(tag, account = nil) |     def as_tag_timeline(tag, account = nil) | ||||||
|       query = tag.statuses |       query = tag.statuses | ||||||
|                  .joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id') |                  .joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id') | ||||||
|  |                  .where(visibility: :public) | ||||||
|                  .where('accounts.silenced = FALSE') |                  .where('accounts.silenced = FALSE') | ||||||
|                  .where('statuses.in_reply_to_id IS NULL') |                  .where('statuses.in_reply_to_id IS NULL') | ||||||
|                  .where('statuses.reblog_of_id IS NULL') |                  .where('statuses.reblog_of_id IS NULL') | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ class FanOutOnWriteService < BaseService | |||||||
|     deliver_to_followers(status) |     deliver_to_followers(status) | ||||||
|     deliver_to_mentioned(status) |     deliver_to_mentioned(status) | ||||||
| 
 | 
 | ||||||
|     return if status.account.silenced? |     return if status.account.silenced? || !status.public_visibility? | ||||||
| 
 | 
 | ||||||
|     deliver_to_hashtags(status) |     deliver_to_hashtags(status) | ||||||
|     deliver_to_public(status) |     deliver_to_public(status) | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ class PostStatusService < BaseService | |||||||
|   # @option [Enumerable] :media_ids Optional array of media IDs to attach |   # @option [Enumerable] :media_ids Optional array of media IDs to attach | ||||||
|   # @return [Status] |   # @return [Status] | ||||||
|   def call(account, text, in_reply_to = nil, options = {}) |   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]) |     attach_media(status, options[:media_ids]) | ||||||
|     process_mentions_service.call(status) |     process_mentions_service.call(status) | ||||||
|     process_hashtags_service.call(status) |     process_hashtags_service.call(status) | ||||||
|  | |||||||
| @ -1,9 +1,6 @@ | |||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
| 
 | 
 | ||||||
| class ProcessFeedService < BaseService | class ProcessFeedService < BaseService | ||||||
|   ACTIVITY_NS = 'http://activitystrea.ms/spec/1.0/' |  | ||||||
|   THREAD_NS   = 'http://purl.org/syndication/thread/1.0' |  | ||||||
| 
 |  | ||||||
|   def call(body, account) |   def call(body, account) | ||||||
|     xml = Nokogiri::XML(body) |     xml = Nokogiri::XML(body) | ||||||
|     xml.encoding = 'utf-8' |     xml.encoding = 'utf-8' | ||||||
| @ -15,12 +12,12 @@ class ProcessFeedService < BaseService | |||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def update_author(xml, account) |   def update_author(xml, account) | ||||||
|     return if xml.at_xpath('/xmlns:feed').nil? |     return if xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS).nil? | ||||||
|     UpdateRemoteProfileService.new.call(xml.at_xpath('/xmlns:feed'), account, true) |     UpdateRemoteProfileService.new.call(xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS), account, true) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def process_entries(xml, account) |   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 |   end | ||||||
| 
 | 
 | ||||||
|   class ProcessEntry |   class ProcessEntry | ||||||
| @ -48,7 +45,7 @@ class ProcessFeedService < BaseService | |||||||
|       status = status_from_xml(@xml) |       status = status_from_xml(@xml) | ||||||
| 
 | 
 | ||||||
|       if verb == :share |       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 |         status.reblog   = original_status | ||||||
| 
 | 
 | ||||||
|         if original_status.nil? |         if original_status.nil? | ||||||
| @ -138,9 +135,15 @@ class ProcessFeedService < BaseService | |||||||
| 
 | 
 | ||||||
|     def mentions_from_xml(parent, xml) |     def mentions_from_xml(parent, xml) | ||||||
|       processed_account_ids = [] |       processed_account_ids = [] | ||||||
|  |       public_visibility     = false | ||||||
| 
 | 
 | ||||||
|       xml.xpath('./xmlns:link[@rel="mentioned"]').each do |link| |       xml.xpath('./xmlns:link[@rel="mentioned"]', xmlns: TagManager::XMLNS).each do |link| | ||||||
|         next if link['href'] == 'http://activityschema.org/collection/public' |         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']) |         url = Addressable::URI.parse(link['href']) | ||||||
| 
 | 
 | ||||||
| @ -160,15 +163,18 @@ class ProcessFeedService < BaseService | |||||||
|         # So we can skip duplicate mentions |         # So we can skip duplicate mentions | ||||||
|         processed_account_ids << mentioned_account.id |         processed_account_ids << mentioned_account.id | ||||||
|       end |       end | ||||||
|  | 
 | ||||||
|  |       parent.visibility = public_visibility ? :public : :unlisted | ||||||
|  |       parent.save! | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def hashtags_from_xml(parent, xml) |     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) |       ProcessHashtagsService.new.call(parent, tags) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def media_from_xml(parent, xml) |     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'] |         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']) |         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 |     end | ||||||
| 
 | 
 | ||||||
|     def id(xml = @xml) |     def id(xml = @xml) | ||||||
|       xml.at_xpath('./xmlns:id').content |       xml.at_xpath('./xmlns:id', xmlns: TagManager::XMLNS).content | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def verb(xml = @xml) |     def verb(xml = @xml) | ||||||
|       raw = xml.at_xpath('./activity:verb', activity: ACTIVITY_NS).content |       raw = xml.at_xpath('./activity:verb', activity: TagManager::AS_XMLNS).content | ||||||
|       raw.gsub('http://activitystrea.ms/schema/1.0/', '').gsub('http://ostatus.org/schema/1.0/', '').to_sym |       TagManager::VERBS.key(raw) | ||||||
|     rescue |     rescue | ||||||
|       :post |       :post | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def type(xml = @xml) |     def type(xml = @xml) | ||||||
|       raw = xml.at_xpath('./activity:object-type', activity: ACTIVITY_NS).content |       raw = xml.at_xpath('./activity:object-type', activity: TagManager::AS_XMLNS).content | ||||||
|       raw.gsub('http://activitystrea.ms/schema/1.0/', '').gsub('http://ostatus.org/schema/1.0/', '').to_sym |       TagManager::TYPES.key(raw) | ||||||
|     rescue |     rescue | ||||||
|       :activity |       :activity | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def url(xml = @xml) |     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'] |       link.nil? ? nil : link['href'] | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def content(xml = @xml) |     def content(xml = @xml) | ||||||
|       xml.at_xpath('./xmlns:content').content |       xml.at_xpath('./xmlns:content', xmlns: TagManager::XMLNS).content | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def published(xml = @xml) |     def published(xml = @xml) | ||||||
|       xml.at_xpath('./xmlns:published').content |       xml.at_xpath('./xmlns:published', xmlns: TagManager::XMLNS).content | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def thread?(xml = @xml) |     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 |     end | ||||||
| 
 | 
 | ||||||
|     def thread(xml = @xml) |     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']] |       [thr['ref'], thr['href']] | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def account?(xml = @xml) |     def account?(xml = @xml) | ||||||
|       !xml.at_xpath('./xmlns:author').nil? |       !xml.at_xpath('./xmlns:author', xmlns: TagManager::XMLNS).nil? | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def acct(xml = @xml) |     def acct(xml = @xml) | ||||||
|       username = xml.at_xpath('./xmlns:author/xmlns:name').content |       username = xml.at_xpath('./xmlns:author/xmlns:name', xmlns: TagManager::XMLNS).content | ||||||
|       url      = xml.at_xpath('./xmlns:author/xmlns:uri').content |       url      = xml.at_xpath('./xmlns:author/xmlns:uri', xmlns: TagManager::XMLNS).content | ||||||
|       domain   = Addressable::URI.parse(url).host |       domain   = Addressable::URI.parse(url).host | ||||||
| 
 | 
 | ||||||
|       "#{username}@#{domain}" |       "#{username}@#{domain}" | ||||||
|  | |||||||
| @ -1,8 +1,6 @@ | |||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
| 
 | 
 | ||||||
| class ProcessInteractionService < BaseService | class ProcessInteractionService < BaseService | ||||||
|   ACTIVITY_NS = 'http://activitystrea.ms/spec/1.0/' |  | ||||||
| 
 |  | ||||||
|   # Record locally the remote interaction with our user |   # Record locally the remote interaction with our user | ||||||
|   # @param [String] envelope Salmon envelope |   # @param [String] envelope Salmon envelope | ||||||
|   # @param [Account] target_account Account the Salmon was addressed to |   # @param [Account] target_account Account the Salmon was addressed to | ||||||
| @ -14,8 +12,8 @@ class ProcessInteractionService < BaseService | |||||||
| 
 | 
 | ||||||
|     return unless contains_author?(xml) |     return unless contains_author?(xml) | ||||||
| 
 | 
 | ||||||
|     username = xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:name').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').content |     url      = xml.at_xpath('/xmlns:entry/xmlns:author/xmlns:uri', xmlns: TagManager::XMLNS).content | ||||||
|     domain   = Addressable::URI.parse(url).host |     domain   = Addressable::URI.parse(url).host | ||||||
|     account  = Account.find_by(username: username, domain: domain) |     account  = Account.find_by(username: username, domain: domain) | ||||||
| 
 | 
 | ||||||
| @ -26,7 +24,7 @@ class ProcessInteractionService < BaseService | |||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     if salmon.verify(envelope, account.keypair) |     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) |       case verb(xml) | ||||||
|       when :follow |       when :follow | ||||||
| @ -50,16 +48,17 @@ class ProcessInteractionService < BaseService | |||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def contains_author?(xml) |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def mentions_account?(xml, account) |   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 |     false | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def verb(xml) |   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 |   rescue | ||||||
|     :post |     :post | ||||||
|   end |   end | ||||||
| @ -74,7 +73,7 @@ class ProcessInteractionService < BaseService | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def delete_post!(xml, account) |   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? |     return if status.nil? | ||||||
| 
 | 
 | ||||||
| @ -96,7 +95,7 @@ class ProcessInteractionService < BaseService | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def activity_id(xml) |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def salmon |   def salmon | ||||||
|  | |||||||
| @ -1,19 +1,16 @@ | |||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
| 
 | 
 | ||||||
| class UpdateRemoteProfileService < BaseService | 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) |   def call(xml, account, resubscribe = false) | ||||||
|     return if xml.nil? |     return if xml.nil? | ||||||
| 
 | 
 | ||||||
|     author_xml = xml.at_xpath('./xmlns:author') || xml.at_xpath('./dfrn:owner', dfrn: DFRN_NS) |     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"]') |     hub_link   = xml.at_xpath('./xmlns:link[@rel="hub"]', xmlns: TagManager::XMLNS) | ||||||
| 
 | 
 | ||||||
|     unless author_xml.nil? |     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.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: POCO_NS).content unless author_xml.at_xpath('./poco:note').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"]')['href'] unless author_xml.at_xpath('./xmlns:link[@rel="avatar"]').nil? || author_xml.at_xpath('./xmlns:link[@rel="avatar"]')['href'].blank? |       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 |     end | ||||||
| 
 | 
 | ||||||
|     old_hub_url     = account.hub_url |     old_hub_url     = account.hub_url | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ | |||||||
|   = f.input :note, placeholder: t('simple_form.labels.defaults.note') |   = f.input :note, placeholder: t('simple_form.labels.defaults.note') | ||||||
|   = f.input :avatar, wrapper: :with_label |   = f.input :avatar, wrapper: :with_label | ||||||
|   = f.input :header, wrapper: :with_label |   = f.input :header, wrapper: :with_label | ||||||
|   = f.input :silenced, as: :boolean, wrapper: :with_label |  | ||||||
| 
 | 
 | ||||||
|   .actions |   .actions | ||||||
|     = f.button :button, t('generic.save_changes'), type: :submit |     = f.button :button, t('generic.save_changes'), type: :submit | ||||||
|  | |||||||
| @ -14,7 +14,6 @@ de: | |||||||
|         new_password: Neues Passwort |         new_password: Neues Passwort | ||||||
|         note: Über mich |         note: Über mich | ||||||
|         password: Passwort |         password: Passwort | ||||||
|         silenced: Öffentliche Beiträge nicht auflisten |  | ||||||
|         username: Nutzername |         username: Nutzername | ||||||
|       interactions: |       interactions: | ||||||
|         must_be_follower: Benachrichtigungen von nicht-Folgern blockieren |         must_be_follower: Benachrichtigungen von nicht-Folgern blockieren | ||||||
|  | |||||||
| @ -14,7 +14,6 @@ en: | |||||||
|         new_password: New password |         new_password: New password | ||||||
|         note: Bio |         note: Bio | ||||||
|         password: Password |         password: Password | ||||||
|         silenced: Unlisted mode |  | ||||||
|         username: Username |         username: Username | ||||||
|       interactions: |       interactions: | ||||||
|         must_be_follower: Block notifications from non-followers |         must_be_follower: Block notifications from non-followers | ||||||
|  | |||||||
| @ -14,7 +14,6 @@ es: | |||||||
|         new_password: Nueva contraseña |         new_password: Nueva contraseña | ||||||
|         note: Biografía |         note: Biografía | ||||||
|         password: Contraseña |         password: Contraseña | ||||||
|         silenced: No listado |  | ||||||
|         username: Nombre de usuario |         username: Nombre de usuario | ||||||
|       notification_emails: |       notification_emails: | ||||||
|         favourite: Enviar correo electrónico cuando alguien de a favorito en su publicación |         favourite: Enviar correo electrónico cuando alguien de a favorito en su publicación | ||||||
|  | |||||||
| @ -14,7 +14,6 @@ fr: | |||||||
|         new_password: Nouveau mot de passe |         new_password: Nouveau mot de passe | ||||||
|         note: Présentation |         note: Présentation | ||||||
|         password: Mot de passe |         password: Mot de passe | ||||||
|         silenced: Ne pas apparaître dans le fil public |  | ||||||
|         username: Identifiant |         username: Identifiant | ||||||
|       interactions: |       interactions: | ||||||
|         must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas |         must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas | ||||||
|  | |||||||
| @ -14,7 +14,6 @@ hu: | |||||||
|         new_password: Új jelszó |         new_password: Új jelszó | ||||||
|         note: Önéletrajz |         note: Önéletrajz | ||||||
|         password: Jelszó |         password: Jelszó | ||||||
|         silenced: Listázatlan mód |  | ||||||
|         username: Felhasználónév |         username: Felhasználónév | ||||||
|       notification_emails: |       notification_emails: | ||||||
|         favourite: E-mail küldése amikor valaki kedvencnek jelöli az állapotod |         favourite: E-mail küldése amikor valaki kedvencnek jelöli az állapotod | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								db/migrate/20161130185319_add_visibility_to_statuses.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								db/migrate/20161130185319_add_visibility_to_statuses.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | class AddVisibilityToStatuses < ActiveRecord::Migration[5.0] | ||||||
|  |   def change | ||||||
|  |     add_column :statuses, :visibility, :integer, null: false, default: 0 | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -10,7 +10,7 @@ | |||||||
| # | # | ||||||
| # It's strongly recommended that you check this file into your version control system. | # It's strongly recommended that you check this file into your version control system. | ||||||
| 
 | 
 | ||||||
| ActiveRecord::Schema.define(version: 20161130142058) do | ActiveRecord::Schema.define(version: 20161130185319) do | ||||||
| 
 | 
 | ||||||
|   # These are extensions that must be enabled in order to support this database |   # These are extensions that must be enabled in order to support this database | ||||||
|   enable_extension "plpgsql" |   enable_extension "plpgsql" | ||||||
| @ -176,6 +176,7 @@ ActiveRecord::Schema.define(version: 20161130142058) do | |||||||
|     t.integer  "reblog_of_id" |     t.integer  "reblog_of_id" | ||||||
|     t.string   "url" |     t.string   "url" | ||||||
|     t.boolean  "sensitive",      default: false |     t.boolean  "sensitive",      default: false | ||||||
|  |     t.integer  "visibility",     default: 0,     null: false | ||||||
|     t.index ["account_id"], name: "index_statuses_on_account_id", using: :btree |     t.index ["account_id"], name: "index_statuses_on_account_id", using: :btree | ||||||
|     t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id", using: :btree |     t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id", using: :btree | ||||||
|     t.index ["reblog_of_id"], name: "index_statuses_on_reblog_of_id", using: :btree |     t.index ["reblog_of_id"], name: "index_statuses_on_reblog_of_id", using: :btree | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do | |||||||
| 
 | 
 | ||||||
|   describe '#feed' do |   describe '#feed' do | ||||||
|     it 'creates a feed' do |     it 'creates a feed' do | ||||||
|       expect(used_in_builder { |xml| helper.feed(xml) }).to match '<feed 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"/>' |       expect(used_in_builder { |xml| helper.feed(xml) }).to match '<feed 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" xmlns:ostatus="http://ostatus.org/schema/1.0"/>' | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
| @ -46,7 +46,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do | |||||||
| 
 | 
 | ||||||
|   describe '#verb' do |   describe '#verb' do | ||||||
|     it 'creates an entry' do |     it 'creates an entry' do | ||||||
|       expect(used_with_namespaces { |xml| helper.verb(xml, 'verb') }).to match '<activity:verb>http://activitystrea.ms/schema/1.0/verb</activity:verb>' |       expect(used_with_namespaces { |xml| helper.verb(xml, :post) }).to match '<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>' | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
| @ -76,7 +76,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do | |||||||
| 
 | 
 | ||||||
|   describe '#object_type' do |   describe '#object_type' do | ||||||
|     it 'creates an object type' do |     it 'creates an object type' do | ||||||
|       expect(used_with_namespaces { |xml| helper.object_type(xml, 'test') }).to match '<activity:object-type>http://activitystrea.ms/schema/1.0/test</activity:object-type>' |       expect(used_with_namespaces { |xml| helper.object_type(xml, :person) }).to match '<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>' | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
| @ -146,7 +146,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do | |||||||
|     let(:account) { Fabricate(:account, username: 'alice') } |     let(:account) { Fabricate(:account, username: 'alice') } | ||||||
| 
 | 
 | ||||||
|     it 'creates a link' do |     it 'creates a link' do | ||||||
|       expect(used_in_builder { |xml| helper.link_mention(xml, account) }).to match '<link rel="mentioned" href="https://cb6e6126.ngrok.io/users/alice"/>' |       expect(used_in_builder { |xml| helper.link_mention(xml, account) }).to match '<link rel="mentioned" href="https://cb6e6126.ngrok.io/users/alice" ostatus:object-type="http://activitystrea.ms/schema/1.0/person"/>' | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user