Merge branch 'master' into skylight

This commit is contained in:
Eugen Rochko 2017-04-09 19:14:08 +02:00
commit 24656a9f8f
24 changed files with 176 additions and 114 deletions

37
Gemfile
View file

@ -21,37 +21,38 @@ gem 'paperclip', '~> 5.1'
gem 'paperclip-av-transcoder' gem 'paperclip-av-transcoder'
gem 'aws-sdk', '>= 2.0' gem 'aws-sdk', '>= 2.0'
gem 'http'
gem 'httplog'
gem 'addressable' gem 'addressable'
gem 'nokogiri'
gem 'link_header'
gem 'ostatus2'
gem 'goldfinger'
gem 'devise' gem 'devise'
gem 'devise-two-factor' gem 'devise-two-factor'
gem 'doorkeeper' gem 'doorkeeper'
gem 'rabl'
gem 'rqrcode'
gem 'twitter-text'
gem 'ox'
gem 'oj'
gem 'hiredis'
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
gem 'fast_blank' gem 'fast_blank'
gem 'goldfinger'
gem 'hiredis'
gem 'htmlentities' gem 'htmlentities'
gem 'simple_form' gem 'http'
gem 'will_paginate' gem 'http_accept_language'
gem 'httplog'
gem 'link_header'
gem 'nokogiri'
gem 'oj'
gem 'ostatus2'
gem 'ox'
gem 'rabl'
gem 'rack-attack' gem 'rack-attack'
gem 'rack-cors', require: 'rack/cors' gem 'rack-cors', require: 'rack/cors'
gem 'rack-timeout'
gem 'rails-settings-cached'
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
gem 'rqrcode'
gem 'ruby-oembed', require: 'oembed'
gem 'sidekiq' gem 'sidekiq'
gem 'sidekiq-unique-jobs' gem 'sidekiq-unique-jobs'
gem 'rails-settings-cached'
gem 'simple-navigation' gem 'simple-navigation'
gem 'simple_form'
gem 'statsd-instrument' gem 'statsd-instrument'
gem 'ruby-oembed', require: 'oembed' gem 'twitter-text'
gem 'rack-timeout'
gem 'tzinfo-data' gem 'tzinfo-data'
gem 'will_paginate'
gem 'react-rails' gem 'react-rails'
gem 'browserify-rails' gem 'browserify-rails'

View file

@ -184,6 +184,7 @@ GEM
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
http-form_data (1.0.1) http-form_data (1.0.1)
http_accept_language (2.1.0)
http_parser.rb (0.6.0) http_parser.rb (0.6.0)
httplog (0.3.2) httplog (0.3.2)
colorize colorize
@ -478,6 +479,7 @@ DEPENDENCIES
hiredis hiredis
htmlentities htmlentities
http http
http_accept_language
httplog httplog
i18n-tasks (~> 0.9.6) i18n-tasks (~> 0.9.6)
jquery-rails jquery-rails
@ -529,4 +531,4 @@ RUBY VERSION
ruby 2.3.1p112 ruby 2.3.1p112
BUNDLED WITH BUNDLED WITH
1.14.3 1.14.5

View file

