From 5b45c1646aa324d2eb4a3ecb8c12ed4df3cdf129 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 29 Sep 2017 03:03:03 +0200 Subject: Remove dependency on db during assets:precompile (#5138) --- app/views/layouts/error.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/views/layouts/error.html.haml b/app/views/layouts/error.html.haml index 31f322096..7d014dff4 100644 --- a/app/views/layouts/error.html.haml +++ b/app/views/layouts/error.html.haml @@ -3,7 +3,7 @@ %head %meta{ content: 'text/html; charset=UTF-8', 'http-equiv' => 'Content-Type' }/ %meta{ charset: 'utf-8' }/ - %title= safe_join([yield(:page_title), title], ' - ') + %title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ') %meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/ = stylesheet_pack_tag 'common', media: 'all' = stylesheet_pack_tag Setting.default_settings['theme'], media: 'all' -- cgit From 0b3f1ec62a08ab2aad2b7c1ab8f88bdac5e8a3c6 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 1 Oct 2017 10:52:39 +0200 Subject: Reorganize preferences page (#5161) --- app/javascript/styles/forms.scss | 11 ++++++++ app/views/settings/preferences/show.html.haml | 38 ++++++++++++++------------- config/locales/de.yml | 1 - config/locales/devise.de.yml | 4 +-- config/locales/en.yml | 6 +++++ config/locales/ja.yml | 2 +- config/locales/oc.yml | 2 +- config/locales/pl.yml | 2 +- config/locales/simple_form.en.yml | 5 ++-- 9 files changed, 45 insertions(+), 26 deletions(-) (limited to 'app/views') diff --git a/app/javascript/styles/forms.scss b/app/javascript/styles/forms.scss index 0526f174c..d241c9d26 100644 --- a/app/javascript/styles/forms.scss +++ b/app/javascript/styles/forms.scss @@ -22,6 +22,16 @@ code { margin-top: 4px; } + h4 { + text-transform: uppercase; + font-size: 13px; + font-weight: 500; + color: $ui-primary-color; + padding-bottom: 8px; + margin-bottom: 8px; + border-bottom: 1px solid lighten($ui-base-color, 8%); + } + p.hint { margin-bottom: 15px; color: $ui-primary-color; @@ -316,6 +326,7 @@ code { select { font-size: 16px; + max-height: 29px; } .input-with-append { diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index 5efd538e4..ffb1bbf6a 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -4,30 +4,22 @@ = simple_form_for current_user, url: settings_preferences_path, html: { method: :put } do |f| = render 'shared/error_messages', object: current_user + %h4= t 'preferences.languages' + .fields-group - = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| safe_join([I18n.t("themes.#{theme}", default: theme)])}, wrapper: :with_label, include_blank: false - - = f.input :locale, - collection: I18n.available_locales, - wrapper: :with_label, - include_blank: false, - label_method: lambda { |locale| human_locale(locale) }, - selected: I18n.locale - - = f.input :filtered_languages, - collection: filterable_languages, - wrapper: :with_block_label, - include_blank: false, - label_method: lambda { |locale| human_locale(locale) }, - required: false, - as: :check_boxes, - collection_wrapper_tag: 'ul', - item_wrapper_tag: 'li' + = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale + + = f.input :filtered_languages, collection: filterable_languages, wrapper: :with_block_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' + + %h4= t 'preferences.publishing' + .fields-group = f.input :setting_default_privacy, collection: Status.visibilities.keys - ['direct'], wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' = 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 @@ -35,6 +27,9 @@ = 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 @@ -42,10 +37,17 @@ = 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 = f.input :setting_noindex, as: :boolean, wrapper: :with_label + %h4= t 'preferences.web' + .fields-group + - if Themes.instance.names.size > 1 + = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false + = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label = f.input :setting_delete_modal, as: :boolean, wrapper: :with_label diff --git a/config/locales/de.yml b/config/locales/de.yml index 06a535ba6..1192a7b10 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -388,7 +388,6 @@ de: private_long: Nur für Folgende sichtbar public: Öffentlich public_long: Für alle sichtbar - unlisted: Nicht gelistet unlisted: Für alle sichtbar, aber nicht in öffentlichen Zeitleisten aufgelistet stream_entries: click_to_show: Klicken, um zu zeigen diff --git a/config/locales/devise.de.yml b/config/locales/devise.de.yml index b1e26f1e5..0db946b9f 100644 --- a/config/locales/devise.de.yml +++ b/config/locales/devise.de.yml @@ -8,10 +8,10 @@ de: failure: already_authenticated: Du bist bereits angemeldet. inactive: Dein Konto wurde noch nicht aktiviert. - invalid: '%{authentication_keys} oder Passwort ungültig.' + invalid: "%{authentication_keys} oder Passwort ungültig." last_attempt: Du hast noch einen Versuch, bevor dein Konto gesperrt wird. locked: Dein Konto ist gesperrt. - not_found_in_database: '%{authentication_keys} oder Passwort ungültig.' + not_found_in_database: "%{authentication_keys} oder Passwort ungültig." timeout: Deine Sitzung ist abgelaufen. Bitte melde dich erneut an. unauthenticated: Du musst dich anmelden oder registrieren, bevor du fortfahren kannst. unconfirmed: Du musst deine E-Mail-Adresse bestätigen, bevor du fortfahren kannst. diff --git a/config/locales/en.yml b/config/locales/en.yml index f87d8532c..3049e0365 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -393,6 +393,12 @@ en: next: Next prev: Prev truncate: "…" + preferences: + languages: Languages + notifications: Notifications + other: Other + publishing: Publishing + web: Web push_notifications: favourite: title: "%{name} favourited your status" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 2ccc827da..364bfcfd6 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -247,7 +247,7 @@ ja: salutation: "%{name} さん" settings: 'メール設定の変更: %{link}' signature: Mastodon %{instance} インスタンスからの通知 - view: 'リンク' + view: リンク applications: created: アプリが作成されました destroyed: アプリが削除されました diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 406de36f0..3100e6265 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -93,8 +93,8 @@ oc: reset_password: Reïnicializar lo senhal resubscribe: Se tornar abonar salmon_url: URL Salmon - shared_inbox_url: URL de recepcion partejada search: Cercar + shared_inbox_url: URL de recepcion partejada show: created_reports: Rapòrts creat per aqueste compte report: rapòrt diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 9bf57e38b..f52883123 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -62,7 +62,7 @@ pl: followers: Śledzący followers_url: Adres śledzących follows: Śledzeni - inbox: Adres skrzynki + inbox_url: Adres skrzynki ip: Adres IP location: all: Wszystkie diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 6f8b88898..86c80290c 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -4,6 +4,7 @@ en: hints: defaults: avatar: PNG, GIF or JPG. At most 2MB. Will be downscaled to 120x120px + digest: Sent after a long period of inactivity with a summary of mentions you've received in your absence display_name: one: 1 character left other: %{count} characters left @@ -19,7 +20,7 @@ en: sessions: otp: Enter the Two-factor code from your phone or use one of your recovery codes. user: - filtered_languages: Selected languages will be removed from your public timelines. + filtered_languages: Checked languages will be filtered from public timelines for you labels: defaults: avatar: Avatar @@ -44,7 +45,7 @@ en: setting_delete_modal: Show confirmation dialog before deleting a toot setting_noindex: Opt-out of search engine indexing setting_system_font_ui: Use system's default font - setting_theme: Site theme + setting_theme: Site theme setting_unfollow_modal: Show confirmation dialog before unfollowing someone severity: Severity type: Import type -- 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 'app/views') 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 d841af4e80e512921653f55bf545199f22a74b32 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Mon, 2 Oct 2017 14:21:46 +0900 Subject: Append confirmation link as plain text (#5146) * Append confirmation link as plain text Some mail application is malfunctioning with links. * Change description text --- app/views/user_mailer/confirmation_instructions.en.html.erb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'app/views') diff --git a/app/views/user_mailer/confirmation_instructions.en.html.erb b/app/views/user_mailer/confirmation_instructions.en.html.erb index f28a38be2..885c0b56a 100644 --- a/app/views/user_mailer/confirmation_instructions.en.html.erb +++ b/app/views/user_mailer/confirmation_instructions.en.html.erb @@ -2,11 +2,14 @@

You just created an account on <%= @instance %>.

-

To confirm your inscription, please click on the following link :
-<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %> +

If the above link did not work, copy and paste this URL into your address bar:
+<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

+ +

If above link not worked, Copy and paste this URL

+<%= confirmation_url(@resource, confirmation_token: @token) %>

Please also check out our <%= link_to 'terms and conditions', terms_url %>.

Sincerely,

-

The <%= @instance %> team

\ No newline at end of file +

The <%= @instance %> team

-- cgit From c30e4925870a31a13a4f36d4ba3ad16b7d1d93d6 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Tue, 3 Oct 2017 06:26:28 +0900 Subject: Korean translation (#5185) * Add missing Korean translations * Add Korean email confirmation template * Fix Korean typo --- .../confirmation_instructions.ko.html.erb | 13 +++++++ .../confirmation_instructions.ko.text.erb | 10 +++++ config/locales/ko.yml | 43 +++++++++++++++++++--- 3 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 app/views/user_mailer/confirmation_instructions.ko.html.erb create mode 100644 app/views/user_mailer/confirmation_instructions.ko.text.erb (limited to 'app/views') diff --git a/app/views/user_mailer/confirmation_instructions.ko.html.erb b/app/views/user_mailer/confirmation_instructions.ko.html.erb new file mode 100644 index 000000000..a749cd97b --- /dev/null +++ b/app/views/user_mailer/confirmation_instructions.ko.html.erb @@ -0,0 +1,13 @@ +

안녕하세요 <%= @resource.email %> 님!

+ +

<%= @instance %>에 새로 계정을 만들었습니다.

+ +

아래 링크를 눌러 회원가입을 완료 하세요:
+<%= link_to '계정 활성화', confirmation_url(@resource, confirmation_token: @token) %>

+ +

만약 위의 링크가 작동하지 않는다면 아래 URL을 복사하여 주소창에 붙여넣으세요

+<%= confirmation_url(@resource, confirmation_token: @token) %> + +

<%= link_to '약관', terms_url %>도 확인 바랍니다.

+ +

<%= @instance %> 드림

diff --git a/app/views/user_mailer/confirmation_instructions.ko.text.erb b/app/views/user_mailer/confirmation_instructions.ko.text.erb new file mode 100644 index 000000000..c46400f07 --- /dev/null +++ b/app/views/user_mailer/confirmation_instructions.ko.text.erb @@ -0,0 +1,10 @@ +안녕하세요 <%= @resource.email %> 님! + +<%= @instance %>에 새로 계정을 만들었습니다. + +아래 링크를 눌러 회원가입을 완료 하세요. +<%= confirmation_url(@resource, confirmation_token: @token) %> + +약관도 확인 바랍니다. <%= terms_url %> + +<%= @instance %> 드림 diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 6fdc3b985..3a7636dbb 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -60,6 +60,7 @@ ko: email: E-mail feed_url: 피드 URL followers: 팔로워 수 + followers_url: 팔로워 URL follows: 팔로잉 수 inbox_url: Inbox URL ip: IP @@ -106,6 +107,18 @@ ko: unsubscribe: 구독 해제 username: 아이디 web: Web + custom_emojis: + created_msg: 에모지가 성공적으로 생성되었습니다! + delete: 삭제 + destroyed_msg: 에모지가 성공적으로 삭제되었습니다! + emoji: Emoji + image_hint: 50KB 이하의 PNG + new: + title: 새 커스텀 에모지 추가 + shortcode: Shortcode + shortcode_hint: 최소 2글자, 영문자, 숫자, _만 사용 가능 + title: 커스텀 에모지 + upload: 업로드 domain_blocks: add_new: 추가하기 created_msg: 도메인 차단 처리를 완료했습니다. @@ -141,6 +154,8 @@ ko: instances: account_count: 알려진 계정의 수 domain_name: 도메인 이름 + reset: 리셋 + search: 검색 title: 알려진 인스턴스들 reports: action_taken_by: 신고 처리자 @@ -167,6 +182,9 @@ ko: unresolved: 미해결 view: 표시 settings: + bootstrap_timeline_accounts: + desc_html: 콤마로 여러 유저명을 구분. 로컬의 잠기지 않은 계정만 가능합니다. 비워 둘 경우 모든 로컬 관리자가 기본으로 사용 됩니다. + title: 새 유저가 팔로우 할 계정들 contact_information: email: 공개할 메일 주소를 입력 username: 아이디를 입력 @@ -190,6 +208,9 @@ ko: desc_html: 당신은 독자적인 개인정보 취급 방침이나 이용약관, 그 외의 법적 근거를 작성할 수 있습니다. 또한 HTML태그를 사용할 수 있습니다. title: 커스텀 서비스 이용 약관 site_title: 사이트 이름 + thumbnail: + desc_html: OpenGraph와 API의 미리보기로 사용 됩니다. 1200x630px을 권장합니다 + title: 인스턴스 썸네일 timeline_preview: desc_html: Landing page에 공개 타임라인을 표시합니다. title: 타임라인 프리뷰 @@ -287,6 +308,9 @@ ko: content: 보안 인증에 실패했습니다. Cookie를 차단하고 있진 않습니까? title: 보안 인증 실패 '429': 요청 횟수 제한에 도달했습니다. + '500': + content: 죄송합니다, 뭔가 잘못 되었습니다. + title: 이 페이지는 잘못되었습니다. noscript_html: Mastodon을 사용하기 위해서는 JavaScript를 켜 주십시오. exports: blocks: 차단 @@ -338,8 +362,8 @@ ko: one: "1건의 새로운 알림 \U0001F418" other: "%{count}건의 새로운 알림 \U0001F418" favourite: - body: "%{name} 님이 내 Toot를 즐겨찾기에 등록했습니다." - subject: "%{name} 님이 내 Toot를 즐겨찾기에 등록했습니다" + body: "%{name} 님이 내 Toot을 즐겨찾기에 등록했습니다." + subject: "%{name} 님이 내 Toot을 즐겨찾기에 등록했습니다" follow: body: "%{name} 님이 나를 팔로우 했습니다" subject: "%{name} 님이 나를 팔로우 했습니다" @@ -367,6 +391,12 @@ ko: next: 다음 prev: 이전 truncate: "…" + preferences: + languages: 언어 + notifications: 알림 + other: 기타 + publishing: 퍼블리싱 + web: 웹 push_notifications: favourite: title: "%{name} 님이 당신의 Toot를 즐겨찾기에 등록했습니다." @@ -442,8 +472,9 @@ ko: open_in_web: Web으로 열기 over_character_limit: 최대 %{max}자까지 입력할 수 있습니다 pin_errors: - ownership: 다른 사람의 Toot는 고정될 수 없습니다. - private: 비공개 Toot는 고정될 수 없습니다. + limit: 너무 많은 툿을 고정했습니다. + ownership: 다른 사람의 툿은 고정될 수 없습니다. + private: 비공개 툿은 고정될 수 없습니다. reblog: 부스트는 고정될 수 없습니다. show_more: 더 보기 visibilities: @@ -455,11 +486,13 @@ ko: unlisted_long: 누구나 볼 수 있지만, 공개 타임라인에는 표시되지 않습니다 stream_entries: click_to_show: 클릭해서 표시 - pinned: 고정된 Toot + pinned: 고정된 툿 reblogged: 님이 부스트 했습니다 sensitive_content: 민감한 컨텐츠 terms: title: "%{instance} 이용약관과 개인정보 취급 방침" + themes: + default: Mastodon time: formats: default: "%Y년 %m월 %d일 %H:%M" -- cgit From 0db47196fbbff4e45e8a35cfd78489b35b8605c4 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Tue, 3 Oct 2017 06:27:52 +0900 Subject: Fix stupid typo (#5184) --- app/views/user_mailer/confirmation_instructions.en.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/views') diff --git a/app/views/user_mailer/confirmation_instructions.en.html.erb b/app/views/user_mailer/confirmation_instructions.en.html.erb index 885c0b56a..cd0d70377 100644 --- a/app/views/user_mailer/confirmation_instructions.en.html.erb +++ b/app/views/user_mailer/confirmation_instructions.en.html.erb @@ -2,10 +2,10 @@

You just created an account on <%= @instance %>.

-

If the above link did not work, copy and paste this URL into your address bar:
+

To confirm your inscription, please click on the following link :
<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

-

If above link not worked, Copy and paste this URL

+

If the above link did not work, copy and paste this URL into your address bar:
<%= confirmation_url(@resource, confirmation_token: @token) %>

Please also check out our <%= link_to 'terms and conditions', terms_url %>.

-- 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 'app/views') 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 'app/views') 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 43577e9f5966df5029167f8450afca5de782cebb Mon Sep 17 00:00:00 2001 From: Lynx Kotoura Date: Thu, 5 Oct 2017 01:16:30 +0900 Subject: Fix style of email domain block table (#5218) --- app/views/admin/email_domain_blocks/index.html.haml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'app/views') diff --git a/app/views/admin/email_domain_blocks/index.html.haml b/app/views/admin/email_domain_blocks/index.html.haml index fbdb3b80b..7bb204e52 100644 --- a/app/views/admin/email_domain_blocks/index.html.haml +++ b/app/views/admin/email_domain_blocks/index.html.haml @@ -1,13 +1,14 @@ - 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 +.table-wrapper + %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' -- cgit From b406e3cc4cfc1b51a276d9e0e8a9910f1ca529e2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Oct 2017 19:06:23 +0200 Subject: Fix #5050 - Use summary_large_image only with media attachments (#5219) --- app/views/stream_entries/_og_image.html.haml | 2 ++ app/views/stream_entries/show.html.haml | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'app/views') diff --git a/app/views/stream_entries/_og_image.html.haml b/app/views/stream_entries/_og_image.html.haml index b5058583b..1056c1744 100644 --- a/app/views/stream_entries/_og_image.html.haml +++ b/app/views/stream_entries/_og_image.html.haml @@ -17,7 +17,9 @@ - unless media.file.meta.nil? = opengraph 'og:video:width', media.file.meta['small']['width'] = opengraph 'og:video:height', media.file.meta['small']['height'] + = opengraph 'twitter:card', 'summary_large_image' - else = opengraph 'og:image', full_asset_url(account.avatar.url(:original)) = opengraph 'og:image:width', '120' = opengraph 'og:image:height','120' + = opengraph 'twitter:card', 'summary' diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml index 1bb8a32b2..428069931 100644 --- a/app/views/stream_entries/show.html.haml +++ b/app/views/stream_entries/show.html.haml @@ -14,8 +14,6 @@ = render 'stream_entries/og_description', activity: @stream_entry.activity = render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account - = opengraph 'twitter:card', 'summary_large_image' - - if show_landing_strip? = render partial: 'shared/landing_strip', locals: { account: @stream_entry.account } -- cgit From 49cc0eb3e7d1521079e33a60216df46679082547 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Oct 2017 23:42:05 +0200 Subject: Improve admin UI for custom emojis, add copy/disable/enable (#5231) --- app/controllers/admin/custom_emojis_controller.rb | 42 ++++- app/controllers/api/v1/custom_emojis_controller.rb | 2 +- app/models/account_filter.rb | 2 + app/models/custom_emoji.rb | 11 +- app/models/custom_emoji_filter.rb | 34 ++++ .../admin/custom_emojis/_custom_emoji.html.haml | 13 ++ app/views/admin/custom_emojis/index.html.haml | 20 +++ config/brakeman.ignore | 182 ++++++++++++++++++++- config/locales/de.yml | 6 +- config/locales/en.yml | 7 + config/routes.rb | 8 +- ...20171005171936_add_disabled_to_custom_emojis.rb | 15 ++ db/schema.rb | 3 +- 13 files changed, 330 insertions(+), 15 deletions(-) create mode 100644 app/models/custom_emoji_filter.rb create mode 100644 db/migrate/20171005171936_add_disabled_to_custom_emojis.rb (limited to 'app/views') diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index d70514d9a..dba9f1012 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -2,8 +2,10 @@ module Admin class CustomEmojisController < BaseController + before_action :set_custom_emoji, except: [:index, :new, :create] + def index - @custom_emojis = CustomEmoji.local + @custom_emojis = filtered_custom_emojis.page(params[:page]) end def new @@ -21,14 +23,50 @@ module Admin end def destroy - CustomEmoji.find(params[:id]).destroy + @custom_emoji.destroy redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg') end + def copy + emoji = @custom_emoji.dup + emoji.domain = nil + + if emoji.save + redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.copied_msg') + else + redirect_to admin_custom_emojis_path, alert: I18n.t('admin.custom_emojis.copy_failed_msg') + end + end + + def enable + @custom_emoji.update!(disabled: false) + redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.enabled_msg') + end + + def disable + @custom_emoji.update!(disabled: true) + redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.disabled_msg') + end + private + def set_custom_emoji + @custom_emoji = CustomEmoji.find(params[:id]) + end + def resource_params params.require(:custom_emoji).permit(:shortcode, :image) end + + def filtered_custom_emojis + CustomEmojiFilter.new(filter_params).results + end + + def filter_params + params.permit( + :local, + :remote + ) + end end end diff --git a/app/controllers/api/v1/custom_emojis_controller.rb b/app/controllers/api/v1/custom_emojis_controller.rb index 4dd77fb55..f8cd64455 100644 --- a/app/controllers/api/v1/custom_emojis_controller.rb +++ b/app/controllers/api/v1/custom_emojis_controller.rb @@ -4,6 +4,6 @@ class Api::V1::CustomEmojisController < Api::BaseController respond_to :json def index - render json: CustomEmoji.local, each_serializer: REST::CustomEmojiSerializer + render json: CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer end end diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb index 1a8cc5192..189872368 100644 --- a/app/models/account_filter.rb +++ b/app/models/account_filter.rb @@ -9,9 +9,11 @@ class AccountFilter def results scope = Account.alphabetic + params.each do |key, value| scope.merge!(scope_for(key, value)) if value.present? end + scope end diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 9e9be5e12..258b50c82 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -12,6 +12,7 @@ # image_updated_at :datetime # created_at :datetime not null # updated_at :datetime not null +# disabled :boolean default(FALSE), not null # class CustomEmoji < ApplicationRecord @@ -26,10 +27,16 @@ class CustomEmoji < ApplicationRecord validates_attachment :image, content_type: { content_type: 'image/png' }, presence: true, size: { in: 0..50.kilobytes } validates :shortcode, uniqueness: { scope: :domain }, format: { with: /\A#{SHORTCODE_RE_FRAGMENT}\z/ }, length: { minimum: 2 } - scope :local, -> { where(domain: nil) } + scope :local, -> { where(domain: nil) } + scope :remote, -> { where.not(domain: nil) } + scope :alphabetic, -> { order(domain: :asc, shortcode: :asc) } include Remotable + def local? + domain.nil? + end + class << self def from_text(text, domain) return [] if text.blank? @@ -38,7 +45,7 @@ class CustomEmoji < ApplicationRecord return [] if shortcodes.empty? - where(shortcode: shortcodes, domain: domain) + where(shortcode: shortcodes, domain: domain, disabled: false) end end end diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb new file mode 100644 index 000000000..2d1394a59 --- /dev/null +++ b/app/models/custom_emoji_filter.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class CustomEmojiFilter + attr_reader :params + + def initialize(params) + @params = params + end + + def results + scope = CustomEmoji.alphabetic + + params.each do |key, value| + scope.merge!(scope_for(key, value)) if value.present? + end + + scope + end + + private + + def scope_for(key, value) + case key.to_s + when 'local' + CustomEmoji.local + when 'remote' + CustomEmoji.remote + when 'by_domain' + CustomEmoji.where(domain: value) + else + raise "Unknown filter: #{key}" + end + end +end diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml index ff1aa9925..53263c43f 100644 --- a/app/views/admin/custom_emojis/_custom_emoji.html.haml +++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml @@ -3,5 +3,18 @@ = image_tag custom_emoji.image.url, class: 'emojione', alt: ":#{custom_emoji.shortcode}:" %td %samp= ":#{custom_emoji.shortcode}:" + %td + - if custom_emoji.local? + = t('admin.accounts.location.local') + - else + = custom_emoji.domain + %td + - unless custom_emoji.local? + = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji), method: :post + %td + - if custom_emoji.disabled? + = table_link_to 'power-off', t('admin.custom_emojis.enable'), enable_admin_custom_emoji_path(custom_emoji), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } + - else + = table_link_to 'power-off', t('admin.custom_emojis.disable'), disable_admin_custom_emoji_path(custom_emoji), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } %td = table_link_to 'times', t('admin.custom_emojis.delete'), admin_custom_emoji_path(custom_emoji), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index d5f32e84b..20ffb8529 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -1,14 +1,34 @@ - content_for :page_title do = t('admin.custom_emojis.title') +.filters + .filter-subset + %strong= t('admin.accounts.location.title') + %ul + %li= filter_link_to t('admin.accounts.location.all'), local: nil, remote: nil + %li + - if selected? local: '1', remote: nil + = filter_link_to t('admin.accounts.location.local'), {local: nil, remote: nil}, {local: '1', remote: nil} + - else + = filter_link_to t('admin.accounts.location.local'), local: '1', remote: nil + %li + - if selected? remote: '1', local: nil + = filter_link_to t('admin.accounts.location.remote'), {remote: nil, local: nil}, {remote: '1', local: nil} + - else + = filter_link_to t('admin.accounts.location.remote'), remote: '1', local: nil + .table-wrapper %table.table %thead %tr %th= t('admin.custom_emojis.emoji') %th= t('admin.custom_emojis.shortcode') + %th= t('admin.accounts.domain') + %th + %th %th %tbody = render @custom_emojis += paginate @custom_emojis = link_to t('admin.custom_emojis.upload'), new_admin_custom_emoji_path, class: 'button' diff --git a/config/brakeman.ignore b/config/brakeman.ignore index dbb59dd07..ed6e121d2 100644 --- a/config/brakeman.ignore +++ b/config/brakeman.ignore @@ -1,5 +1,81 @@ { "ignored_warnings": [ + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "0adbe361b91afff22ba51e5fc2275ec703cc13255a0cb3eecd8dab223ab9f61e", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in link_to href", + "file": "app/views/admin/accounts/show.html.haml", + "line": 122, + "link": "http://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(Account.find(params[:id]).inbox_url, Account.find(params[:id]).inbox_url)", + "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}], + "location": { + "type": "template", + "template": "admin/accounts/show" + }, + "user_input": "Account.find(params[:id]).inbox_url", + "confidence": "Weak", + "note": "" + }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "1fc29c578d0c89bf13bd5476829d272d54cd06b92ccf6df18568fa1f2674926e", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in link_to href", + "file": "app/views/admin/accounts/show.html.haml", + "line": 128, + "link": "http://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(Account.find(params[:id]).shared_inbox_url, Account.find(params[:id]).shared_inbox_url)", + "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}], + "location": { + "type": "template", + "template": "admin/accounts/show" + }, + "user_input": "Account.find(params[:id]).shared_inbox_url", + "confidence": "Weak", + "note": "" + }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "2129d4c1e63a351d28d8d2937ff0b50237809c3df6725c0c5ef82b881dbb2086", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in link_to href", + "file": "app/views/admin/accounts/show.html.haml", + "line": 35, + "link": "http://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(Account.find(params[:id]).url, Account.find(params[:id]).url)", + "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}], + "location": { + "type": "template", + "template": "admin/accounts/show" + }, + "user_input": "Account.find(params[:id]).url", + "confidence": "Weak", + "note": "" + }, + { + "warning_type": "Dynamic Render Path", + "warning_code": 15, + "fingerprint": "3b0a20b08aef13cf8cf865384fae0cfd3324d8200a83262bf4abbc8091b5fec5", + "check_name": "Render", + "message": "Render path contains parameter value", + "file": "app/views/admin/custom_emojis/index.html.haml", + "line": 31, + "link": "http://brakemanscanner.org/docs/warning_types/dynamic_render_path/", + "code": "render(action => filtered_custom_emojis.page(params[:page]), {})", + "render_path": [{"type":"controller","class":"Admin::CustomEmojisController","method":"index","line":9,"file":"app/controllers/admin/custom_emojis_controller.rb"}], + "location": { + "type": "template", + "template": "admin/custom_emojis/index" + }, + "user_input": "params[:page]", + "confidence": "Weak", + "note": "" + }, { "warning_type": "Dynamic Render Path", "warning_code": 15, @@ -19,6 +95,44 @@ "confidence": "Weak", "note": "" }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "64b5b2a02ede9c2b3598881eb5a466d63f7d27fe0946aa00d570111ec7338d2e", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in link_to href", + "file": "app/views/admin/accounts/show.html.haml", + "line": 131, + "link": "http://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(Account.find(params[:id]).followers_url, Account.find(params[:id]).followers_url)", + "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}], + "location": { + "type": "template", + "template": "admin/accounts/show" + }, + "user_input": "Account.find(params[:id]).followers_url", + "confidence": "Weak", + "note": "" + }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "82f7b0d09beb3ab68e0fa16be63cedf4e820f2490326e9a1cec05761d92446cd", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in link_to href", + "file": "app/views/admin/accounts/show.html.haml", + "line": 106, + "link": "http://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(Account.find(params[:id]).salmon_url, Account.find(params[:id]).salmon_url)", + "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}], + "location": { + "type": "template", + "template": "admin/accounts/show" + }, + "user_input": "Account.find(params[:id]).salmon_url", + "confidence": "Weak", + "note": "" + }, { "warning_type": "Dynamic Render Path", "warning_code": 15, @@ -26,7 +140,7 @@ "check_name": "Render", "message": "Render path contains parameter value", "file": "app/views/admin/accounts/index.html.haml", - "line": 63, + "line": 64, "link": "http://brakemanscanner.org/docs/warning_types/dynamic_render_path/", "code": "render(action => filtered_accounts.page(params[:page]), {})", "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"index","line":10,"file":"app/controllers/admin/accounts_controller.rb"}], @@ -38,6 +152,25 @@ "confidence": "Weak", "note": "" }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "bb0ad5c4a42e06e3846c2089ff5269c17f65483a69414f6ce65eecf2bb11fab7", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in link_to href", + "file": "app/views/admin/accounts/show.html.haml", + "line": 95, + "link": "http://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(Account.find(params[:id]).remote_url, Account.find(params[:id]).remote_url)", + "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}], + "location": { + "type": "template", + "template": "admin/accounts/show" + }, + "user_input": "Account.find(params[:id]).remote_url", + "confidence": "Weak", + "note": "" + }, { "warning_type": "Redirect", "warning_code": 18, @@ -65,7 +198,7 @@ "check_name": "Render", "message": "Render path contains parameter value", "file": "app/views/admin/reports/index.html.haml", - "line": 24, + "line": 25, "link": "http://brakemanscanner.org/docs/warning_types/dynamic_render_path/", "code": "render(action => filtered_reports.page(params[:page]), {})", "render_path": [{"type":"controller","class":"Admin::ReportsController","method":"index","line":9,"file":"app/controllers/admin/reports_controller.rb"}], @@ -77,6 +210,45 @@ "confidence": "Weak", "note": "" }, + { + "warning_type": "SQL Injection", + "warning_code": 0, + "fingerprint": "cd440d9d0bcb76225f4142030cec0bdec6ad119c537c108c9d514bf87bc34d29", + "check_name": "SQL", + "message": "Possible SQL injection", + "file": "lib/mastodon/timestamp_ids.rb", + "line": 69, + "link": "http://brakemanscanner.org/docs/warning_types/sql_injection/", + "code": "ActiveRecord::Base.connection.execute(\" CREATE OR REPLACE FUNCTION timestamp_id(table_name text)\\n RETURNS bigint AS\\n $$\\n DECLARE\\n time_part bigint;\\n sequence_base bigint;\\n tail bigint;\\n BEGIN\\n -- Our ID will be composed of the following:\\n -- 6 bytes (48 bits) of millisecond-level timestamp\\n -- 2 bytes (16 bits) of sequence data\\n\\n -- The 'sequence data' is intended to be unique within a\\n -- given millisecond, yet obscure the 'serial number' of\\n -- this row.\\n\\n -- To do this, we hash the following data:\\n -- * Table name (if provided, skipped if not)\\n -- * Secret salt (should not be guessable)\\n -- * Timestamp (again, millisecond-level granularity)\\n\\n -- We then take the first two bytes of that value, and add\\n -- the lowest two bytes of the table ID sequence number\\n -- (`table_name`_id_seq). This means that even if we insert\\n -- two rows at the same millisecond, they will have\\n -- distinct 'sequence data' portions.\\n\\n -- If this happens, and an attacker can see both such IDs,\\n -- they can determine which of the two entries was inserted\\n -- first, but not the total number of entries in the table\\n -- (even mod 2**16).\\n\\n -- The table name is included in the hash to ensure that\\n -- different tables derive separate sequence bases so rows\\n -- inserted in the same millisecond in different tables do\\n -- not reveal the table ID sequence number for one another.\\n\\n -- The secret salt is included in the hash to ensure that\\n -- external users cannot derive the sequence base given the\\n -- timestamp and table name, which would allow them to\\n -- compute the table ID sequence number.\\n\\n time_part := (\\n -- Get the time in milliseconds\\n ((date_part('epoch', now()) * 1000))::bigint\\n -- And shift it over two bytes\\n << 16);\\n\\n sequence_base := (\\n 'x' ||\\n -- Take the first two bytes (four hex characters)\\n substr(\\n -- Of the MD5 hash of the data we documented\\n md5(table_name ||\\n '#{SecureRandom.hex(16)}' ||\\n time_part::text\\n ),\\n 1, 4\\n )\\n -- And turn it into a bigint\\n )::bit(16)::bigint;\\n\\n -- Finally, add our sequence number to our base, and chop\\n -- it to the last two bytes\\n tail := (\\n (sequence_base + nextval(table_name || '_id_seq'))\\n & 65535);\\n\\n -- Return the time part and the sequence part. OR appears\\n -- faster here than addition, but they're equivalent:\\n -- time_part has no trailing two bytes, and tail is only\\n -- the last two bytes.\\n RETURN time_part | tail;\\n END\\n $$ LANGUAGE plpgsql VOLATILE;\\n\")", + "render_path": null, + "location": { + "type": "method", + "class": "Mastodon::TimestampIds", + "method": "s(:self).define_timestamp_id" + }, + "user_input": "SecureRandom.hex(16)", + "confidence": "Medium", + "note": "" + }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "e04aafe1e06cf8317fb6ac0a7f35783e45aa1274272ee6eaf28d39adfdad489b", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in link_to href", + "file": "app/views/admin/accounts/show.html.haml", + "line": 125, + "link": "http://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(Account.find(params[:id]).outbox_url, Account.find(params[:id]).outbox_url)", + "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}], + "location": { + "type": "template", + "template": "admin/accounts/show" + }, + "user_input": "Account.find(params[:id]).outbox_url", + "confidence": "Weak", + "note": "" + }, { "warning_type": "Dynamic Render Path", "warning_code": 15, @@ -84,7 +256,7 @@ "check_name": "Render", "message": "Render path contains parameter value", "file": "app/views/stream_entries/show.html.haml", - "line": 23, + "line": 21, "link": "http://brakemanscanner.org/docs/warning_types/dynamic_render_path/", "code": "render(partial => \"stream_entries/#{Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity_type.downcase}\", { :locals => ({ Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity_type.downcase.to_sym => Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity, :include_threads => true }) })", "render_path": [{"type":"controller","class":"StatusesController","method":"show","line":20,"file":"app/controllers/statuses_controller.rb"}], @@ -97,6 +269,6 @@ "note": "" } ], - "updated": "2017-08-30 05:14:04 +0200", - "brakeman_version": "3.7.2" + "updated": "2017-10-05 20:06:40 +0200", + "brakeman_version": "4.0.1" } diff --git a/config/locales/de.yml b/config/locales/de.yml index ec48bd5ff..7c0edff94 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -137,7 +137,7 @@ de: reject_media: Mediendateien ablehnen reject_media_hint: Entfernt lokal gespeicherte Mediendateien und verhindert deren künftiges Herunterladen. Für Sperren irrelevant severities: - none: Kein + noop: Kein silence: Stummschaltung suspend: Sperren severity: Schweregrad @@ -180,7 +180,7 @@ de: nsfw: 'false': Medienanhänge wieder anzeigen 'true': Medienanhänge verbergen - report: "Meldung #%{id}" + report: 'Meldung #%{id}' report_contents: Inhalt reported_account: Gemeldetes Konto reported_by: Gemeldet von @@ -386,7 +386,7 @@ de: body: "%{name} hat dich erwähnt:" subject: "%{name} hat dich erwähnt" reblog: - body: '%{name} hat deinen Beitrag geteilt:' + body: "%{name} hat deinen Beitrag geteilt:" subject: "%{name} hat deinen Beitrag geteilt" number: human: diff --git a/config/locales/en.yml b/config/locales/en.yml index 5d9557535..2059c5e2b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -109,10 +109,17 @@ en: username: Username web: Web custom_emojis: + copied_msg: Successfully created local copy of the emoji + copy: Copy + copy_failed_msg: Could not make a local copy of that emoji created_msg: Emoji successfully created! delete: Delete destroyed_msg: Emojo successfully destroyed! + disable: Disable + disabled_msg: Successfully disabled that emoji emoji: Emoji + enable: Enable + enabled_msg: Successfully enabled that emoji image_hint: PNG up to 50KB new: title: Add new custom emoji diff --git a/config/routes.rb b/config/routes.rb index 959afc23f..cc1f66e52 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -139,7 +139,13 @@ Rails.application.routes.draw do resource :two_factor_authentication, only: [:destroy] end - resources :custom_emojis, only: [:index, :new, :create, :destroy] + resources :custom_emojis, only: [:index, :new, :create, :destroy] do + member do + post :copy + post :enable + post :disable + end + end end get '/admin', to: redirect('/admin/settings/edit', status: 302) diff --git a/db/migrate/20171005171936_add_disabled_to_custom_emojis.rb b/db/migrate/20171005171936_add_disabled_to_custom_emojis.rb new file mode 100644 index 000000000..067a7bee0 --- /dev/null +++ b/db/migrate/20171005171936_add_disabled_to_custom_emojis.rb @@ -0,0 +1,15 @@ +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddDisabledToCustomEmojis < ActiveRecord::Migration[5.1] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def up + safety_assured { add_column_with_default :custom_emojis, :disabled, :bool, default: false } + end + + def down + remove_column :custom_emojis, :disabled + end +end diff --git a/db/schema.rb b/db/schema.rb index 337678c67..3358e2997 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: 20170928082043) do +ActiveRecord::Schema.define(version: 20171005171936) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -98,6 +98,7 @@ ActiveRecord::Schema.define(version: 20170928082043) do t.datetime "image_updated_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.boolean "disabled", default: false, null: false t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true 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 'app/views') 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 'app/views') 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 6e9e0c14e6be915f18fbb8090276993c7f4415d7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 9 Oct 2017 10:05:10 +0200 Subject: Fix dependency of error page generation on database (#5280) --- app/views/layouts/error.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/views/layouts/error.html.haml b/app/views/layouts/error.html.haml index 7d014dff4..37359b89b 100644 --- a/app/views/layouts/error.html.haml +++ b/app/views/layouts/error.html.haml @@ -9,6 +9,6 @@ = stylesheet_pack_tag Setting.default_settings['theme'], media: 'all' %body.error .dialog - %img{ alt: title, src: '/oops.gif' }/ + %img{ alt: Setting.default_settings['site_title'], src: '/oops.gif' }/ %div %h1= yield :content -- cgit From 552d22bec9602a64616538f7df0bdac13140c7f8 Mon Sep 17 00:00:00 2001 From: takayamaki Date: Wed, 11 Oct 2017 07:52:25 +0900 Subject: sign_in and sign_up views present og meta infos (#5308) --- app/controllers/auth/registrations_controller.rb | 5 +++++ app/controllers/auth/sessions_controller.rb | 5 +++++ app/views/about/_og.html.haml | 10 ---------- app/views/about/more.html.haml | 2 +- app/views/about/show.html.haml | 2 +- app/views/auth/registrations/new.html.haml | 3 +++ app/views/auth/sessions/new.html.haml | 3 +++ app/views/shared/_og.html.haml | 10 ++++++++++ 8 files changed, 28 insertions(+), 12 deletions(-) delete mode 100644 app/views/about/_og.html.haml create mode 100644 app/views/shared/_og.html.haml (limited to 'app/views') diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 60ace04d7..aac3c31ff 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -6,6 +6,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :check_enabled_registrations, only: [:new, :create] before_action :configure_sign_up_params, only: [:create] before_action :set_sessions, only: [:edit, :update] + before_action :set_instance_presenter, only: [:new, :update] def destroy not_found @@ -39,6 +40,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController private + def set_instance_presenter + @instance_presenter = InstancePresenter.new + end + def determine_layout %w(edit update).include?(action_name) ? 'admin' : 'auth' end diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index bc3bd2f4b..463a183e4 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -8,6 +8,7 @@ class Auth::SessionsController < Devise::SessionsController skip_before_action :require_no_authentication, only: [:create] skip_before_action :check_suspension, only: [:destroy] prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] + before_action :set_instance_presenter, only: [:new] def create super do |resource| @@ -84,6 +85,10 @@ class Auth::SessionsController < Devise::SessionsController private + def set_instance_presenter + @instance_presenter = InstancePresenter.new + end + def home_paths(resource) paths = [about_path] if single_user_mode? && resource.is_a?(User) diff --git a/app/views/about/_og.html.haml b/app/views/about/_og.html.haml deleted file mode 100644 index dbd476915..000000000 --- a/app/views/about/_og.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -- thumbnail = @instance_presenter.thumbnail -= opengraph 'og:site_name', t('about.hosted_on', domain: site_hostname) -= opengraph 'og:url', about_url -= opengraph 'og:type', 'website' -= opengraph 'og:title', @instance_presenter.site_title -= opengraph 'og:description', strip_tags(@instance_presenter.site_description.presence || t('about.about_mastodon_html')) -= opengraph 'og:image', full_asset_url(thumbnail&.file&.url || asset_pack_path('preview.jpg', protocol: :request)) -= opengraph 'og:image:width', thumbnail ? thumbnail.meta['width'] : '1200' -= opengraph 'og:image:height', thumbnail ? thumbnail.meta['height'] : '630' -= opengraph 'twitter:card', 'summary_large_image' diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml index 1a4e74643..b012606ce 100644 --- a/app/views/about/more.html.haml +++ b/app/views/about/more.html.haml @@ -3,7 +3,7 @@ - content_for :header_tags do = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' - = render partial: 'og' + = render partial: 'shared/og' .landing-page .header-wrapper.compact diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index ef27d07a1..f8f90ce24 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -4,7 +4,7 @@ - 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 partial: 'og' + = render partial: 'shared/og' .landing-page .header-wrapper diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index 807020310..f71675df0 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -1,6 +1,9 @@ - content_for :page_title do = t('auth.register') +- content_for :header_tags do + = render partial: 'shared/og' + = simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = render 'shared/error_messages', object: resource diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml index e589377bf..a52b0053b 100644 --- a/app/views/auth/sessions/new.html.haml +++ b/app/views/auth/sessions/new.html.haml @@ -1,6 +1,9 @@ - content_for :page_title do = t('auth.login') +- content_for :header_tags do + = render partial: 'shared/og' + = simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| = f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') } = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' } diff --git a/app/views/shared/_og.html.haml b/app/views/shared/_og.html.haml new file mode 100644 index 000000000..dbd476915 --- /dev/null +++ b/app/views/shared/_og.html.haml @@ -0,0 +1,10 @@ +- thumbnail = @instance_presenter.thumbnail += opengraph 'og:site_name', t('about.hosted_on', domain: site_hostname) += opengraph 'og:url', about_url += opengraph 'og:type', 'website' += opengraph 'og:title', @instance_presenter.site_title += opengraph 'og:description', strip_tags(@instance_presenter.site_description.presence || t('about.about_mastodon_html')) += opengraph 'og:image', full_asset_url(thumbnail&.file&.url || asset_pack_path('preview.jpg', protocol: :request)) += opengraph 'og:image:width', thumbnail ? thumbnail.meta['width'] : '1200' += opengraph 'og:image:height', thumbnail ? thumbnail.meta['height'] : '630' += opengraph 'twitter:card', 'summary_large_image' -- cgit