From: Thibaut Girka Date: Mon, 19 Aug 2019 17:31:32 +0000 (+0200) Subject: Merge branch 'master' into glitch-soc/merge-upstream X-Git-Url: https://git.xn--scling-oua.cat.family/?a=commitdiff_plain;h=1488be7d9683fb3442c6f7bd4fd911d224f0c929;p=mastodon.git Merge branch 'master' into glitch-soc/merge-upstream Conflicts: - app/controllers/home_controller.rb - app/controllers/shares_controller.rb - app/javascript/packs/public.js - app/models/status.rb - app/serializers/initial_state_serializer.rb - app/views/home/index.html.haml - app/views/layouts/public.html.haml - app/views/public_timelines/show.html.haml - app/views/shares/show.html.haml - app/views/tags/show.html.haml - config/initializers/content_security_policy.rb - config/locales/en.yml - config/webpack/shared.js - package.json --- 1488be7d9683fb3442c6f7bd4fd911d224f0c929 diff --cc app/controllers/about_controller.rb index f41e52aae,5e942e5c0..5003ae61c --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@@ -1,13 -1,14 +1,15 @@@ # frozen_string_literal: true class AboutController < ApplicationController + before_action :set_pack layout 'public' - before_action :require_open_federation!, only: [:show, :more] + before_action :require_open_federation!, only: [:show, :more, :blocks] + before_action :check_blocklist_enabled, only: [:blocks] + before_action :authenticate_user!, only: [:blocks], if: :blocklist_account_required? before_action :set_body_classes, only: :show before_action :set_instance_presenter - before_action :set_expires_in + before_action :set_expires_in, only: [:show, :more, :terms] skip_before_action :require_functional!, only: [:more, :terms] diff --cc app/controllers/home_controller.rb index a09aed801,7c8a18d17..efdb1d226 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@@ -2,10 -2,7 +2,9 @@@ class HomeController < ApplicationController before_action :authenticate_user! + + before_action :set_pack before_action :set_referrer_policy_header - before_action :set_initial_state_json def index @body_classes = 'app-body' @@@ -41,25 -38,6 +40,10 @@@ redirect_to(matches ? tag_path(CGI.unescape(matches[:tag])) : default_redirect_path) end + def set_pack + use_pack 'home' + end + - def set_initial_state_json - serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) - @initial_state_json = serializable_resource.to_json - end - - def initial_state_params - { - settings: Web::Setting.find_by(user: current_user)&.data || {}, - push_subscription: current_account.user.web_push_subscription(current_session), - current_account: current_account, - token: current_session.token, - admin: Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')), - } - end - def default_redirect_path if request.path.start_with?('/web') || whitelist_mode? new_user_session_path diff --cc app/controllers/shares_controller.rb index ada4eec54,6546b8497..e13e7e8b6 --- a/app/controllers/shares_controller.rb +++ b/app/controllers/shares_controller.rb @@@ -4,33 -4,12 +4,17 @@@ class SharesController < ApplicationCon layout 'modal' before_action :authenticate_user! + before_action :set_pack before_action :set_body_classes - def show - serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) - @initial_state_json = serializable_resource.to_json - end + def show; end private - def initial_state_params - text = [params[:title], params[:text], params[:url]].compact.join(' ') - - { - settings: Web::Setting.find_by(user: current_user)&.data || {}, - push_subscription: current_account.user.web_push_subscription(current_session), - current_account: current_account, - token: current_session.token, - admin: Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')), - text: text, - } - end - + def set_pack + use_pack 'share' + end + def set_body_classes @body_classes = 'modal-layout compose-standalone' end diff --cc app/controllers/tags_controller.rb index d6bb28eb5,4dfa05264..c447a3a2b --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@@ -16,13 -16,7 +16,8 @@@ class TagsController < ApplicationContr def show respond_to do |format| format.html do + use_pack 'about' expires_in 0, public: true - - @initial_state_json = ActiveModelSerializers::SerializableResource.new( - InitialStatePresenter.new(settings: {}, token: current_session&.token), - serializer: InitialStateSerializer - ).to_json end format.rss do diff --cc app/javascript/mastodon/locales/ja.json index 3c6d71835,352a00c1e..d28fe4247 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@@ -254,8 -252,8 +256,9 @@@ "navigation_bar.preferences": "ユーザー設定", "navigation_bar.profile_directory": "ディレクトリ", "navigation_bar.public_timeline": "連合タイムライン", + "navigation_bar.misc": "その他", "navigation_bar.security": "セキュリティ", + "notification.and_n_others": "and {count, plural, one {# other} other {# others}}", "notification.favourite": "{name}さんがあなたのトゥートをお気に入りに登録しました", "notification.follow": "{name}さんにフォローされました", "notification.mention": "{name}さんがあなたに返信しました", diff --cc app/javascript/packs/public.js index 6aea119e3,c5cd7129f..e49dcaadb --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@@ -100,6 -117,135 +100,15 @@@ function main() delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); - }); - - delegate(document, '.webapp-btn', 'click', ({ target, button }) => { - if (button !== 0) { - return true; - } - window.location.href = target.href; - return false; - }); - - delegate(document, '.status__content__spoiler-link', 'click', function() { - const contentEl = this.parentNode.parentNode.querySelector('.e-content'); - - if (contentEl.style.display === 'block') { - contentEl.style.display = 'none'; - this.parentNode.style.marginBottom = 0; - } else { - contentEl.style.display = 'block'; - this.parentNode.style.marginBottom = null; - } - - return false; - }); - - delegate(document, '.blocks-table button.icon-button', 'click', function(e) { - e.preventDefault(); - - const classList = this.firstElementChild.classList; - classList.toggle('fa-chevron-down'); - classList.toggle('fa-chevron-up'); - this.parentElement.parentElement.nextElementSibling.classList.toggle('hidden'); - }); - - delegate(document, '.modal-button', 'click', e => { - e.preventDefault(); - - let href; - - if (e.target.nodeName !== 'A') { - href = e.target.parentNode.href; - } else { - href = e.target.href; - } - - window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes'); - }); - - delegate(document, '#account_display_name', 'input', ({ target }) => { - const name = document.querySelector('.card .display-name strong'); - if (name) { - if (target.value) { - name.innerHTML = emojify(escapeTextContentForBrowser(target.value)); - } else { - name.textContent = document.querySelector('#default_account_display_name').textContent; - } - } - }); - - delegate(document, '#account_avatar', 'change', ({ target }) => { - const avatar = document.querySelector('.card .avatar img'); - const [file] = target.files || []; - const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc; - - avatar.src = url; - }); - - const getProfileAvatarAnimationHandler = (swapTo) => { - //animate avatar gifs on the profile page when moused over - return ({ target }) => { - const swapSrc = target.getAttribute(swapTo); - //only change the img source if autoplay is off and the image src is actually different - if(target.getAttribute('data-autoplay') !== 'true' && target.src !== swapSrc) { - target.src = swapSrc; - } - }; - }; - - delegate(document, 'img#profile_page_avatar', 'mouseover', getProfileAvatarAnimationHandler('data-original')); - - delegate(document, 'img#profile_page_avatar', 'mouseout', getProfileAvatarAnimationHandler('data-static')); - - delegate(document, '#account_header', 'change', ({ target }) => { - const header = document.querySelector('.card .card__img img'); - const [file] = target.files || []; - const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc; - - header.src = url; - }); + - delegate(document, '#account_locked', 'change', ({ target }) => { - const lock = document.querySelector('.card .display-name i'); ++ delegate(document, '.blocks-table button.icon-button', 'click', function(e) { ++ e.preventDefault(); + - if (target.checked) { - lock.style.display = 'inline'; - } else { - lock.style.display = 'none'; - } - }); - - delegate(document, '.input-copy input', 'click', ({ target }) => { - target.focus(); - target.select(); - target.setSelectionRange(0, target.value.length); - }); - - delegate(document, '.input-copy button', 'click', ({ target }) => { - const input = target.parentNode.querySelector('.input-copy__wrapper input'); - - const oldReadOnly = input.readonly; - - input.readonly = false; - input.focus(); - input.select(); - input.setSelectionRange(0, input.value.length); - - try { - if (document.execCommand('copy')) { - input.blur(); - target.parentNode.classList.add('copied'); - - setTimeout(() => { - target.parentNode.classList.remove('copied'); - }, 700); - } - } catch (err) { - console.error(err); - } - - input.readonly = oldReadOnly; ++ const classList = this.firstElementChild.classList; ++ classList.toggle('fa-chevron-down'); ++ classList.toggle('fa-chevron-up'); ++ this.parentElement.parentElement.nextElementSibling.classList.toggle('hidden'); ++ }); }); } diff --cc app/models/form/admin_settings.rb index 2c3a7f13b,6bc3ca9f5..57dd3edd9 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@@ -32,10 -28,10 +32,12 @@@ class Form::AdminSetting thumbnail hero mascot + show_reblogs_in_public_timelines + show_replies_in_public_timelines spam_check_enabled trends + show_domain_blocks + show_domain_blocks_rationale ).freeze BOOLEAN_KEYS = %i( diff --cc app/models/status.rb index 642d3cf5e,0538c4e9e..de790027d --- a/app/models/status.rb +++ b/app/models/status.rb @@@ -446,15 -388,10 +446,19 @@@ class Status < ApplicationRecor end end + def marked_local_only? + # match both with and without U+FE0F (the emoji variation selector) + /#{local_only_emoji}\ufe0f?\z/.match?(content) + end + + def local_only_emoji + '👁' + end + + def status_stat + super || build_status_stat + end + private def update_status_stat!(attrs) diff --cc app/serializers/initial_state_serializer.rb index c8f6bec7a,fb53ea314..d40fe3380 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@@ -52,8 -38,11 +53,13 @@@ class InitialStateSerializer < ActiveMo store[:use_pending_items] = object.current_account.user.setting_use_pending_items store[:is_staff] = object.current_account.user.staff? store[:trends] = Setting.trends && object.current_account.user.setting_trends + store[:default_content_type] = object.current_account.user.setting_default_content_type + store[:system_emoji_font] = object.current_account.user.setting_system_emoji_font + else + store[:auto_play_gif] = Setting.auto_play_gif + store[:display_media] = Setting.display_media + store[:reduce_motion] = Setting.reduce_motion + store[:use_blurhash] = Setting.use_blurhash end store diff --cc app/views/home/index.html.haml index 6c5268b61,30c7aab19..9530e612a --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@@ -5,7 -5,8 +5,7 @@@ = preload_link_tag asset_pack_path('features/notifications.js'), crossorigin: 'anonymous' %meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key} - %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) + = render_initial_state - = javascript_pack_tag 'application', integrity: true, crossorigin: 'anonymous' .app-holder#mastodon{ data: { props: Oj.dump(default_props) } } %noscript diff --cc app/views/layouts/public.html.haml index da510fa7a,b9179e23d..fb9ac5cec --- a/app/views/layouts/public.html.haml +++ b/app/views/layouts/public.html.haml @@@ -1,3 -1,7 +1,6 @@@ + - content_for :header_tags do + = render_initial_state - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' + - content_for :content do .public-layout - unless @hide_navbar diff --cc app/views/public_timelines/show.html.haml index f80157c67,07215efdf..591620f64 --- a/app/views/public_timelines/show.html.haml +++ b/app/views/public_timelines/show.html.haml @@@ -3,7 -3,7 +3,6 @@@ - content_for :header_tags do %meta{ name: 'robots', content: 'noindex' }/ - %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) - = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' .page-header %h1= t('about.see_whats_happening') diff --cc app/views/shares/show.html.haml index 4c0390c42,f2f5479a7..28910d3ab --- a/app/views/shares/show.html.haml +++ b/app/views/shares/show.html.haml @@@ -1,4 -1,5 +1,4 @@@ - content_for :header_tags do - %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) + = render_initial_state - = javascript_pack_tag 'share', integrity: true, crossorigin: 'anonymous' #mastodon-compose{ data: { props: Oj.dump(default_props) } } diff --cc app/views/tags/show.html.haml index 1a9c58983,630702277..74d44bd7b --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@@ -5,7 -5,7 +5,6 @@@ %meta{ name: 'robots', content: 'noindex' }/ %link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/ - %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) - = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' = render 'og' .page-header diff --cc config/initializers/content_security_policy.rb index 2fe1a33fa,2dbc15a8d..c8396c773 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@@ -2,42 -2,39 +2,42 @@@ # For further information see the following documentation # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy -def host_to_url(str) - "http#{Rails.configuration.x.use_https ? 's' : ''}://#{str}" unless str.blank? -end - -base_host = Rails.configuration.x.web_domain - -assets_host = Rails.configuration.action_controller.asset_host -assets_host ||= host_to_url(base_host) - -media_host = host_to_url(ENV['S3_ALIAS_HOST']) -media_host ||= host_to_url(ENV['S3_CLOUDFRONT_HOST']) -media_host ||= host_to_url(ENV['S3_HOSTNAME']) if ENV['S3_ENABLED'] == 'true' -media_host ||= assets_host +if Rails.env.production? + assets_host = Rails.configuration.action_controller.asset_host || "https://#{ENV['WEB_DOMAIN'] || ENV['LOCAL_DOMAIN']}" + data_hosts = [assets_host] + + if ENV['S3_ENABLED'] == 'true' + attachments_host = "https://#{ENV['S3_ALIAS_HOST'] || ENV['S3_CLOUDFRONT_HOST'] || ENV['S3_HOSTNAME'] || "s3-#{ENV['S3_REGION'] || 'us-east-1'}.amazonaws.com"}" + attachments_host = "https://#{Addressable::URI.parse(attachments_host).host}" + elsif ENV['SWIFT_ENABLED'] == 'true' + attachments_host = ENV['SWIFT_OBJECT_URL'] + attachments_host = "https://#{Addressable::URI.parse(attachments_host).host}" + else + attachments_host = nil + end -Rails.application.config.content_security_policy do |p| - p.base_uri :none - p.default_src :none - p.frame_ancestors :none - p.font_src :self, assets_host - p.img_src :self, :https, :data, :blob, assets_host - p.style_src :self, :unsafe_inline, assets_host - p.media_src :self, :https, :data, assets_host - p.frame_src :self, :https - p.manifest_src :self, assets_host + data_hosts << attachments_host unless attachments_host.nil? - if Rails.env.development? - webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{Webpacker.dev_server.host_with_port}" } + if ENV['PAPERCLIP_ROOT_URL'] + url = Addressable::URI.parse(assets_host) + ENV['PAPERCLIP_ROOT_URL'] + data_hosts << "https://#{url.host}" + end - p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url, *webpacker_urls - p.script_src :self, :blob, :unsafe_inline, :unsafe_eval, assets_host - else - p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url - p.script_src :self, :blob, assets_host + data_hosts.uniq! + + Rails.application.config.content_security_policy do |p| + p.base_uri :none + p.default_src :none + p.frame_ancestors :none + p.script_src :self, assets_host + p.font_src :self, assets_host + p.img_src :self, :data, :blob, *data_hosts + p.style_src :self, :unsafe_inline, assets_host + p.media_src :self, :data, *data_hosts + p.frame_src :self, :https - p.worker_src :self, assets_host ++ p.worker_src :self, :blob, assets_host + p.connect_src :self, :blob, Rails.configuration.x.streaming_api_base_url, *data_hosts + p.manifest_src :self, assets_host end end diff --cc config/locales/en.yml index 79b8e02bd,8d267065c..be190f0f1 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@@ -422,9 -423,13 +424,16 @@@ en custom_css: desc_html: Modify the look with CSS loaded on every page title: Custom CSS + domain_blocks: + all: To everyone + disabled: To no one + title: Show domain blocks + users: To logged-in local users + domain_blocks_rationale: + title: Show rationale + enable_keybase: + desc_html: Allow your users to prove their identity via keybase + title: Enable keybase integration hero: desc_html: Displayed on the frontpage. At least 600x100px recommended. When not set, falls back to server thumbnail title: Hero image diff --cc config/settings.yml index 70a9a3a44,6dbc46706..0e615f8e8 --- a/config/settings.yml +++ b/config/settings.yml @@@ -68,10 -63,9 +68,12 @@@ defaults: &default activity_api_enabled: true peers_api_enabled: true show_known_fediverse_at_about_page: true + show_reblogs_in_public_timelines: false + show_replies_in_public_timelines: false + default_content_type: 'text/plain' spam_check_enabled: true + show_domain_blocks: 'disabled' + show_domain_blocks_rationale: 'disabled' development: <<: *defaults diff --cc config/webpack/shared.js index b6e2537b1,d5e399ced..1741ffdb9 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@@ -5,56 -5,34 +5,57 @@@ const { basename, dirname, join, relati const { sync } = require('glob'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const AssetsManifestPlugin = require('webpack-assets-manifest'); + const CopyPlugin = require('copy-webpack-plugin'); -const extname = require('path-complete-extname'); -const { env, settings, themes, output } = require('./configuration'); +const { env, settings, core, flavours, output } = require('./configuration.js'); const rules = require('./rules'); -const localePackPaths = require('./generateLocalePacks'); +const localePacks = require('./generateLocalePacks'); + +function reducePacks (data, into = {}) { + if (!data.pack) { + return into; + } + Object.keys(data.pack).reduce((map, entry) => { + const pack = data.pack[entry]; + if (!pack) { + return map; + } + const packFile = typeof pack === 'string' ? pack : pack.filename; + if (packFile) { + map[data.name ? `flavours/${data.name}/${entry}` : `core/${entry}`] = resolve(data.pack_directory, packFile); + } + return map; + }, into); + if (data.name) { + Object.keys(data.skin).reduce((map, entry) => { + const skin = data.skin[entry]; + const skinName = entry; + if (!skin) { + return map; + } + Object.keys(skin).reduce((map, entry) => { + const packFile = skin[entry]; + if (!packFile) { + return map; + } + map[`skins/${data.name}/${skinName}/${entry}`] = resolve(packFile); + return map; + }, into); + return map; + }, into); + } + return into; +} + +const entries = Object.assign( + { locales: resolve('app', 'javascript', 'locales') }, + localePacks, + reducePacks(core), + Object.keys(flavours).reduce((map, entry) => reducePacks(flavours[entry], map), {}) +); -const extensionGlob = `**/*{${settings.extensions.join(',')}}*`; -const entryPath = join(settings.source_path, settings.source_entry_path); -const packPaths = sync(join(entryPath, extensionGlob)); module.exports = { - entry: Object.assign( - packPaths.reduce((map, entry) => { - const localMap = map; - const namespace = relative(join(entryPath), dirname(entry)); - localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry); - return localMap; - }, {}), - localePackPaths.reduce((map, entry) => { - const localMap = map; - localMap[basename(entry, extname(entry, extname(entry)))] = resolve(entry); - return localMap; - }, {}), - Object.keys(themes).reduce((themePaths, name) => { - themePaths[name] = resolve(join(settings.source_path, themes[name])); - return themePaths; - }, {}) - ), + entry: entries, output: { filename: 'js/[name]-[chunkhash].js', diff --cc package.json index 061234a31,8f050d042..6bbe87f11 --- a/package.json +++ b/package.json @@@ -96,8 -95,7 +97,8 @@@ "escape-html": "^1.0.3", "exif-js": "^2.3.0", "express": "^4.17.1", + "file-loader": "^4.2.0", + "favico.js": "^0.3.10", - "file-loader": "^4.1.0", "font-awesome": "^4.7.0", "glob": "^7.1.1", "http-link-header": "^1.0.2",