@ -2,54 +2,71 @@ const pt = {
"column_back_button.label": "Voltar", "column_back_button.label": "Voltar",
"lightbox.close": "Fechar", "lightbox.close": "Fechar",
"loading_indicator.label": "Carregando...", "loading_indicator.label": "Carregando...",
"status.mention": "Menção", "status.mention": "Mencionar @{name}",
"status.delete": "Deletar", "status.delete": "Eliminar",
"status.reply": "Responder", "status.reply": "Responder",
"status.reblog": "Reblogar", "status.reblog": "Partilhar",
"status.favourite": "Favoritar", "status.favourite": "Adicionar aos favoritos",
"status.reblogged_by": "{name} reblogou", "status.reblogged_by": "{name} partilhou",
"video_player.toggle_sound": "Alterar som", "status.sensitive_warning": "Conteúdo sensível",
"account.mention": "Menção", "status.sensitive_toggle": "Clique para ver",
"status.show_more": "Mostrar mais",
"status.show_less": "Mostrar menos",
"status.open": "Expandir",
"status.report": "Reportar @{name}",
"video_player.toggle_sound": "Ligar/Desligar som",
"account.mention": "Mencionar @{name}",
"account.edit_profile": "Editar perfil", "account.edit_profile": "Editar perfil",
"account.unblock": "Desbloquear", "account.unblock": "Não bloquear @{name}",
"account.unfollow": "Unfollow", "account.unfollow": "Não seguir",
"account.block": "Bloquear", "account.block": "Bloquear @{name}",
"account.follow": "Seguir", "account.follow": "Seguir",
"account.block": "Bloquear",
"account.posts": "Posts", "account.posts": "Posts",
"account.follows": "Segue", "account.follows": "Segue",
"account.followers": "Seguidores", "account.followers": "Seguidores",
"account.follows_you": "Segue você", "account.follows_you": "É teu seguidor",
"account.requested": "A aguardar aprovação",
"getting_started.heading": "Primeiros passos", "getting_started.heading": "Primeiros passos",
"getting_started.about_addressing": "Podes seguir pessoas se sabes o nome de usuário deles e o domínio em que estão entrando um endereço similar a e-mail no campo no topo da barra lateral.", "getting_started.about_addressing": "Podes seguir pessoas se sabes o nome de usuário deles e o domínio em que estão colocando um endereço similar a e-mail no campo no topo da barra lateral.",
"getting_started.about_shortcuts": "Se o usuário alvo está no mesmo domínio, só o nome funcionará. A mesma regra se aplica a mencionar pessoas nas postagens.", "getting_started.about_shortcuts": "Se o usuário alvo está no mesmo domínio, só o nome funcionará. A mesma regra se aplica a mencionar pessoas nas postagens.",
"getting_started.about_developer": "O desenvolvedor desse projeto pode ser seguido em Gargron@mastodon.social", "getting_started.open_source_notice": "Mastodon é software de fonte aberta. Podes contribuir ou repostar problemas no GitHub do projecto: {github}. {apps}.",
"column.home": "Home", "column.home": "Home",
"column.mentions": "Menções", "column.community": "Local",
"column.public": "Público", "column.public": "Público",
"tabs_bar.compose": "Compôr", "column.notifications": "Notificações",
"tabs_bar.compose": "Criar",
"tabs_bar.home": "Home", "tabs_bar.home": "Home",
"tabs_bar.mentions": "Menções", "tabs_bar.mentions": "Menções",
"tabs_bar.public": "Público", "tabs_bar.public": "Público",
"tabs_bar.notifications": "Notificações", "tabs_bar.notifications": "Notificações",
"compose_form.placeholder": "Que estás pensando?", "compose_form.placeholder": "Em que estás a pensar?",
"compose_form.publish": "Publicar", "compose_form.publish": "Publicar",
"compose_form.sensitive": "Marcar conteúdo como sensível", "compose_form.sensitive": "Media com conteúdo sensível",
"compose_form.unlisted": "Modo não-listado", "compose_form.spoiler": "Esconder texto com aviso",
"compose_form.private": "Tornar privado",
"compose_form.privacy_disclaimer": "O teu conteúdo privado vai ser partilhado com os utilizadores do {domains}. Confias {domainsCount, plural, one {neste servidor} other {nestes servidores}}? A privacidade só funciona em instâncias do Mastodon. Se {domains} {domainsCount, plural, one {não é uma instância} other {não são instâncias}}, não existem indicadores da privacidade da tua partilha, e podem ser partilhados com outros.",
"compose_form.unlisted": "Não mostrar na listagem pública",
"navigation_bar.edit_profile": "Editar perfil", "navigation_bar.edit_profile": "Editar perfil",
"navigation_bar.preferences": "Preferências", "navigation_bar.preferences": "Preferências",
"navigation_bar.public_timeline": "Timeline Pública", "navigation_bar.community_timeline": "Local",
"navigation_bar.logout": "Logout", "navigation_bar.public_timeline": "Público",
"navigation_bar.logout": "Sair",
"reply_indicator.cancel": "Cancelar", "reply_indicator.cancel": "Cancelar",
"search.placeholder": "Busca", "search.placeholder": "Pesquisar",
"search.account": "Conta", "search.account": "Conta",
"search.hashtag": "Hashtag", "search.hashtag": "Hashtag",
"upload_button.label": "Adicionar media", "upload_button.label": "Adicionar media",
"upload_form.undo": "Desfazer", "upload_form.undo": "Anular",
"notification.follow": "{name} seguiu você", "notification.follow": "{name} seguiu-te",
"notification.favourite": "{name} favoritou seu post", "notification.favourite": "{name} adicionou o teu post aos favoritos",
"notification.reblog": "{name} reblogou o seu post", "notification.reblog": "{name} partilhou o teu post",
"notification.mention": "{name} mecionou você" "notification.mention": "{name} mencionou-te",
"notifications.column_settings.alert": "Notificações no computador",
"notifications.column_settings.show": "Mostrar nas colunas",
"notifications.column_settings.follow": "Novos seguidores:",
"notifications.column_settings.favourite": "Favoritos:",
"notifications.column_settings.mention": "Menções:",
"notifications.column_settings.reblog": "Partilhas:",
}; };
export default pt; export default pt;

View file

