Merge branch 'ineffyble-feature/toot-app-source'
This commit is contained in:
		
						commit
						bfec9aaee0
					
				@ -87,3 +87,4 @@ AllCops:
 | 
			
		||||
  - 'bin/*'
 | 
			
		||||
  - 'Rakefile'
 | 
			
		||||
  - 'node_modules/**/*'
 | 
			
		||||
  - 'Vagrantfile'
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,9 @@ const DetailedStatus = React.createClass({
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status;
 | 
			
		||||
 | 
			
		||||
    let media           = '';
 | 
			
		||||
    let applicationLink = '';
 | 
			
		||||
 | 
			
		||||
    if (status.get('media_attachments').size > 0) {
 | 
			
		||||
      if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
 | 
			
		||||
@ -42,6 +44,10 @@ const DetailedStatus = React.createClass({
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (status.get('application')) {
 | 
			
		||||
      applicationLink = <span> · <a className='detailed-status__application' style={{ color: 'inherit' }} href={status.getIn(['application', 'website'])} target='_blank' rel='nooopener'>{status.getIn(['application', 'name'])}</a></span>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div style={{ background: '#2f3441', padding: '14px 10px' }} className='detailed-status'>
 | 
			
		||||
        <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name' style={{ display: 'block', overflow: 'hidden', marginBottom: '15px' }}>
 | 
			
		||||
@ -54,7 +60,7 @@ const DetailedStatus = React.createClass({
 | 
			
		||||
        {media}
 | 
			
		||||
 | 
			
		||||
        <div style={{ marginTop: '15px', color: '#616b86', fontSize: '14px', lineHeight: '18px' }}>
 | 
			
		||||
          <a className='detailed-status__datetime' style={{ color: 'inherit' }} href={status.get('url')} target='_blank' rel='noopener'><FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' /></a> · <Link to={`/statuses/${status.get('id')}/reblogs`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-retweet' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}><FormattedNumber value={status.get('reblogs_count')} /></span></Link> · <Link to={`/statuses/${status.get('id')}/favourites`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-star' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}><FormattedNumber value={status.get('favourites_count')} /></span></Link>
 | 
			
		||||
          <a className='detailed-status__datetime' style={{ color: 'inherit' }} href={status.get('url')} target='_blank' rel='noopener'><FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' /></a>{applicationLink} · <Link to={`/statuses/${status.get('id')}/reblogs`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-retweet' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}><FormattedNumber value={status.get('reblogs_count')} /></span></Link> · <Link to={`/statuses/${status.get('id')}/favourites`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-star' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}><FormattedNumber value={status.get('favourites_count')} /></span></Link>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -183,7 +183,7 @@
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.status__display-name, .status__relative-time, .detailed-status__display-name, .detailed-status__datetime, .account__display-name {
 | 
			
		||||
.status__display-name, .status__relative-time, .detailed-status__display-name, .detailed-status__datetime, .detailed-status__application, .account__display-name {
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -663,20 +663,21 @@
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button i.fa-retweet {
 | 
			
		||||
  height: 19px;
 | 
			
		||||
  width: 24px;
 | 
			
		||||
  background: image-url('boost_sprite.png') no-repeat;
 | 
			
		||||
  background-position: 0 0;
 | 
			
		||||
  transition: background-position 0.9s steps(11);
 | 
			
		||||
  transition-duration: 0s;
 | 
			
		||||
// Commented out until sprite matches non-sprite icon visually
 | 
			
		||||
// button i.fa-retweet {
 | 
			
		||||
//   height: 19px;
 | 
			
		||||
//   width: 24px;
 | 
			
		||||
//   background: image-url('boost_sprite.png') no-repeat;
 | 
			
		||||
//   background-position: 0 0;
 | 
			
		||||
//   transition: background-position 0.9s steps(11);
 | 
			
		||||
//   transition-duration: 0s;
 | 
			
		||||
 | 
			
		||||
  &::before {
 | 
			
		||||
    display: none !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
//   &::before {
 | 
			
		||||
//     display: none !important;
 | 
			
		||||
//   }
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
button.active i.fa-retweet {
 | 
			
		||||
  transition-duration: 0.9s;
 | 
			
		||||
  background-position: 0 -209px;
 | 
			
		||||
}
 | 
			
		||||
// button.active i.fa-retweet {
 | 
			
		||||
//   transition-duration: 0.9s;
 | 
			
		||||
//   background-position: 0 -209px;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,6 @@ class Api::V1::AppsController < ApiController
 | 
			
		||||
  respond_to :json
 | 
			
		||||
 | 
			
		||||
  def create
 | 
			
		||||
    @app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: (params[:scopes] || Doorkeeper.configuration.default_scopes))
 | 
			
		||||
    @app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: (params[:scopes] || Doorkeeper.configuration.default_scopes), website: params[:website])
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -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], visibility: params[:visibility])
 | 
			
		||||
    @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], visibility: params[:visibility], application: doorkeeper_token.application)
 | 
			
		||||
    render action: :show
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								app/lib/application_extension.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/lib/application_extension.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module ApplicationExtension
 | 
			
		||||
  extend ActiveSupport::Concern
 | 
			
		||||
 | 
			
		||||
  included do
 | 
			
		||||
    validates :website, url: true, unless: 'website.blank?'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										14
									
								
								app/lib/url_validator.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/lib/url_validator.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class UrlValidator < ActiveModel::EachValidator
 | 
			
		||||
  def validate_each(record, attribute, value)
 | 
			
		||||
    record.errors.add(attribute, I18n.t('applications.invalid_url')) unless compliant?(value)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def compliant?(url)
 | 
			
		||||
    parsed_url = Addressable::URI.parse(url)
 | 
			
		||||
    !parsed_url.nil? && %w(http https).include?(parsed_url.scheme) && parsed_url.host
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -7,6 +7,8 @@ class Status < ApplicationRecord
 | 
			
		||||
 | 
			
		||||
  enum visibility: [:public, :unlisted, :private], _suffix: :visibility
 | 
			
		||||
 | 
			
		||||
  belongs_to :application, class_name: 'Doorkeeper::Application'
 | 
			
		||||
 | 
			
		||||
  belongs_to :account, inverse_of: :statuses
 | 
			
		||||
  belongs_to :in_reply_to_account, foreign_key: 'in_reply_to_account_id', class_name: 'Account'
 | 
			
		||||
 | 
			
		||||
@ -33,7 +35,7 @@ class Status < ApplicationRecord
 | 
			
		||||
  scope :remote, -> { where.not(uri: nil) }
 | 
			
		||||
  scope :local, -> { where(uri: nil) }
 | 
			
		||||
 | 
			
		||||
  cache_associated :account, :media_attachments, :tags, :stream_entry, mentions: :account, reblog: [:account, :stream_entry, :tags, :media_attachments, mentions: :account], thread: :account
 | 
			
		||||
  cache_associated :account, :application, :media_attachments, :tags, :stream_entry, mentions: :account, reblog: [:account, :application, :stream_entry, :tags, :media_attachments, mentions: :account], thread: :account
 | 
			
		||||
 | 
			
		||||
  def local?
 | 
			
		||||
    uri.nil?
 | 
			
		||||
 | 
			
		||||
@ -7,10 +7,17 @@ class PostStatusService < BaseService
 | 
			
		||||
  # @param [Status] in_reply_to Optional status to reply to
 | 
			
		||||
  # @param [Hash] options
 | 
			
		||||
  # @option [Boolean] :sensitive
 | 
			
		||||
  # @option [String] :visibility
 | 
			
		||||
  # @option [Enumerable] :media_ids Optional array of media IDs to attach
 | 
			
		||||
  # @option [Doorkeeper::Application] :application
 | 
			
		||||
  # @return [Status]
 | 
			
		||||
  def call(account, text, in_reply_to = nil, options = {})
 | 
			
		||||
    status = account.statuses.create!(text: text, thread: in_reply_to, sensitive: options[:sensitive], visibility: options[:visibility])
 | 
			
		||||
    status = account.statuses.create!(text:        text,
 | 
			
		||||
                                      thread:      in_reply_to,
 | 
			
		||||
                                      sensitive:   options[:sensitive],
 | 
			
		||||
                                      visibility:  options[:visibility],
 | 
			
		||||
                                      application: options[:application])
 | 
			
		||||
 | 
			
		||||
    attach_media(status, options[:media_ids])
 | 
			
		||||
    process_mentions_service.call(status)
 | 
			
		||||
    process_hashtags_service.call(status)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								app/views/api/v1/apps/show.rabl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/views/api/v1/apps/show.rabl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
object @application
 | 
			
		||||
 | 
			
		||||
attributes :name, :website
 | 
			
		||||
@ -6,6 +6,10 @@ node(:url)              { |status| TagManager.instance.url_for(status) }
 | 
			
		||||
node(:reblogs_count)    { |status| defined?(@reblogs_counts_map)    ? (@reblogs_counts_map[status.id]    || 0) : status.reblogs.count }
 | 
			
		||||
node(:favourites_count) { |status| defined?(@favourites_counts_map) ? (@favourites_counts_map[status.id] || 0) : status.favourites.count }
 | 
			
		||||
 | 
			
		||||
child :application do
 | 
			
		||||
  extends 'api/v1/apps/show'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
child :account do
 | 
			
		||||
  extends 'api/v1/accounts/show'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -28,10 +28,16 @@
 | 
			
		||||
    = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: @external_links ? '_blank' : nil, rel: 'noopener' do
 | 
			
		||||
      %span= l(status.created_at)
 | 
			
		||||
    ·
 | 
			
		||||
    %span
 | 
			
		||||
    - if status.application
 | 
			
		||||
      - if status.application.website.blank?
 | 
			
		||||
        %strong.detailed-status__application= status.application.name
 | 
			
		||||
      - else
 | 
			
		||||
        = link_to status.application.name, status.application.website, class: 'detailed-status__application', target: '_blank', rel: 'noopener'
 | 
			
		||||
      ·
 | 
			
		||||
    %span<
 | 
			
		||||
      = fa_icon('retweet')
 | 
			
		||||
      %span= status.reblogs.count
 | 
			
		||||
    ·
 | 
			
		||||
    %span
 | 
			
		||||
    %span<
 | 
			
		||||
      = fa_icon('star')
 | 
			
		||||
      %span= status.favourites.count
 | 
			
		||||
 | 
			
		||||
@ -46,6 +46,7 @@ module Mastodon
 | 
			
		||||
 | 
			
		||||
    config.to_prepare do
 | 
			
		||||
      Doorkeeper::AuthorizationsController.layout 'public'
 | 
			
		||||
      Doorkeeper::Application.send :include, ApplicationExtension
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    config.action_dispatch.default_headers = {
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ en:
 | 
			
		||||
    domain_count_after: other instances
 | 
			
		||||
    domain_count_before: Connected to
 | 
			
		||||
    get_started: Get started
 | 
			
		||||
    learn_more: Learn more
 | 
			
		||||
    links: Links
 | 
			
		||||
    source_code: Source code
 | 
			
		||||
    status_count_after: statuses
 | 
			
		||||
@ -15,7 +16,6 @@ en:
 | 
			
		||||
    terms: Terms
 | 
			
		||||
    user_count_after: users
 | 
			
		||||
    user_count_before: Home to
 | 
			
		||||
    learn_more: Learn more
 | 
			
		||||
  accounts:
 | 
			
		||||
    follow: Follow
 | 
			
		||||
    followers: Followers
 | 
			
		||||
@ -28,6 +28,8 @@ en:
 | 
			
		||||
    unfollow: Unfollow
 | 
			
		||||
  application_mailer:
 | 
			
		||||
    signature: Mastodon notifications from %{instance}
 | 
			
		||||
  applications:
 | 
			
		||||
    invalid_url: The provided URL is invalid
 | 
			
		||||
  auth:
 | 
			
		||||
    change_password: Change password
 | 
			
		||||
    didnt_get_confirmation: Didn't receive confirmation instructions?
 | 
			
		||||
@ -88,9 +90,9 @@ en:
 | 
			
		||||
    proceed: Proceed to follow
 | 
			
		||||
    prompt: 'You are going to follow:'
 | 
			
		||||
  settings:
 | 
			
		||||
    back: Back to Mastodon
 | 
			
		||||
    edit_profile: Edit profile
 | 
			
		||||
    preferences: Preferences
 | 
			
		||||
    back: Back to Mastodon
 | 
			
		||||
  stream_entries:
 | 
			
		||||
    click_to_show: Click to show
 | 
			
		||||
    favourited: favourited a post by
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								db/migrate/20170114194937_add_application_to_statuses.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								db/migrate/20170114194937_add_application_to_statuses.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
class AddApplicationToStatuses < ActiveRecord::Migration[5.0]
 | 
			
		||||
  def change
 | 
			
		||||
    add_column :statuses, :application_id, :int
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
class AddWebsiteToOauthApplication < ActiveRecord::Migration[5.0]
 | 
			
		||||
  def change
 | 
			
		||||
    add_column :oauth_applications, :website, :string
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -10,7 +10,7 @@
 | 
			
		||||
#
 | 
			
		||||
# It's strongly recommended that you check this file into your version control system.
 | 
			
		||||
 | 
			
		||||
ActiveRecord::Schema.define(version: 20170112154826) do
 | 
			
		||||
ActiveRecord::Schema.define(version: 20170114203041) do
 | 
			
		||||
 | 
			
		||||
  # These are extensions that must be enabled in order to support this database
 | 
			
		||||
  enable_extension "plpgsql"
 | 
			
		||||
@ -153,6 +153,7 @@ ActiveRecord::Schema.define(version: 20170112154826) do
 | 
			
		||||
    t.datetime "created_at"
 | 
			
		||||
    t.datetime "updated_at"
 | 
			
		||||
    t.boolean  "superapp",     default: false, null: false
 | 
			
		||||
    t.string   "website"
 | 
			
		||||
    t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -259,6 +260,7 @@ ActiveRecord::Schema.define(version: 20170112154826) do
 | 
			
		||||
    t.boolean  "sensitive",              default: false
 | 
			
		||||
    t.integer  "visibility",             default: 0,     null: false
 | 
			
		||||
    t.integer  "in_reply_to_account_id"
 | 
			
		||||
    t.integer  "application_id"
 | 
			
		||||
    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 ["reblog_of_id"], name: "index_statuses_on_reblog_of_id", using: :btree
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
 | 
			
		||||
  render_views
 | 
			
		||||
 | 
			
		||||
  let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
 | 
			
		||||
  let(:token) { double acceptable?: true, resource_owner_id: user.id }
 | 
			
		||||
  let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
 | 
			
		||||
  let(:token) { double acceptable?: true, resource_owner_id: user.id, application: app }
 | 
			
		||||
 | 
			
		||||
  before do
 | 
			
		||||
    allow(controller).to receive(:doorkeeper_token) { token }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								spec/fabricators/application_fabricator.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/fabricators/application_fabricator.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
Fabricator(:application, from: Doorkeeper::Application) do
 | 
			
		||||
  name         'Example'
 | 
			
		||||
  website      'http://example.com'
 | 
			
		||||
  redirect_uri 'http://example.com/callback'
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user