From 4ec1771165ab8dd40e52804fd087eacfab25290b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 28 Sep 2017 15:31:31 +0200 Subject: Add ability to specify alternative text for media attachments (#5123) * Fix #117 - Add ability to specify alternative text for media attachments - POST /api/v1/media accepts `description` straight away - PUT /api/v1/media/:id to update `description` (only for unattached ones) - Serialized as `name` of Document object in ActivityPub - Uploads form adjusted for better performance and description input * Add tests * Change undo button blend mode to difference --- spec/controllers/api/v1/media_controller_spec.rb | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'spec/controllers') diff --git a/spec/controllers/api/v1/media_controller_spec.rb b/spec/controllers/api/v1/media_controller_spec.rb index baa22d7e4..0e494638f 100644 --- a/spec/controllers/api/v1/media_controller_spec.rb +++ b/spec/controllers/api/v1/media_controller_spec.rb @@ -101,4 +101,33 @@ RSpec.describe Api::V1::MediaController, type: :controller do end end end + + describe 'PUT #update' do + context 'when somebody else\'s' do + let(:media) { Fabricate(:media_attachment, status: nil) } + + it 'returns http not found' do + put :update, params: { id: media.id, description: 'Lorem ipsum!!!' } + expect(response).to have_http_status(:not_found) + end + end + + context 'when not attached to a status' do + let(:media) { Fabricate(:media_attachment, status: nil, account: user.account) } + + it 'updates the description' do + put :update, params: { id: media.id, description: 'Lorem ipsum!!!' } + expect(media.reload.description).to eq 'Lorem ipsum!!!' + end + end + + context 'when attached to a status' do + let(:media) { Fabricate(:media_attachment, status: Fabricate(:status), account: user.account) } + + it 'returns http not found' do + put :update, params: { id: media.id, description: 'Lorem ipsum!!!' } + expect(response).to have_http_status(:not_found) + end + end + end end -- cgit From eb605141ffb95290c5a537802ea418e6e45bf95f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 30 Sep 2017 22:05:42 +0200 Subject: Fix #5104 - GET /api/v1/apps/verify_credentials to confirm app works (#5112) --- .../api/v1/apps/credentials_controller.rb | 11 ++++++ app/controllers/api/v1/apps_controller.rb | 2 - config/routes.rb | 7 +++- .../api/v1/apps/credentials_controller_spec.rb | 43 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 app/controllers/api/v1/apps/credentials_controller.rb create mode 100644 spec/controllers/api/v1/apps/credentials_controller_spec.rb (limited to 'spec/controllers') diff --git a/app/controllers/api/v1/apps/credentials_controller.rb b/app/controllers/api/v1/apps/credentials_controller.rb new file mode 100644 index 000000000..e469c7d21 --- /dev/null +++ b/app/controllers/api/v1/apps/credentials_controller.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Api::V1::Apps::CredentialsController < Api::BaseController + before_action -> { doorkeeper_authorize! :read } + + respond_to :json + + def show + render json: doorkeeper_token.application, serializer: REST::StatusSerializer::ApplicationSerializer + end +end diff --git a/app/controllers/api/v1/apps_controller.rb b/app/controllers/api/v1/apps_controller.rb index 44a27b20a..e9f7a7291 100644 --- a/app/controllers/api/v1/apps_controller.rb +++ b/app/controllers/api/v1/apps_controller.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class Api::V1::AppsController < Api::BaseController - respond_to :json - def create @app = Doorkeeper::Application.create!(application_options) render json: @app, serializer: REST::ApplicationSerializer diff --git a/config/routes.rb b/config/routes.rb index ad2d8fca2..de3c1e0f9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -194,12 +194,17 @@ Rails.application.routes.draw do resources :follows, only: [:create] resources :media, only: [:create, :update] - resources :apps, only: [:create] resources :blocks, only: [:index] resources :mutes, only: [:index] resources :favourites, only: [:index] resources :reports, only: [:index, :create] + namespace :apps do + get :verify_credentials, to: 'credentials#show' + end + + resources :apps, only: [:create] + resource :instance, only: [:show] resource :domain_blocks, only: [:show, :create, :destroy] diff --git a/spec/controllers/api/v1/apps/credentials_controller_spec.rb b/spec/controllers/api/v1/apps/credentials_controller_spec.rb new file mode 100644 index 000000000..38f2a4e10 --- /dev/null +++ b/spec/controllers/api/v1/apps/credentials_controller_spec.rb @@ -0,0 +1,43 @@ +require 'rails_helper' + +describe Api::V1::Apps::CredentialsController do + render_views + + let(:token) { Fabricate(:accessible_access_token, scopes: 'read', application: Fabricate(:application)) } + + context 'with an oauth token' do + before do + allow(controller).to receive(:doorkeeper_token) { token } + end + + describe 'GET #show' do + before do + get :show + end + + it 'returns http success' do + expect(response).to have_http_status(:success) + end + + it 'does not contain client credentials' do + json = body_as_json + + expect(json).to_not have_key(:client_secret) + expect(json).to_not have_key(:client_id) + end + end + end + + context 'without an oauth token' do + before do + allow(controller).to receive(:doorkeeper_token) { nil } + end + + describe 'GET #show' do + it 'returns http unauthorized' do + get :show + expect(response).to have_http_status(:unauthorized) + end + end + end +end -- cgit From cdacac8c6cbd85ed6e8a1cac8ce6fa5994094c7c Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 1 Oct 2017 06:06:09 +0900 Subject: Fix order of paginated accounts in FollowerDomainsController and spec (#3357) * Fix order of paginated accounts in FollowerDomainsController Unordered pagination could result in unexpected behavior. * Cover Settings::FollowerDomainsController more --- .../settings/follower_domains_controller.rb | 2 +- .../settings/follower_domains_controller_spec.rb | 67 +++++++++++++++++++--- 2 files changed, 59 insertions(+), 10 deletions(-) (limited to 'spec/controllers') diff --git a/app/controllers/settings/follower_domains_controller.rb b/app/controllers/settings/follower_domains_controller.rb index 90b48887f..9968504e5 100644 --- a/app/controllers/settings/follower_domains_controller.rb +++ b/app/controllers/settings/follower_domains_controller.rb @@ -9,7 +9,7 @@ class Settings::FollowerDomainsController < ApplicationController def show @account = current_account - @domains = current_account.followers.reorder(nil).group('accounts.domain').select('accounts.domain, count(accounts.id) as accounts_from_domain').page(params[:page]).per(10) + @domains = current_account.followers.reorder('MIN(follows.id) DESC').group('accounts.domain').select('accounts.domain, count(accounts.id) as accounts_from_domain').page(params[:page]).per(10) end def update diff --git a/spec/controllers/settings/follower_domains_controller_spec.rb b/spec/controllers/settings/follower_domains_controller_spec.rb index d48c3e68c..333223c61 100644 --- a/spec/controllers/settings/follower_domains_controller_spec.rb +++ b/spec/controllers/settings/follower_domains_controller_spec.rb @@ -5,15 +5,41 @@ describe Settings::FollowerDomainsController do let(:user) { Fabricate(:user) } - before do - sign_in user, scope: :user + shared_examples 'authenticate user' do + it 'redirects when not signed in' do + is_expected.to redirect_to '/auth/sign_in' + end end describe 'GET #show' do + subject { get :show, params: { page: 2 } } + + it 'assigns @account' do + sign_in user, scope: :user + subject + expect(assigns(:account)).to eq user.account + end + + it 'assigns @domains' do + Fabricate(:account, domain: 'old').follow!(user.account) + Fabricate(:account, domain: 'recent').follow!(user.account) + + sign_in user, scope: :user + subject + + assigned = assigns(:domains).per(1).to_a + expect(assigned.size).to eq 1 + expect(assigned[0].accounts_from_domain).to eq 1 + expect(assigned[0].domain).to eq 'old' + end + it 'returns http success' do - get :show + sign_in user, scope: :user + subject expect(response).to have_http_status(:success) end + + include_examples 'authenticate user' end describe 'PATCH #update' do @@ -21,16 +47,39 @@ describe Settings::FollowerDomainsController do before do stub_request(:post, 'http://example.com/salmon').to_return(status: 200) - poopfeast.follow!(user.account) - patch :update, params: { select: ['example.com'] } end - it 'redirects back to followers page' do - expect(response).to redirect_to(settings_follower_domains_path) + shared_examples 'redirects back to followers page' do |notice| + it 'redirects back to followers page' do + poopfeast.follow!(user.account) + + sign_in user, scope: :user + subject + + expect(flash[:notice]).to eq notice + expect(response).to redirect_to(settings_follower_domains_path) + end + end + + context 'when select parameter is not provided' do + subject { patch :update } + include_examples 'redirects back to followers page', 'In the process of soft-blocking followers from 0 domains...' end - it 'soft-blocks followers from selected domains' do - expect(poopfeast.following?(user.account)).to be false + context 'when select parameter is provided' do + subject { patch :update, params: { select: ['example.com'] } } + + it 'soft-blocks followers from selected domains' do + poopfeast.follow!(user.account) + + sign_in user, scope: :user + subject + + expect(poopfeast.following?(user.account)).to be false + end + + include_examples 'authenticate user' + include_examples 'redirects back to followers page', 'In the process of soft-blocking followers from one domain...' end end end -- cgit From 47ecd652d3f8256a191401f005d42760e858e6de Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 2 Oct 2017 01:23:32 +0200 Subject: Make Chrome splash screen same color as web UI's background color (#5169) --- app/controllers/manifests_controller.rb | 8 ++--- app/serializers/manifest_serializer.rb | 52 +++++++++++++++++++++++++++ app/views/manifests/show.json.rabl | 11 ------ spec/controllers/manifests_controller_spec.rb | 4 --- 4 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 app/serializers/manifest_serializer.rb delete mode 100644 app/views/manifests/show.json.rabl (limited to 'spec/controllers') diff --git a/app/controllers/manifests_controller.rb b/app/controllers/manifests_controller.rb index 832e1eb6f..ac267c229 100644 --- a/app/controllers/manifests_controller.rb +++ b/app/controllers/manifests_controller.rb @@ -1,11 +1,7 @@ # frozen_string_literal: true class ManifestsController < ApplicationController - before_action :set_instance_presenter - - def show; end - - def set_instance_presenter - @instance_presenter = InstancePresenter.new + def show + render json: InstancePresenter.new, serializer: ManifestSerializer end end diff --git a/app/serializers/manifest_serializer.rb b/app/serializers/manifest_serializer.rb new file mode 100644 index 000000000..95bcc21bb --- /dev/null +++ b/app/serializers/manifest_serializer.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class ManifestSerializer < ActiveModel::Serializer + include RoutingHelper + include ActionView::Helpers::TextHelper + + attributes :name, :short_name, :description, + :icons, :theme_color, :background_color, + :display, :start_url, :scope + + def name + object.site_title + end + + def short_name + object.site_title + end + + def description + strip_tags(object.site_description.presence || I18n.t('about.about_mastodon_html')) + end + + def icons + [ + { + src: '/android-chrome-192x192.png', + sizes: '192x192', + type: 'image/png', + }, + ] + end + + def theme_color + '#282c37' + end + + def background_color + '#191b22' + end + + def display + 'standalone' + end + + def start_url + '/web/timelines/home' + end + + def scope + root_url + end +end diff --git a/app/views/manifests/show.json.rabl b/app/views/manifests/show.json.rabl deleted file mode 100644 index ee0a70324..000000000 --- a/app/views/manifests/show.json.rabl +++ /dev/null @@ -1,11 +0,0 @@ -object false - -node(:name) { Setting.site_title } -node(:short_name) { Setting.site_title } -node(:description) { strip_tags(Setting.site_description.presence || I18n.t('about.about_mastodon_html')) } -node(:icons) { [{ src: '/android-chrome-192x192.png', sizes: '192x192', type: 'image/png' }] } -node(:theme_color) { '#282c37' } -node(:background_color) { '#d9e1e8' } -node(:display) { 'standalone' } -node(:start_url) { '/web/timelines/home' } -node(:scope) { root_url } diff --git a/spec/controllers/manifests_controller_spec.rb b/spec/controllers/manifests_controller_spec.rb index 6f188fa35..71967e4f0 100644 --- a/spec/controllers/manifests_controller_spec.rb +++ b/spec/controllers/manifests_controller_spec.rb @@ -8,10 +8,6 @@ describe ManifestsController do get :show, format: :json end - it 'assigns @instance_presenter' do - expect(assigns(:instance_presenter)).to be_kind_of InstancePresenter - end - it 'returns http success' do expect(response).to have_http_status(:success) end -- cgit From dfaa219f8820224d37cd060d253a507111c63460 Mon Sep 17 00:00:00 2001 From: ThibG Date: Tue, 3 Oct 2017 23:21:19 +0200 Subject: Fix HTTP responses for salmon and ActivityPub inbox processing (#5200) * Return sensible HTTP status for ActivityPub inbox processing * Return sensible HTTP status for salmon slap processing * Return additional information to debug signature verification failures --- app/controllers/activitypub/inboxes_controller.rb | 4 ++-- app/controllers/api/salmon_controller.rb | 6 ++++-- app/controllers/concerns/signature_verification.rb | 9 +++++++++ spec/controllers/api/salmon_controller_spec.rb | 4 ++-- 4 files changed, 17 insertions(+), 6 deletions(-) (limited to 'spec/controllers') diff --git a/app/controllers/activitypub/inboxes_controller.rb b/app/controllers/activitypub/inboxes_controller.rb index d0f8073ed..76553a162 100644 --- a/app/controllers/activitypub/inboxes_controller.rb +++ b/app/controllers/activitypub/inboxes_controller.rb @@ -9,9 +9,9 @@ class ActivityPub::InboxesController < Api::BaseController if signed_request_account upgrade_account process_payload - head 201 - else head 202 + else + [signature_verification_failure_reason, 401] end end diff --git a/app/controllers/api/salmon_controller.rb b/app/controllers/api/salmon_controller.rb index e9e700b18..143e9d3cd 100644 --- a/app/controllers/api/salmon_controller.rb +++ b/app/controllers/api/salmon_controller.rb @@ -7,9 +7,11 @@ class Api::SalmonController < Api::BaseController def update if verify_payload? process_salmon - head 201 - else head 202 + elsif payload.present? + [signature_verification_failure_reason, 401] + else + head 400 end end diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb index 52a9cf290..dc2d9a678 100644 --- a/app/controllers/concerns/signature_verification.rb +++ b/app/controllers/concerns/signature_verification.rb @@ -9,10 +9,15 @@ module SignatureVerification request.headers['Signature'].present? end + def signature_verification_failure_reason + return @signature_verification_failure_reason if defined?(@signature_verification_failure_reason) + end + def signed_request_account return @signed_request_account if defined?(@signed_request_account) unless signed_request? + @signature_verification_failure_reason = 'Request not signed' @signed_request_account = nil return end @@ -27,6 +32,7 @@ module SignatureVerification end if incompatible_signature?(signature_params) + @signature_verification_failure_reason = 'Incompatible request signature' @signed_request_account = nil return end @@ -34,6 +40,7 @@ module SignatureVerification account = account_from_key_id(signature_params['keyId']) if account.nil? + @signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}" @signed_request_account = nil return end @@ -51,9 +58,11 @@ module SignatureVerification @signed_request_account = account @signed_request_account else + @signed_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri}" @signed_request_account = nil end else + @signed_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri}" @signed_request_account = nil end end diff --git a/spec/controllers/api/salmon_controller_spec.rb b/spec/controllers/api/salmon_controller_spec.rb index 3e4686200..323d85b61 100644 --- a/spec/controllers/api/salmon_controller_spec.rb +++ b/spec/controllers/api/salmon_controller_spec.rb @@ -46,8 +46,8 @@ RSpec.describe Api::SalmonController, type: :controller do post :update, params: { id: account.id } end - it 'returns http success' do - expect(response).to have_http_status(202) + it 'returns http client error' do + expect(response).to have_http_status(400) end end end -- cgit From 178f718a9b1cab57fbd9df511abe56533f12e129 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Wed, 4 Oct 2017 17:22:52 +0900 Subject: Separate notifications preferences from general preferences (#4447) * Separate notifications preferences from general preferences * Refine settings/notifications/show * remove preferences.notifications --- .../settings/notifications_controller.rb | 32 +++++++++++++++++++ app/lib/user_settings_decorator.rb | 26 ++++++++------- app/views/settings/notifications/show.html.haml | 25 +++++++++++++++ app/views/settings/preferences/show.html.haml | 19 ----------- config/locales/de.yml | 2 +- config/locales/en.yml | 2 +- config/locales/ja.yml | 2 +- config/locales/ko.yml | 2 +- config/locales/oc.yml | 2 +- config/locales/pl.yml | 2 +- config/navigation.rb | 1 + config/routes.rb | 1 + .../settings/notifications_controller_spec.rb | 37 ++++++++++++++++++++++ .../settings/preferences_controller_spec.rb | 6 ---- 14 files changed, 117 insertions(+), 42 deletions(-) create mode 100644 app/controllers/settings/notifications_controller.rb create mode 100644 app/views/settings/notifications/show.html.haml create mode 100644 spec/controllers/settings/notifications_controller_spec.rb (limited to 'spec/controllers') diff --git a/app/controllers/settings/notifications_controller.rb b/app/controllers/settings/notifications_controller.rb new file mode 100644 index 000000000..09839f16e --- /dev/null +++ b/app/controllers/settings/notifications_controller.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class Settings::NotificationsController < ApplicationController + layout 'admin' + + before_action :authenticate_user! + + def show; end + + def update + user_settings.update(user_settings_params.to_h) + + if current_user.save + redirect_to settings_notifications_path, notice: I18n.t('generic.changes_saved_msg') + else + render :show + end + end + + private + + def user_settings + UserSettingsDecorator.new(current_user) + end + + def user_settings_params + params.require(:user).permit( + notification_emails: %i(follow follow_request reblog favourite mention digest), + interactions: %i(must_be_follower must_be_following) + ) + end +end diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index cb1b3c4a9..1053ec488 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -15,17 +15,17 @@ class UserSettingsDecorator private def process_update - user.settings['notification_emails'] = merged_notification_emails - user.settings['interactions'] = merged_interactions - user.settings['default_privacy'] = default_privacy_preference - user.settings['default_sensitive'] = default_sensitive_preference - user.settings['unfollow_modal'] = unfollow_modal_preference - user.settings['boost_modal'] = boost_modal_preference - user.settings['delete_modal'] = delete_modal_preference - user.settings['auto_play_gif'] = auto_play_gif_preference - user.settings['system_font_ui'] = system_font_ui_preference - user.settings['noindex'] = noindex_preference - user.settings['theme'] = theme_preference + user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails') + user.settings['interactions'] = merged_interactions if change?('interactions') + user.settings['default_privacy'] = default_privacy_preference if change?('setting_default_privacy') + user.settings['default_sensitive'] = default_sensitive_preference if change?('setting_default_sensitive') + user.settings['unfollow_modal'] = unfollow_modal_preference if change?('setting_unfollow_modal') + user.settings['boost_modal'] = boost_modal_preference if change?('setting_boost_modal') + user.settings['delete_modal'] = delete_modal_preference if change?('setting_delete_modal') + user.settings['auto_play_gif'] = auto_play_gif_preference if change?('setting_auto_play_gif') + user.settings['system_font_ui'] = system_font_ui_preference if change?('setting_system_font_ui') + user.settings['noindex'] = noindex_preference if change?('setting_noindex') + user.settings['theme'] = theme_preference if change?('theme') end def merged_notification_emails @@ -83,4 +83,8 @@ class UserSettingsDecorator def coerce_values(params_hash) params_hash.transform_values { |x| x == '1' } end + + def change?(key) + !settings[key].nil? + end end diff --git a/app/views/settings/notifications/show.html.haml b/app/views/settings/notifications/show.html.haml new file mode 100644 index 000000000..80cd615c7 --- /dev/null +++ b/app/views/settings/notifications/show.html.haml @@ -0,0 +1,25 @@ +- content_for :page_title do + = t('settings.notifications') + += simple_form_for current_user, url: settings_notifications_path, html: { method: :put } do |f| + = render 'shared/error_messages', object: current_user + + .fields-group + = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| + = ff.input :follow, as: :boolean, wrapper: :with_label + = ff.input :follow_request, as: :boolean, wrapper: :with_label + = ff.input :reblog, as: :boolean, wrapper: :with_label + = ff.input :favourite, as: :boolean, wrapper: :with_label + = ff.input :mention, as: :boolean, wrapper: :with_label + + .fields-group + = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| + = ff.input :digest, as: :boolean, wrapper: :with_label + + .fields-group + = f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff| + = ff.input :must_be_follower, as: :boolean, wrapper: :with_label + = ff.input :must_be_following, as: :boolean, wrapper: :with_label + + .actions + = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index ffb1bbf6a..7475e3fd2 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -18,25 +18,6 @@ = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label - %h4= t 'preferences.notifications' - - .fields-group - = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| - = ff.input :follow, as: :boolean, wrapper: :with_label - = ff.input :follow_request, as: :boolean, wrapper: :with_label - = ff.input :reblog, as: :boolean, wrapper: :with_label - = ff.input :favourite, as: :boolean, wrapper: :with_label - = ff.input :mention, as: :boolean, wrapper: :with_label - - .fields-group - = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| - = ff.input :digest, as: :boolean, wrapper: :with_label - - .fields-group - = f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff| - = ff.input :must_be_follower, as: :boolean, wrapper: :with_label - = ff.input :must_be_following, as: :boolean, wrapper: :with_label - %h4= t 'preferences.other' .fields-group diff --git a/config/locales/de.yml b/config/locales/de.yml index dce86409b..d4a925d23 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -319,7 +319,6 @@ de: truncate: "…" preferences: languages: Sprachen - notifications: Benachrichtigungen other: Weiteres publishing: Beiträge web: Web @@ -390,6 +389,7 @@ de: export: Datenexport followers: Autorisierte Folgende import: Datenimport + notifications: Benachrichtigungen preferences: Einstellungen settings: Einstellungen two_factor_authentication: Zwei-Faktor-Authentisierung diff --git a/config/locales/en.yml b/config/locales/en.yml index 3049e0365..4a6df8cb2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -395,7 +395,6 @@ en: truncate: "…" preferences: languages: Languages - notifications: Notifications other: Other publishing: Publishing web: Web @@ -466,6 +465,7 @@ en: export: Data export followers: Authorized followers import: Import + notifications: Notifications preferences: Preferences settings: Settings two_factor_authentication: Two-factor Authentication diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 78465e121..d637a99ea 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -395,7 +395,6 @@ ja: truncate: "…" preferences: languages: 言語 - notifications: 通知 other: その他 publishing: 投稿 web: ウェブ @@ -466,6 +465,7 @@ ja: export: データのエクスポート followers: 信頼済みのインスタンス import: データのインポート + notifications: 通知 preferences: ユーザー設定 settings: 設定 two_factor_authentication: 二段階認証 diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 3a7636dbb..73f3f3a37 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -393,7 +393,6 @@ ko: truncate: "…" preferences: languages: 언어 - notifications: 알림 other: 기타 publishing: 퍼블리싱 web: 웹 @@ -464,6 +463,7 @@ ko: export: 데이터 내보내기 followers: 신뢰 중인 인스턴스 import: 데이터 가져오기 + notifications: 알림 preferences: 사용자 설정 settings: 설정 two_factor_authentication: 2단계 인증 diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 0b53b6b2d..1f25525a0 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -473,7 +473,6 @@ oc: truncate: "…" preferences: languages: Lengas - notifications: Notificacions other: Autre publishing: Publicar web: Interfàcia Web @@ -544,6 +543,7 @@ oc: export: Export donadas followers: Seguidors autorizats import: Importar + notifications: Notificacions preferences: Preferéncias settings: Paramètres two_factor_authentication: Autentificacion en dos temps diff --git a/config/locales/pl.yml b/config/locales/pl.yml index d49ecfbe6..26a8a9c69 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -396,7 +396,6 @@ pl: truncate: "…" preferences: languages: Języki - notifications: Powiadomienia other: Pozostałe publishing: Publikowanie web: Sieć @@ -467,6 +466,7 @@ pl: export: Eksportowanie danych followers: Autoryzowani śledzący import: Importowanie danych + notifications: Powiadomienia preferences: Preferencje settings: Ustawienia two_factor_authentication: Uwierzytelnianie dwuetapowe diff --git a/config/navigation.rb b/config/navigation.rb index 0a6ab6d3d..215d843b9 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -7,6 +7,7 @@ SimpleNavigation::Configuration.run do |navigation| primary.item :settings, safe_join([fa_icon('cog fw'), t('settings.settings')]), settings_profile_url do |settings| settings.item :profile, safe_join([fa_icon('user fw'), t('settings.edit_profile')]), settings_profile_url settings.item :preferences, safe_join([fa_icon('sliders fw'), t('settings.preferences')]), settings_preferences_url + settings.item :notifications, safe_join([fa_icon('bell fw'), t('settings.notifications')]), settings_notifications_url settings.item :password, safe_join([fa_icon('lock fw'), t('auth.change_password')]), edit_user_registration_url, highlights_on: %r{/auth/edit|/settings/delete} settings.item :two_factor_authentication, safe_join([fa_icon('mobile fw'), t('settings.two_factor_authentication')]), settings_two_factor_authentication_url, highlights_on: %r{/settings/two_factor_authentication} settings.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url diff --git a/config/routes.rb b/config/routes.rb index de3c1e0f9..8e80e1510 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -67,6 +67,7 @@ Rails.application.routes.draw do namespace :settings do resource :profile, only: [:show, :update] resource :preferences, only: [:show, :update] + resource :notifications, only: [:show, :update] resource :import, only: [:show, :create] resource :export, only: [:show] diff --git a/spec/controllers/settings/notifications_controller_spec.rb b/spec/controllers/settings/notifications_controller_spec.rb new file mode 100644 index 000000000..0bd993448 --- /dev/null +++ b/spec/controllers/settings/notifications_controller_spec.rb @@ -0,0 +1,37 @@ +require 'rails_helper' + +describe Settings::NotificationsController do + render_views + + let(:user) { Fabricate(:user) } + + before do + sign_in user, scope: :user + end + + describe 'GET #show' do + it 'returns http success' do + get :show + expect(response).to have_http_status(:success) + end + end + + describe 'PUT #update' do + it 'updates notifications settings' do + user.settings['notification_emails'] = user.settings['notification_emails'].merge('follow' => false) + user.settings['interactions'] = user.settings['interactions'].merge('must_be_follower' => true) + + put :update, params: { + user: { + notification_emails: { follow: '1' }, + interactions: { must_be_follower: '0' }, + } + } + + expect(response).to redirect_to(settings_notifications_path) + user.reload + expect(user.settings['notification_emails']['follow']).to be true + expect(user.settings['interactions']['must_be_follower']).to be false + end + end +end diff --git a/spec/controllers/settings/preferences_controller_spec.rb b/spec/controllers/settings/preferences_controller_spec.rb index 60fa42302..0f9431673 100644 --- a/spec/controllers/settings/preferences_controller_spec.rb +++ b/spec/controllers/settings/preferences_controller_spec.rb @@ -29,15 +29,11 @@ describe Settings::PreferencesController do it 'updates user settings' do user.settings['boost_modal'] = false user.settings['delete_modal'] = true - user.settings['notification_emails'] = user.settings['notification_emails'].merge('follow' => false) - user.settings['interactions'] = user.settings['interactions'].merge('must_be_follower' => true) put :update, params: { user: { setting_boost_modal: '1', setting_delete_modal: '0', - notification_emails: { follow: '1' }, - interactions: { must_be_follower: '0' }, } } @@ -45,8 +41,6 @@ describe Settings::PreferencesController do user.reload expect(user.settings['boost_modal']).to be true expect(user.settings['delete_modal']).to be false - expect(user.settings['notification_emails']['follow']).to be true - expect(user.settings['interactions']['must_be_follower']).to be false end end end -- cgit From b3af3f9f8cd5ed9c7ee06452e981b1b7734e1d89 Mon Sep 17 00:00:00 2001 From: utam0k Date: Wed, 4 Oct 2017 22:16:10 +0900 Subject: Implement EmailBlackList (#5109) * Implement BlacklistedEmailDomain * Use Faker::Internet.domain_name * Remove note column * Add frozen_string_literal comment * Delete unnecessary codes * Sort alphabetically * Change of wording * Rename BlacklistedEmailDomain to EmailDomainBlock --- .../admin/email_domain_blocks_controller.rb | 40 +++++++++++++++ app/models/email_domain_block.rb | 17 +++++++ app/validators/blacklisted_email_validator.rb | 1 + .../_email_domain_block.html.haml | 5 ++ .../admin/email_domain_blocks/index.html.haml | 13 +++++ app/views/admin/email_domain_blocks/new.html.haml | 10 ++++ config/locales/en.yml | 10 ++++ config/locales/ja.yml | 10 ++++ config/navigation.rb | 1 + config/routes.rb | 1 + .../20170928082043_create_email_domain_blocks.rb | 9 ++++ db/schema.rb | 8 ++- .../admin/email_domain_blocks_controller_spec.rb | 59 ++++++++++++++++++++++ spec/fabricators/email_domain_block_fabricator.rb | 3 ++ spec/models/email_domain_block_spec.rb | 21 ++++++++ 15 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 app/controllers/admin/email_domain_blocks_controller.rb create mode 100644 app/models/email_domain_block.rb create mode 100644 app/views/admin/email_domain_blocks/_email_domain_block.html.haml create mode 100644 app/views/admin/email_domain_blocks/index.html.haml create mode 100644 app/views/admin/email_domain_blocks/new.html.haml create mode 100644 db/migrate/20170928082043_create_email_domain_blocks.rb create mode 100644 spec/controllers/admin/email_domain_blocks_controller_spec.rb create mode 100644 spec/fabricators/email_domain_block_fabricator.rb create mode 100644 spec/models/email_domain_block_spec.rb (limited to 'spec/controllers') diff --git a/app/controllers/admin/email_domain_blocks_controller.rb b/app/controllers/admin/email_domain_blocks_controller.rb new file mode 100644 index 000000000..09275d5dc --- /dev/null +++ b/app/controllers/admin/email_domain_blocks_controller.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Admin + class EmailDomainBlocksController < BaseController + before_action :set_email_domain_block, only: [:show, :destroy] + + def index + @email_domain_blocks = EmailDomainBlock.page(params[:page]) + end + + def new + @email_domain_block = EmailDomainBlock.new + end + + def create + @email_domain_block = EmailDomainBlock.new(resource_params) + + if @email_domain_block.save + redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg') + else + render :new + end + end + + def destroy + @email_domain_block.destroy + redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg') + end + + private + + def set_email_domain_block + @email_domain_block = EmailDomainBlock.find(params[:id]) + end + + def resource_params + params.require(:email_domain_block).permit(:domain) + end + end +end diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb new file mode 100644 index 000000000..839038bea --- /dev/null +++ b/app/models/email_domain_block.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: email_domain_blocks +# +# id :integer not null, primary key +# domain :string not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class EmailDomainBlock < ApplicationRecord + def self.block?(email) + domain = email.gsub(/.+@([^.]+)/, '\1') + where(domain: domain).exists? + end +end diff --git a/app/validators/blacklisted_email_validator.rb b/app/validators/blacklisted_email_validator.rb index 0ba79694b..3f203f49a 100644 --- a/app/validators/blacklisted_email_validator.rb +++ b/app/validators/blacklisted_email_validator.rb @@ -12,6 +12,7 @@ class BlacklistedEmailValidator < ActiveModel::Validator end def on_blacklist?(value) + return true if EmailDomainBlock.block?(value) return false if Rails.configuration.x.email_domains_blacklist.blank? domains = Rails.configuration.x.email_domains_blacklist.gsub('.', '\.') diff --git a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml new file mode 100644 index 000000000..61cff9395 --- /dev/null +++ b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml @@ -0,0 +1,5 @@ +%tr + %td.domain + %samp= email_domain_block.domain + %td + = table_link_to 'trash', t('admin.email_domain_blocks.delete'), admin_email_domain_block_path(email_domain_block), method: :delete diff --git a/app/views/admin/email_domain_blocks/index.html.haml b/app/views/admin/email_domain_blocks/index.html.haml new file mode 100644 index 000000000..fbdb3b80b --- /dev/null +++ b/app/views/admin/email_domain_blocks/index.html.haml @@ -0,0 +1,13 @@ +- content_for :page_title do + = t('admin.email_domain_blocks.title') + +%table.table + %thead + %tr + %th= t('admin.email_domain_blocks.domain') + %th + %tbody + = render @email_domain_blocks + += paginate @email_domain_blocks += link_to t('admin.email_domain_blocks.add_new'), new_admin_email_domain_block_path, class: 'button' diff --git a/app/views/admin/email_domain_blocks/new.html.haml b/app/views/admin/email_domain_blocks/new.html.haml new file mode 100644 index 000000000..bcae867d9 --- /dev/null +++ b/app/views/admin/email_domain_blocks/new.html.haml @@ -0,0 +1,10 @@ +- content_for :page_title do + = t('.title') + += simple_form_for @email_domain_block, url: admin_email_domain_blocks_path do |f| + = render 'shared/error_messages', object: @email_domain_block + + = f.input :domain, placeholder: t('admin.email_domain_blocks.domain') + + .actions + = f.button :button, t('.create'), type: :submit diff --git a/config/locales/en.yml b/config/locales/en.yml index 4a6df8cb2..5d9557535 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -152,6 +152,16 @@ en: undo: Undo title: Domain Blocks undo: Undo + email_domain_blocks: + add_new: Add new + created_msg: Email domain block successfully created + delete: Delete + destroyed_msg: Email domain block successfully deleted + domain: Domain + new: + create: Create block + title: New email domain block + title: Email Domain Block instances: account_count: Known accounts domain_name: Domain diff --git a/config/locales/ja.yml b/config/locales/ja.yml index d637a99ea..3d6f2fd0b 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -152,6 +152,16 @@ ja: undo: 元に戻す title: ドメインブロック undo: 元に戻す + email_domain_blocks: + add_new: 新規追加 + created_msg: 処理を完了しました + delete: 消去 + destroyed_msg: 消去しました + domain: ドメイン + new: + create: ブロックを作成 + title: 新規メールドメインブロック + title: メールドメインブロック instances: account_count: 既知のアカウント数 domain_name: ドメイン名 diff --git a/config/navigation.rb b/config/navigation.rb index 215d843b9..50bfbd480 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -26,6 +26,7 @@ SimpleNavigation::Configuration.run do |navigation| admin.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url, highlights_on: %r{/admin/instances} admin.item :subscriptions, safe_join([fa_icon('paper-plane-o fw'), t('admin.subscriptions.title')]), admin_subscriptions_url admin.item :domain_blocks, safe_join([fa_icon('lock fw'), t('admin.domain_blocks.title')]), admin_domain_blocks_url, highlights_on: %r{/admin/domain_blocks} + admin.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks} admin.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' } admin.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' } admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url diff --git a/config/routes.rb b/config/routes.rb index 8e80e1510..959afc23f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -108,6 +108,7 @@ Rails.application.routes.draw do namespace :admin do resources :subscriptions, only: [:index] resources :domain_blocks, only: [:index, :new, :create, :show, :destroy] + resources :email_domain_blocks, only: [:index, :new, :create, :destroy] resource :settings, only: [:edit, :update] resources :instances, only: [:index] do diff --git a/db/migrate/20170928082043_create_email_domain_blocks.rb b/db/migrate/20170928082043_create_email_domain_blocks.rb new file mode 100644 index 000000000..1f0fb7587 --- /dev/null +++ b/db/migrate/20170928082043_create_email_domain_blocks.rb @@ -0,0 +1,9 @@ +class CreateEmailDomainBlocks < ActiveRecord::Migration[5.1] + def change + create_table :email_domain_blocks do |t| + t.string :domain, null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 00cc24bae..337678c67 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170927215609) do +ActiveRecord::Schema.define(version: 20170928082043) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -110,6 +110,12 @@ ActiveRecord::Schema.define(version: 20170927215609) do t.index ["domain"], name: "index_domain_blocks_on_domain", unique: true end + create_table "email_domain_blocks", force: :cascade do |t| + t.string "domain", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "favourites", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false diff --git a/spec/controllers/admin/email_domain_blocks_controller_spec.rb b/spec/controllers/admin/email_domain_blocks_controller_spec.rb new file mode 100644 index 000000000..295de9073 --- /dev/null +++ b/spec/controllers/admin/email_domain_blocks_controller_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Admin::EmailDomainBlocksController, type: :controller do + render_views + + before do + sign_in Fabricate(:user, admin: true), scope: :user + end + + describe 'GET #index' do + around do |example| + default_per_page = EmailDomainBlock.default_per_page + EmailDomainBlock.paginates_per 1 + example.run + EmailDomainBlock.paginates_per default_per_page + end + + it 'renders email blacks' do + 2.times { Fabricate(:email_domain_block) } + + get :index, params: { page: 2 } + + assigned = assigns(:email_domain_blocks) + expect(assigned.count).to eq 1 + expect(assigned.klass).to be EmailDomainBlock + expect(response).to have_http_status(:success) + end + end + + describe 'GET #new' do + it 'assigns a new email black' do + get :new + + expect(assigns(:email_domain_block)).to be_instance_of(EmailDomainBlock) + expect(response).to have_http_status(:success) + end + end + + describe 'POST #create' do + it 'blocks the domain when succeeded to save' do + post :create, params: { email_domain_block: { domain: 'example.com'} } + + expect(flash[:notice]).to eq I18n.t('admin.email_domain_blocks.created_msg') + expect(response).to redirect_to(admin_email_domain_blocks_path) + end + end + + describe 'DELETE #destroy' do + it 'unblocks the domain' do + email_domain_block = Fabricate(:email_domain_block) + delete :destroy, params: { id: email_domain_block.id } + + expect(flash[:notice]).to eq I18n.t('admin.email_domain_blocks.destroyed_msg') + expect(response).to redirect_to(admin_email_domain_blocks_path) + end + end +end diff --git a/spec/fabricators/email_domain_block_fabricator.rb b/spec/fabricators/email_domain_block_fabricator.rb new file mode 100644 index 000000000..d18af6433 --- /dev/null +++ b/spec/fabricators/email_domain_block_fabricator.rb @@ -0,0 +1,3 @@ +Fabricator(:email_domain_block) do + domain { sequence(:domain) { |i| "#{i}#{Faker::Internet.domain_name}" } } +end diff --git a/spec/models/email_domain_block_spec.rb b/spec/models/email_domain_block_spec.rb new file mode 100644 index 000000000..5f5d189d9 --- /dev/null +++ b/spec/models/email_domain_block_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +RSpec.describe EmailDomainBlock, type: :model do + describe 'validations' do + it 'has a valid fabricator' do + email_domain_block = Fabricate.build(:email_domain_block) + expect(email_domain_block).to be_valid + end + end + + describe 'block?' do + it 'returns true if the domain is registed' do + Fabricate(:email_domain_block, domain: 'example.com') + expect(EmailDomainBlock.block?('nyarn@example.com')).to eq true + end + it 'returns true if the domain is not registed' do + Fabricate(:email_domain_block, domain: 'domain') + expect(EmailDomainBlock.block?('example')).to eq false + end + end +end -- cgit From f486ef2666dacbcb6fcd26e371bb5e945369dcfe Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 7 Oct 2017 20:00:35 +0200 Subject: Redesign public hashtag pages (#5237) --- app/controllers/tags_controller.rb | 30 +++++-- .../mastodon/containers/timeline_container.js | 14 +++- .../features/standalone/hashtag_timeline/index.js | 70 +++++++++++++++++ app/javascript/packs/about.js | 6 +- app/javascript/styles/about.scss | 91 ++++++++++++++++++++++ app/javascript/styles/basics.scss | 5 ++ app/javascript/styles/components.scss | 1 + app/views/about/show.html.haml | 2 +- app/views/tags/_og.html.haml | 6 ++ app/views/tags/show.html.haml | 47 +++++++---- config/locales/en.yml | 1 + spec/controllers/tags_controller_spec.rb | 42 ++-------- 12 files changed, 253 insertions(+), 62 deletions(-) create mode 100644 app/javascript/mastodon/features/standalone/hashtag_timeline/index.js create mode 100644 app/views/tags/_og.html.haml (limited to 'spec/controllers') diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 240ef058a..9f3090e37 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -1,17 +1,22 @@ # frozen_string_literal: true class TagsController < ApplicationController - layout 'public' + before_action :set_body_classes + before_action :set_instance_presenter def show - @tag = Tag.find_by!(name: params[:id].downcase) - @statuses = Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(20, params[:max_id]) - @statuses = cache_collection(@statuses, Status) + @tag = Tag.find_by!(name: params[:id].downcase) respond_to do |format| - format.html + format.html do + serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) + @initial_state_json = serializable_resource.to_json + end format.json do + @statuses = Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(20, params[:max_id]) + @statuses = cache_collection(@statuses, Status) + render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, @@ -22,6 +27,14 @@ class TagsController < ApplicationController private + def set_body_classes + @body_classes = 'tag-body' + end + + def set_instance_presenter + @instance_presenter = InstancePresenter.new + end + def collection_presenter ActivityPub::CollectionPresenter.new( id: tag_url(@tag), @@ -30,4 +43,11 @@ class TagsController < ApplicationController items: @statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) } ) end + + def initial_state_params + { + settings: {}, + token: current_session&.token, + } + end end diff --git a/app/javascript/mastodon/containers/timeline_container.js b/app/javascript/mastodon/containers/timeline_container.js index 6b545ef09..4be037955 100644 --- a/app/javascript/mastodon/containers/timeline_container.js +++ b/app/javascript/mastodon/containers/timeline_container.js @@ -6,6 +6,7 @@ import { hydrateStore } from '../actions/store'; import { IntlProvider, addLocaleData } from 'react-intl'; import { getLocale } from '../locales'; import PublicTimeline from '../features/standalone/public_timeline'; +import HashtagTimeline from '../features/standalone/hashtag_timeline'; const { localeData, messages } = getLocale(); addLocaleData(localeData); @@ -22,15 +23,24 @@ export default class TimelineContainer extends React.PureComponent { static propTypes = { locale: PropTypes.string.isRequired, + hashtag: PropTypes.string, }; render () { - const { locale } = this.props; + const { locale, hashtag } = this.props; + + let timeline; + + if (hashtag) { + timeline = ; + } else { + timeline = ; + } return ( - + {timeline} ); diff --git a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js new file mode 100644 index 000000000..f15fbb2f4 --- /dev/null +++ b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js @@ -0,0 +1,70 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import StatusListContainer from '../../ui/containers/status_list_container'; +import { + refreshHashtagTimeline, + expandHashtagTimeline, +} from '../../../actions/timelines'; +import Column from '../../../components/column'; +import ColumnHeader from '../../../components/column_header'; + +@connect() +export default class HashtagTimeline extends React.PureComponent { + + static propTypes = { + dispatch: PropTypes.func.isRequired, + hashtag: PropTypes.string.isRequired, + }; + + handleHeaderClick = () => { + this.column.scrollTop(); + } + + setRef = c => { + this.column = c; + } + + componentDidMount () { + const { dispatch, hashtag } = this.props; + + dispatch(refreshHashtagTimeline(hashtag)); + + this.polling = setInterval(() => { + dispatch(refreshHashtagTimeline(hashtag)); + }, 10000); + } + + componentWillUnmount () { + if (typeof this.polling !== 'undefined') { + clearInterval(this.polling); + this.polling = null; + } + } + + handleLoadMore = () => { + this.props.dispatch(expandHashtagTimeline(this.props.hashtag)); + } + + render () { + const { hashtag } = this.props; + + return ( + + + + + + ); + } + +} diff --git a/app/javascript/packs/about.js b/app/javascript/packs/about.js index 6705377c1..50c81198e 100644 --- a/app/javascript/packs/about.js +++ b/app/javascript/packs/about.js @@ -4,9 +4,9 @@ require.context('../images/', true); function loaded() { const TimelineContainer = require('../mastodon/containers/timeline_container').default; - const React = require('react'); - const ReactDOM = require('react-dom'); - const mountNode = document.getElementById('mastodon-timeline'); + const React = require('react'); + const ReactDOM = require('react-dom'); + const mountNode = document.getElementById('mastodon-timeline'); if (mountNode !== null) { const props = JSON.parse(mountNode.getAttribute('data-props')); diff --git a/app/javascript/styles/about.scss b/app/javascript/styles/about.scss index 2adcb5ba2..a15afc32c 100644 --- a/app/javascript/styles/about.scss +++ b/app/javascript/styles/about.scss @@ -481,6 +481,7 @@ flex: 0 0 auto; background: $ui-base-color; overflow: hidden; + border-radius: 4px; box-shadow: 0 0 6px rgba($black, 0.1); .column-header { @@ -703,8 +704,98 @@ .features #mastodon-timeline { height: 70vh; width: 100%; + min-width: 330px; margin-bottom: 50px; + + .column { + width: 100%; + } + } + } + + .cta { + margin: 20px; + } + + &.tag-page { + .brand { + padding-top: 20px; + margin-bottom: 20px; + + img { + height: 48px; + width: auto; + } + } + + .container { + max-width: 690px; + } + + .cta { + margin: 40px 0; + margin-bottom: 80px; + + .button { + margin-right: 4px; + } + } + + .about-mastodon { + max-width: 330px; + + p { + strong { + color: $ui-secondary-color; + font-weight: 700; + } + } } + + @media screen and (max-width: 675px) { + .container { + display: flex; + flex-direction: column; + } + + .features { + padding: 20px 0; + } + + .about-mastodon { + order: 1; + flex: 0 0 auto; + max-width: 100%; + } + + #mastodon-timeline { + order: 2; + flex: 0 0 auto; + height: 60vh; + } + + .cta { + margin: 20px 0; + margin-bottom: 30px; + } + + .features-list { + display: none; + } + + .stripe { + display: none; + } + } + } + + .stripe { + width: 100%; + height: 360px; + overflow: hidden; + background: darken($ui-base-color, 4%); + position: absolute; + z-index: -1; } } diff --git a/app/javascript/styles/basics.scss b/app/javascript/styles/basics.scss index 0018c9a5d..500e506f6 100644 --- a/app/javascript/styles/basics.scss +++ b/app/javascript/styles/basics.scss @@ -42,6 +42,11 @@ body { padding-bottom: 0; } + &.tag-body { + background: darken($ui-base-color, 8%); + padding-bottom: 0; + } + &.embed { background: transparent; margin: 0; diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 6c64528d6..0e7022e9b 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -66,6 +66,7 @@ text-transform: none; background: transparent; padding: 3px 15px; + border-radius: 4px; border: 1px solid $ui-primary-color; &:active, diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index 0d311b895..ef27d07a1 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -62,7 +62,7 @@ .about-mastodon %h3= t 'about.what_is_mastodon' %p= t 'about.about_mastodon_html' - %a.button.button-secondary{ href: 'https://joinmastodon.org/' }= t 'about.learn_more' + = link_to t('about.learn_more'), 'https://joinmastodon.org/', class: 'button button-secondary' = render 'features' .footer-links .container diff --git a/app/views/tags/_og.html.haml b/app/views/tags/_og.html.haml new file mode 100644 index 000000000..853a499ae --- /dev/null +++ b/app/views/tags/_og.html.haml @@ -0,0 +1,6 @@ += opengraph 'og:site_name', t('about.hosted_on', domain: site_hostname) += opengraph 'og:url', tag_url(@tag) += opengraph 'og:type', 'website' += opengraph 'og:title', "##{@tag.name}" += opengraph 'og:description', t('about.about_hashtag_html', hashtag: @tag.name) += opengraph 'twitter:card', 'summary' diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml index 8cd2f1825..6266d3c0c 100644 --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@ -1,19 +1,38 @@ - content_for :page_title do = "##{@tag.name}" -.compact-header - %h1< - = link_to site_title, root_path - %br - %small ##{@tag.name} +- content_for :header_tags do + %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) + = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' + = render 'og' -- if @statuses.empty? - .accounts-grid - = render partial: 'accounts/nothing_here' -- else - .activity-stream.h-feed - = render partial: 'stream_entries/status', collection: @statuses, as: :status +.landing-page.tag-page + .stripe + .features + .container + #mastodon-timeline{ data: { props: Oj.dump(default_props.merge(hashtag: @tag.name)) } } -- if @statuses.size == 20 - .pagination - = link_to safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), tag_url(@tag, max_id: @statuses.last.id), class: 'next', rel: 'next' + .about-mastodon + .brand + = link_to root_url do + = image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon' + + %p= t 'about.about_hashtag_html', hashtag: @tag.name + + .cta + = link_to t('auth.login'), new_user_session_path, class: 'button button-secondary' + = link_to t('about.learn_more'), root_url, class: 'button button-alternative' + + .features-list + .features-list__row + .text + %h6= t 'about.features.not_a_product_title' + = t 'about.features.not_a_product_body' + .visual + = fa_icon 'fw users' + .features-list__row + .text + %h6= t 'about.features.humane_approach_title' + = t 'about.features.humane_approach_body' + .visual + = fa_icon 'fw leaf' diff --git a/config/locales/en.yml b/config/locales/en.yml index 2059c5e2b..82041be24 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2,6 +2,7 @@ en: about: about_mastodon_html: Mastodon is a social network based on open web protocols and free, open-source software. It is decentralized like e-mail. + about_hashtag_html: These are public toots tagged with #%{hashtag}. You can interact with them if you have an account anywhere in the fediverse. about_this: About closed_registrations: Registrations are currently closed on this instance. However! You can find a different instance to make an account on and get access to the very same network from there. contact: Contact diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb index 3f46c14c0..b04666c0f 100644 --- a/spec/controllers/tags_controller_spec.rb +++ b/spec/controllers/tags_controller_spec.rb @@ -5,9 +5,9 @@ RSpec.describe TagsController, type: :controller do describe 'GET #show' do let!(:tag) { Fabricate(:tag, name: 'test') } - let!(:local) { Fabricate(:status, tags: [ tag ], text: 'local #test') } - let!(:remote) { Fabricate(:status, tags: [ tag ], text: 'remote #test', account: Fabricate(:account, domain: 'remote')) } - let!(:late) { Fabricate(:status, tags: [ tag ], text: 'late #test') } + let!(:local) { Fabricate(:status, tags: [tag], text: 'local #test') } + let!(:remote) { Fabricate(:status, tags: [tag], text: 'remote #test', account: Fabricate(:account, domain: 'remote')) } + let!(:late) { Fabricate(:status, tags: [tag], text: 'late #test') } context 'when tag exists' do it 'returns http success' do @@ -15,41 +15,9 @@ RSpec.describe TagsController, type: :controller do expect(response).to have_http_status(:success) end - it 'renders public layout' do + it 'renders application layout' do get :show, params: { id: 'test', max_id: late.id } - expect(response).to render_template layout: 'public' - end - - it 'renders only local statuses if local parameter is specified' do - get :show, params: { id: 'test', local: true, max_id: late.id } - - expect(assigns(:tag)).to eq tag - statuses = assigns(:statuses).to_a - expect(statuses.size).to eq 1 - expect(statuses[0]).to eq local - end - - it 'renders local and remote statuses if local parameter is not specified' do - get :show, params: { id: 'test', max_id: late.id } - - expect(assigns(:tag)).to eq tag - statuses = assigns(:statuses).to_a - expect(statuses.size).to eq 2 - expect(statuses[0]).to eq remote - expect(statuses[1]).to eq local - end - - it 'filters statuses by the current account' do - user = Fabricate(:user) - user.account.block!(remote.account) - - sign_in(user) - get :show, params: { id: 'test', max_id: late.id } - - expect(assigns(:tag)).to eq tag - statuses = assigns(:statuses).to_a - expect(statuses.size).to eq 1 - expect(statuses[0]).to eq local + expect(response).to render_template layout: 'application' end end -- cgit From 633426b2616e8559acfa76f4294a51afcf434fc2 Mon Sep 17 00:00:00 2001 From: nullkal Date: Sun, 8 Oct 2017 03:26:43 +0900 Subject: Add moderation note (#5240) * Add moderation note * Add frozen_string_literal * Make rspec pass --- .../admin/account_moderation_notes_controller.rb | 31 ++++++++++++++++++++++ app/controllers/admin/accounts_controller.rb | 5 +++- .../admin/account_moderation_notes_helper.rb | 4 +++ app/models/account.rb | 4 +++ app/models/account_moderation_note.rb | 22 +++++++++++++++ .../_account_moderation_note.html.haml | 10 +++++++ app/views/admin/accounts/show.html.haml | 22 +++++++++++++++ config/locales/en.yml | 10 +++++++ config/routes.rb | 2 ++ ...171005102658_create_account_moderation_notes.rb | 12 +++++++++ db/schema.rb | 11 ++++++++ .../account_moderation_notes_controller_spec.rb | 4 +++ .../account_moderation_note_fabricator.rb | 4 +++ .../admin/account_moderation_notes_helper_spec.rb | 15 +++++++++++ spec/models/account_moderation_note_spec.rb | 5 ++++ 15 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 app/controllers/admin/account_moderation_notes_controller.rb create mode 100644 app/helpers/admin/account_moderation_notes_helper.rb create mode 100644 app/models/account_moderation_note.rb create mode 100644 app/views/admin/account_moderation_notes/_account_moderation_note.html.haml create mode 100644 db/migrate/20171005102658_create_account_moderation_notes.rb create mode 100644 spec/controllers/admin/account_moderation_notes_controller_spec.rb create mode 100644 spec/fabricators/account_moderation_note_fabricator.rb create mode 100644 spec/helpers/admin/account_moderation_notes_helper_spec.rb create mode 100644 spec/models/account_moderation_note_spec.rb (limited to 'spec/controllers') diff --git a/app/controllers/admin/account_moderation_notes_controller.rb b/app/controllers/admin/account_moderation_notes_controller.rb new file mode 100644 index 000000000..414a875d0 --- /dev/null +++ b/app/controllers/admin/account_moderation_notes_controller.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class Admin::AccountModerationNotesController < Admin::BaseController + def create + @account_moderation_note = current_account.account_moderation_notes.new(resource_params) + if @account_moderation_note.save + @target_account = @account_moderation_note.target_account + redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.created_msg') + else + @account = @account_moderation_note.target_account + @moderation_notes = @account.targeted_moderation_notes.latest + render template: 'admin/accounts/show' + end + end + + def destroy + @account_moderation_note = AccountModerationNote.find(params[:id]) + @target_account = @account_moderation_note.target_account + @account_moderation_note.destroy + redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg') + end + + private + + def resource_params + params.require(:account_moderation_note).permit( + :content, + :target_account_id + ) + end +end diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index 54c659e1b..ffa4dc850 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -9,7 +9,10 @@ module Admin @accounts = filtered_accounts.page(params[:page]) end - def show; end + def show + @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) + @moderation_notes = @account.targeted_moderation_notes.latest + end def subscribe Pubsubhubbub::SubscribeWorker.perform_async(@account.id) diff --git a/app/helpers/admin/account_moderation_notes_helper.rb b/app/helpers/admin/account_moderation_notes_helper.rb new file mode 100644 index 000000000..b17c52264 --- /dev/null +++ b/app/helpers/admin/account_moderation_notes_helper.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +module Admin::AccountModerationNotesHelper +end diff --git a/app/models/account.rb b/app/models/account.rb index 54035d94a..88f16026d 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -90,6 +90,10 @@ class Account < ApplicationRecord has_many :reports has_many :targeted_reports, class_name: 'Report', foreign_key: :target_account_id + # Moderation notes + has_many :account_moderation_notes + has_many :targeted_moderation_notes, class_name: 'AccountModerationNote', foreign_key: :target_account_id + scope :remote, -> { where.not(domain: nil) } scope :local, -> { where(domain: nil) } scope :without_followers, -> { where(followers_count: 0) } diff --git a/app/models/account_moderation_note.rb b/app/models/account_moderation_note.rb new file mode 100644 index 000000000..be52d10b6 --- /dev/null +++ b/app/models/account_moderation_note.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: account_moderation_notes +# +# id :integer not null, primary key +# content :text not null +# account_id :integer +# target_account_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +class AccountModerationNote < ApplicationRecord + belongs_to :account + belongs_to :target_account, class_name: 'Account' + + scope :latest, -> { reorder('created_at DESC') } + + validates :content, presence: true, length: { maximum: 500 } +end diff --git a/app/views/admin/account_moderation_notes/_account_moderation_note.html.haml b/app/views/admin/account_moderation_notes/_account_moderation_note.html.haml new file mode 100644 index 000000000..4651630e9 --- /dev/null +++ b/app/views/admin/account_moderation_notes/_account_moderation_note.html.haml @@ -0,0 +1,10 @@ +%tr + %td + = simple_format(h(account_moderation_note.content)) + %td + = account_moderation_note.account.acct + %td + %time.formatted{ datetime: account_moderation_note.created_at.iso8601, title: l(account_moderation_note.created_at) } + = l account_moderation_note.created_at + %td + = link_to t('admin.account_moderation_notes.delete'), admin_account_moderation_note_path(account_moderation_note), method: :delete diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index 3775b6721..1f5c8fcf5 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -129,3 +129,25 @@ %tr %th= t('admin.accounts.followers_url') %td= link_to @account.followers_url, @account.followers_url + +%hr +%h3= t('admin.accounts.moderation_notes') + += simple_form_for @account_moderation_note, url: admin_account_moderation_notes_path do |f| + = render 'shared/error_messages', object: @account_moderation_note + + = f.input :content + = f.hidden_field :target_account_id + + .actions + = f.button :button, t('admin.account_moderation_notes.create'), type: :submit + +.table-wrapper + %table.table + %thead + %tr + %th + %th= t('admin.account_moderation_notes.account') + %th= t('admin.account_moderation_notes.created_at') + %tbody + = render @moderation_notes diff --git a/config/locales/en.yml b/config/locales/en.yml index 82041be24..7d2596fc6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -76,6 +76,7 @@ en: silenced: Silenced suspended: Suspended title: Moderation + moderation_notes: Moderation notes most_recent_activity: Most recent activity most_recent_ip: Most recent IP not_subscribed: Not subscribed @@ -109,6 +110,15 @@ en: unsubscribe: Unsubscribe username: Username web: Web + + account_moderation_notes: + account: Moderator + created_at: Date + create: Create + created_msg: Moderation note successfully created! + delete: Delete + destroyed_msg: Moderation note successfully destroyed! + custom_emojis: copied_msg: Successfully created local copy of the emoji copy: Copy diff --git a/config/routes.rb b/config/routes.rb index bd7068b5c..5a6351f77 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -147,6 +147,8 @@ Rails.application.routes.draw do post :disable end end + + resources :account_moderation_notes, only: [:create, :destroy] end get '/admin', to: redirect('/admin/settings/edit', status: 302) diff --git a/db/migrate/20171005102658_create_account_moderation_notes.rb b/db/migrate/20171005102658_create_account_moderation_notes.rb new file mode 100644 index 000000000..d1802b5b3 --- /dev/null +++ b/db/migrate/20171005102658_create_account_moderation_notes.rb @@ -0,0 +1,12 @@ +class CreateAccountModerationNotes < ActiveRecord::Migration[5.1] + def change + create_table :account_moderation_notes do |t| + t.text :content, null: false + t.references :account + t.references :target_account + + t.timestamps + end + add_foreign_key :account_moderation_notes, :accounts, column: :target_account_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 7180d3515..91f1b1acb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -23,6 +23,16 @@ ActiveRecord::Schema.define(version: 20171006142024) do t.index ["account_id", "domain"], name: "index_account_domain_blocks_on_account_id_and_domain", unique: true end + create_table "account_moderation_notes", force: :cascade do |t| + t.text "content", null: false + t.bigint "account_id" + t.bigint "target_account_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_account_moderation_notes_on_account_id" + t.index ["target_account_id"], name: "index_account_moderation_notes_on_target_account_id" + end + create_table "accounts", force: :cascade do |t| t.string "username", default: "", null: false t.string "domain" @@ -449,6 +459,7 @@ ActiveRecord::Schema.define(version: 20171006142024) do end add_foreign_key "account_domain_blocks", "accounts", name: "fk_206c6029bd", on_delete: :cascade + add_foreign_key "account_moderation_notes", "accounts", column: "target_account_id" add_foreign_key "blocks", "accounts", column: "target_account_id", name: "fk_9571bfabc1", on_delete: :cascade add_foreign_key "blocks", "accounts", name: "fk_4269e03e65", on_delete: :cascade add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade diff --git a/spec/controllers/admin/account_moderation_notes_controller_spec.rb b/spec/controllers/admin/account_moderation_notes_controller_spec.rb new file mode 100644 index 000000000..ca4e55c4d --- /dev/null +++ b/spec/controllers/admin/account_moderation_notes_controller_spec.rb @@ -0,0 +1,4 @@ +require 'rails_helper' + +RSpec.describe Admin::AccountModerationNotesController, type: :controller do +end diff --git a/spec/fabricators/account_moderation_note_fabricator.rb b/spec/fabricators/account_moderation_note_fabricator.rb new file mode 100644 index 000000000..9277af165 --- /dev/null +++ b/spec/fabricators/account_moderation_note_fabricator.rb @@ -0,0 +1,4 @@ +Fabricator(:account_moderation_note) do + content "MyText" + account nil +end diff --git a/spec/helpers/admin/account_moderation_notes_helper_spec.rb b/spec/helpers/admin/account_moderation_notes_helper_spec.rb new file mode 100644 index 000000000..01b60c851 --- /dev/null +++ b/spec/helpers/admin/account_moderation_notes_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the Admin::AccountModerationNotesHelper. For example: +# +# describe Admin::AccountModerationNotesHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe Admin::AccountModerationNotesHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/account_moderation_note_spec.rb b/spec/models/account_moderation_note_spec.rb new file mode 100644 index 000000000..c4be8c4af --- /dev/null +++ b/spec/models/account_moderation_note_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe AccountModerationNote, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end -- cgit From cc796298c9b1c1d2e8b6d36311eb9acc95ab8dc0 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 10 Oct 2017 00:30:31 +0900 Subject: Fix pagination in Api::V1::BlocksController (#5285) --- app/controllers/api/v1/blocks_controller.rb | 26 +++++++------- spec/controllers/api/v1/blocks_controller_spec.rb | 42 ++++++++++++++++++++--- 2 files changed, 49 insertions(+), 19 deletions(-) (limited to 'spec/controllers') diff --git a/app/controllers/api/v1/blocks_controller.rb b/app/controllers/api/v1/blocks_controller.rb index a412e4341..3a6690766 100644 --- a/app/controllers/api/v1/blocks_controller.rb +++ b/app/controllers/api/v1/blocks_controller.rb @@ -15,19 +15,17 @@ class Api::V1::BlocksController < Api::BaseController private def load_accounts - default_accounts.merge(paginated_blocks).to_a - end - - def default_accounts - Account.includes(:blocked_by).references(:blocked_by) + paginated_blocks.map(&:target_account) end def paginated_blocks - Block.where(account: current_account).paginate_by_max_id( - limit_param(DEFAULT_ACCOUNTS_LIMIT), - params[:max_id], - params[:since_id] - ) + @paginated_blocks ||= Block.eager_load(:target_account) + .where(account: current_account) + .paginate_by_max_id( + limit_param(DEFAULT_ACCOUNTS_LIMIT), + params[:max_id], + params[:since_id] + ) end def insert_pagination_headers @@ -41,21 +39,21 @@ class Api::V1::BlocksController < Api::BaseController end def prev_path - unless @accounts.empty? + unless paginated_blocks.empty? api_v1_blocks_url pagination_params(since_id: pagination_since_id) end end def pagination_max_id - @accounts.last.blocked_by_ids.last + paginated_blocks.last.id end def pagination_since_id - @accounts.first.blocked_by_ids.first + paginated_blocks.first.id end def records_continue? - @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) + paginated_blocks.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) end def pagination_params(core_params) diff --git a/spec/controllers/api/v1/blocks_controller_spec.rb b/spec/controllers/api/v1/blocks_controller_spec.rb index f25a7e878..9b2bbdf0e 100644 --- a/spec/controllers/api/v1/blocks_controller_spec.rb +++ b/spec/controllers/api/v1/blocks_controller_spec.rb @@ -6,15 +6,47 @@ RSpec.describe Api::V1::BlocksController, type: :controller do let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'follow') } - before do - Fabricate(:block, account: user.account) - allow(controller).to receive(:doorkeeper_token) { token } - end + before { allow(controller).to receive(:doorkeeper_token) { token } } describe 'GET #index' do - it 'returns http success' do + it 'limits according to limit parameter' do + 2.times.map { Fabricate(:block, account: user.account) } get :index, params: { limit: 1 } + expect(body_as_json.size).to eq 1 + end + + it 'queries blocks in range according to max_id' do + blocks = 2.times.map { Fabricate(:block, account: user.account) } + + get :index, params: { max_id: blocks[1] } + + expect(body_as_json.size).to eq 1 + expect(body_as_json[0][:id]).to eq blocks[0].target_account_id.to_s + end + + it 'queries blocks in range according to since_id' do + blocks = 2.times.map { Fabricate(:block, account: user.account) } + get :index, params: { since_id: blocks[0] } + + expect(body_as_json.size).to eq 1 + expect(body_as_json[0][:id]).to eq blocks[1].target_account_id.to_s + end + + it 'sets pagination header for next path' do + blocks = 2.times.map { Fabricate(:block, account: user.account) } + get :index, params: { limit: 1, since_id: blocks[0] } + expect(response.headers['Link'].find_link(['rel', 'next']).href).to eq api_v1_blocks_url(limit: 1, max_id: blocks[1]) + end + + it 'sets pagination header for previous path' do + block = Fabricate(:block, account: user.account) + get :index + expect(response.headers['Link'].find_link(['rel', 'prev']).href).to eq api_v1_blocks_url(since_id: block) + end + + it 'returns http success' do + get :index expect(response).to have_http_status(:success) end end -- cgit