@ -1,10 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::AccountsController < ApiController class Api::V1::AccountsController < ApiController
before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute] before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute, :update_credentials]
before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock, :mute, :unmute] before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
before_action -> { doorkeeper_authorize! :write }, only: [:update_credentials]
before_action :require_user!, except: [:show, :following, :followers, :statuses] before_action :require_user!, except: [:show, :following, :followers, :statuses]
before_action :set_account, except: [:verify_credentials, :suggestions, :search] before_action :set_account, except: [:verify_credentials, :update_credentials, :suggestions, :search]
respond_to :json respond_to :json
@ -15,6 +16,14 @@ class Api::V1::AccountsController < ApiController
render action: :show render action: :show
end end
def update_credentials
@account = current_user.account
@account.update_attributes!(account_params)
render action: :show
end
def following def following
results = Follow.where(account: @account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) results = Follow.where(account: @account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
@ -135,4 +144,8 @@ class Api::V1::AccountsController < ApiController
def statuses_pagination_params(core_params) def statuses_pagination_params(core_params)
params.permit(:limit, :only_media, :exclude_replies).merge(core_params) params.permit(:limit, :only_media, :exclude_replies).merge(core_params)
end end
def account_params
@account_params ||= params.permit(:display_name, :note, :avatar, :header)
end
end end

View file

@ -26,6 +26,8 @@ module Localized
end end
def default_locale def default_locale
ENV.fetch('DEFAULT_LOCALE') { I18n.default_locale } ENV.fetch('DEFAULT_LOCALE') {
http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
}
end end
end end

View file

@ -1,4 +0,0 @@
# frozen_string_literal: true
module AboutHelper
end

View file

@ -1,4 +0,0 @@
# frozen_string_literal: true
module Admin::DomainBlocksHelper
end

View file

@ -1,4 +0,0 @@
# frozen_string_literal: true
module Admin::PubsubhubbubHelper
end

View file

@ -1,4 +0,0 @@
# frozen_string_literal: true
module AuthorizeFollowHelper
end

View file

@ -1,4 +0,0 @@
# frozen_string_literal: true
module TagsHelper
end

View file

@ -1,4 +0,0 @@
# frozen_string_literal: true
module XrdHelper
end

View file

@ -19,11 +19,16 @@ class FetchRemoteAccountService < BaseService
xml = Nokogiri::XML(body) xml = Nokogiri::XML(body)
xml.encoding = 'utf-8' xml.encoding = 'utf-8'
url_parts = Addressable::URI.parse(url) email = xml.at_xpath('//xmlns:author/xmlns:email').try(:content)
username = xml.at_xpath('//xmlns:author/xmlns:name').try(:content) if email.nil?
domain = url_parts.host url_parts = Addressable::URI.parse(url)
username = xml.at_xpath('//xmlns:author/xmlns:name').try(:content)
domain = url_parts.host
else
username, domain = email.split('@')
end
return nil if username.nil? return nil if username.nil? || domain.nil?
Rails.logger.debug "Going to webfinger #{username}@#{domain}" Rails.logger.debug "Going to webfinger #{username}@#{domain}"

View file

@ -163,6 +163,7 @@ Rails.application.routes.draw do
collection do collection do
get :relationships get :relationships
get :verify_credentials get :verify_credentials
patch :update_credentials
get :search get :search
end end

View file

@ -1,4 +1,11 @@
# config/app.yml for rails-settings-cached # config/app.yml for rails-settings-cached
#
# This file contains default values, and does not need to be edited
# when configuring an instance. These settings may be changed by an
# Administrator using the Web UI.
#
# For more information, see docs/Running-Mastodon/Administration-guide.md
#
defaults: &defaults defaults: &defaults
site_title: 'Mastodon' site_title: 'Mastodon'
site_description: '' site_description: ''

View file

@ -30,7 +30,7 @@ API overview
- [Instance](#instance) - [Instance](#instance)
- [Mention](#mention) - [Mention](#mention)
- [Notification](#notification) - [Notification](#notification)
- [Relationships](#relationships) - [Relationship](#relationship)
- [Results](#results) - [Results](#results)
- [Status](#status) - [Status](#status)
- [Tag](#tag) - [Tag](#tag)
@ -85,6 +85,17 @@ Returns an [Account](#account).
Returns the authenticated user's [Account](#account). Returns the authenticated user's [Account](#account).
#### Updating the current user:
PATCH /api/v1/accounts/update_credentials
Form data:
- `display_name`: The name to display in the user's profile
- `note`: A new biography for the user
- `avatar`: A base64 encoded image to display as the user's avatar (e.g. `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADrCAYAAAA...`)
- `header`: A base64 encoded image to display as the user's header image (e.g. `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADrCAYAAAA...`)
#### Getting an account's followers: #### Getting an account's followers:
GET /api/v1/accounts/:id/followers GET /api/v1/accounts/:id/followers
@ -456,7 +467,7 @@ ___
| `acct` | Equals `username` for local users, includes `@domain` for remote ones | | `acct` | Equals `username` for local users, includes `@domain` for remote ones |
| `id` | Account ID | | `id` | Account ID |
### Notifications ### Notification
| Attribute | Description | | Attribute | Description |
| ------------------------ | ----------- | | ------------------------ | ----------- |
@ -464,9 +475,9 @@ ___
| `type` | One of: "mention", "reblog", "favourite", "follow" | | `type` | One of: "mention", "reblog", "favourite", "follow" |
| `created_at` | The time the notification was created | | `created_at` | The time the notification was created |
| `account` | The [Account](#account) sending the notification to the user | | `account` | The [Account](#account) sending the notification to the user |
| `status` | The [Status](#status) associated with the notification, if applicible | | `status` | The [Status](#status) associated with the notification, if applicable |
### Relationships ### Relationship
| Attribute | Description | | Attribute | Description |
| ------------------------ | ----------- | | ------------------------ | ----------- |
@ -516,7 +527,7 @@ ___
| `tags` | An array of [Tags](#tag) | | `tags` | An array of [Tags](#tag) |
| `application` | [Application](#application) from which the status was posted | | `application` | [Application](#application) from which the status was posted |
### Tags ### Tag
| Attribute | Description | | Attribute | Description |
| ------------------------ | ----------- | | ------------------------ | ----------- |

View file

@ -24,6 +24,39 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
end end
end end
describe 'PATCH #update_credentials' do
it 'returns http success' do
expect(user.account.avatar).not_to exist
expect(user.account.header).not_to exist
avatar = File.read(Rails.root.join('app', 'assets', 'images', 'logo.png'))
header = File.read(Rails.root.join('app', 'assets', 'images', 'mastodon-getting-started.png'))
params = {
display_name: "Alice Isn't Dead",
note: "Hi!\n\nToot toot!",
avatar: "data:image/png;base64,#{Base64.encode64(avatar)}",
header: "data:image/png;base64,#{Base64.encode64(header)}"
}
patch :update_credentials, params: params
expect(response).to have_http_status(:success)
user.reload
expect(user.account.display_name).to eq("Alice Isn't Dead")
expect(user.account.note).to eq("Hi!\n\nToot toot!")
expect(user.account.avatar).to exist
expect(user.account.header).to exist
end
it 'respects Account validations' do
note = "This is too long. " * 10
error = { error: 'The account could not be updated: Note is too long (maximum is 160 characters)' }.to_json
patch :update_credentials, params: { note: note }
expect(response).to have_http_status(:unprocessable_entity)
expect(response.body).to eq(error)
end
end
describe 'GET #statuses' do describe 'GET #statuses' do
it 'returns http success' do it 'returns http success' do
get :statuses, params: { id: user.account.id } get :statuses, params: { id: user.account.id }

View file

@ -1,5 +0,0 @@
require 'rails_helper'
RSpec.describe AboutHelper, type: :helper do
end

View file

@ -1,5 +0,0 @@
require 'rails_helper'
RSpec.describe Admin::DomainBlocksHelper, type: :helper do
end

View file

@ -1,5 +0,0 @@
require 'rails_helper'
RSpec.describe Admin::PubsubhubbubHelper, type: :helper do
end

View file

@ -1,5 +1,19 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe ApplicationHelper, type: :helper do describe ApplicationHelper do
describe 'active_nav_class' do
it 'returns active when on the current page' do
allow(helper).to receive(:current_page?).and_return(true)
result = helper.active_nav_class("/test")
expect(result).to eq "active"
end
it 'returns empty string when not on current page' do
allow(helper).to receive(:current_page?).and_return(false)
result = helper.active_nav_class("/test")
expect(result).to eq ""
end
end
end end

View file

@ -1,5 +0,0 @@
require 'rails_helper'
RSpec.describe AuthorizeFollowHelper, type: :helper do
end

View file

@ -2,7 +2,17 @@ require 'rails_helper'
RSpec.describe StreamEntriesHelper, type: :helper do RSpec.describe StreamEntriesHelper, type: :helper do
describe '#display_name' do describe '#display_name' do
pending it 'uses the display name when it exists' do
account = Account.new(display_name: "Display", username: "Username")
expect(helper.display_name(account)).to eq "Display"
end
it 'uses the username when display name is nil' do
account = Account.new(display_name: nil, username: "Username")
expect(helper.display_name(account)).to eq "Username"
end
end end
describe '#avatar_for_status_url' do describe '#avatar_for_status_url' do

View file

@ -1,5 +0,0 @@
require 'rails_helper'
RSpec.describe TagsHelper, type: :helper do
end

View file

@ -1,5 +0,0 @@
require 'rails_helper'
RSpec.describe XrdHelper, type: :helper do
end