From 4e6b5e78796c1c1074a3bc6a7fd5e5797dd9987e Mon Sep 17 00:00:00 2001 From: unarist Date: Sat, 24 Jun 2017 09:43:26 +0900 Subject: [PATCH 01/37] Use debounce for dispatch scrollTopNotification and expandNotifications (#3700) --- .../mastodon/features/notifications/index.js | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 1dd1b9a71a..ed4b3ad982 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -13,6 +13,7 @@ import ColumnSettingsContainer from './containers/column_settings_container'; import { createSelector } from 'reselect'; import Immutable from 'immutable'; import LoadMore from '../../components/load_more'; +import { debounce } from 'lodash'; const messages = defineMessages({ title: { id: 'column.notifications', defaultMessage: 'Notifications' }, @@ -50,19 +51,27 @@ export default class Notifications extends React.PureComponent { trackScroll: true, }; + dispatchExpandNotifications = debounce(() => { + this.props.dispatch(expandNotifications()); + }, 300, { leading: true }); + + dispatchScrollToTop = debounce((top) => { + this.props.dispatch(scrollTopNotifications(top)); + }, 100); + handleScroll = (e) => { const { scrollTop, scrollHeight, clientHeight } = e.target; const offset = scrollHeight - scrollTop - clientHeight; this._oldScrollPosition = scrollHeight - scrollTop; - if (250 > offset && !this.props.isLoading) { - if (this.props.hasMore) { - this.props.dispatch(expandNotifications()); - } - } else if (scrollTop < 100) { - this.props.dispatch(scrollTopNotifications(true)); + if (250 > offset && this.props.hasMore && !this.props.isLoading) { + this.dispatchExpandNotifications(); + } + + if (scrollTop < 100) { + this.dispatchScrollToTop(true); } else { - this.props.dispatch(scrollTopNotifications(false)); + this.dispatchScrollToTop(false); } } @@ -74,7 +83,7 @@ export default class Notifications extends React.PureComponent { handleLoadMore = (e) => { e.preventDefault(); - this.props.dispatch(expandNotifications()); + this.dispatchExpandNotifications(); } handlePin = () => { From 79dacea96254d8b01f9896e026b27c572401f2a8 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sat, 24 Jun 2017 19:24:02 +0900 Subject: [PATCH 02/37] Fix #3924 (regression from #3906) (#3925) --- .../mastodon/features/favourited_statuses/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.js index 137e55089a..8cef6a1e4e 100644 --- a/app/javascript/mastodon/features/favourited_statuses/index.js +++ b/app/javascript/mastodon/features/favourited_statuses/index.js @@ -1,6 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import LoadingIndicator from '../../components/loading_indicator'; import { fetchFavouritedStatuses, expandFavouritedStatuses } from '../../actions/favourites'; import Column from '../ui/components/column'; @@ -14,7 +15,9 @@ const messages = defineMessages({ }); const mapStateToProps = state => ({ + statusIds: state.getIn(['status_lists', 'favourites', 'items']), loaded: state.getIn(['status_lists', 'favourites', 'loaded']), + me: state.getIn(['meta', 'me']), }); @connect(mapStateToProps) @@ -23,8 +26,10 @@ export default class Favourites extends ImmutablePureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, + statusIds: ImmutablePropTypes.list.isRequired, loaded: PropTypes.bool, intl: PropTypes.object.isRequired, + me: PropTypes.number.isRequired, }; componentWillMount () { From 138e5a0b1ebc7d21c1f2a73c9742cef600c5af40 Mon Sep 17 00:00:00 2001 From: unarist Date: Sat, 24 Jun 2017 21:03:52 +0900 Subject: [PATCH 03/37] Fix webpack config for Windows (#3926) --- config/webpack/loaders/babel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/webpack/loaders/babel.js b/config/webpack/loaders/babel.js index c23aa93751..ae65db9eb5 100644 --- a/config/webpack/loaders/babel.js +++ b/config/webpack/loaders/babel.js @@ -1,7 +1,7 @@ module.exports = { test: /\.js$/, // include react-intl because transform-react-remove-prop-types needs to apply to it - exclude: /node_modules\/(?!react-intl)/, + exclude: /node_modules[\/\\](?!react-intl)/, loader: 'babel-loader', options: { forceEnv: process.env.NODE_ENV || 'development', From d23293c762f703e334607882c66de028fa216ca2 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Sat, 24 Jun 2017 23:17:39 +0200 Subject: [PATCH 04/37] feat(components/onboarding_modal): Swipe between pages (#3934) --- .../mastodon/features/ui/components/onboarding_modal.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/onboarding_modal.js b/app/javascript/mastodon/features/ui/components/onboarding_modal.js index d2e02d63b4..b056357a22 100644 --- a/app/javascript/mastodon/features/ui/components/onboarding_modal.js +++ b/app/javascript/mastodon/features/ui/components/onboarding_modal.js @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import ReactSwipeable from 'react-swipeable'; import classNames from 'classnames'; import Permalink from '../../../components/permalink'; import TransitionMotion from 'react-motion/lib/TransitionMotion'; @@ -274,7 +275,7 @@ export default class OnboardingModal extends React.PureComponent {
{interpolatedStyles => ( -
+ {interpolatedStyles.map(({ key, data, style }, i) => { const className = classNames('onboarding-modal__page__wrapper', { 'onboarding-modal__page__wrapper--active': i === currentIndex, @@ -283,7 +284,7 @@ export default class OnboardingModal extends React.PureComponent {
{data}
); })} -
+ )}
From 21c2bc119ca2bd278339799960914a91fdfa30c1 Mon Sep 17 00:00:00 2001 From: unarist Date: Sun, 25 Jun 2017 06:18:11 +0900 Subject: [PATCH 05/37] Clean column collapsible (#3931) * Remove unused column_collapsable.js * Remove old styles * Extract `> div` style to independent class --- .../mastodon/components/column_collapsable.js | 50 ------------------- .../mastodon/components/column_header.js | 2 +- app/javascript/styles/components.scss | 41 ++------------- 3 files changed, 6 insertions(+), 87 deletions(-) delete mode 100644 app/javascript/mastodon/components/column_collapsable.js diff --git a/app/javascript/mastodon/components/column_collapsable.js b/app/javascript/mastodon/components/column_collapsable.js deleted file mode 100644 index d6b4edb9fe..0000000000 --- a/app/javascript/mastodon/components/column_collapsable.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -export default class ColumnCollapsable extends React.PureComponent { - - static propTypes = { - icon: PropTypes.string.isRequired, - title: PropTypes.string, - fullHeight: PropTypes.number.isRequired, - children: PropTypes.node, - onCollapse: PropTypes.func, - }; - - state = { - collapsed: true, - animating: false, - }; - - handleToggleCollapsed = () => { - const currentState = this.state.collapsed; - - this.setState({ collapsed: !currentState, animating: true }); - - if (!currentState && this.props.onCollapse) { - this.props.onCollapse(); - } - } - - handleTransitionEnd = () => { - this.setState({ animating: false }); - } - - render () { - const { icon, title, fullHeight, children } = this.props; - const { collapsed, animating } = this.state; - - return ( -
-
- -
- -
- {(!collapsed || animating) && children} -
-
- ); - } - -} diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index a309f74e8a..ec9379320f 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -132,7 +132,7 @@ export default class ColumnHeader extends React.PureComponent {
-
+
{(!collapsed || animating) && collapsedContent}
diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index c2062c398f..4b4f72cf7a 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -58,37 +58,6 @@ position: relative; } -.column-collapsable { - position: relative; - - .column-collapsable__content { - overflow: auto; - transition: 300ms ease; - opacity: 1; - max-height: 70vh; - } - - &.collapsed .column-collapsable__content { - height: 0 !important; - opacity: 0; - } - - .column-collapsable__button { - color: $primary-text-color; - background: lighten($ui-base-color, 8%); - - &:hover { - color: $primary-text-color; - background: lighten($ui-base-color, 8%); - } - } - - &.collapsed .column-collapsable__button { - color: $ui-primary-color; - background: lighten($ui-base-color, 4%); - } -} - .column-icon { background: lighten($ui-base-color, 4%); color: $ui-primary-color; @@ -2110,11 +2079,6 @@ button.icon-button.active i.fa-retweet { transition: max-height 150ms ease-in-out, opacity 300ms linear; opacity: 1; - & > div { - background: lighten($ui-base-color, 8%); - padding: 15px; - } - &.collapsed { max-height: 0; opacity: 0.5; @@ -2125,6 +2089,11 @@ button.icon-button.active i.fa-retweet { } } +.column-header__collapsible-inner { + background: lighten($ui-base-color, 8%); + padding: 15px; +} + .column-header__setting-btn { &:hover { color: lighten($ui-primary-color, 4%); From 1fc096ec7598e688b1cd804188575a853ac84541 Mon Sep 17 00:00:00 2001 From: unarist Date: Sun, 25 Jun 2017 06:18:32 +0900 Subject: [PATCH 06/37] Fix elephant in onboarding modal being very small sized on small devices (#3932) --- app/javascript/styles/components.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 4b4f72cf7a..bb9723f5a4 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -3126,6 +3126,7 @@ button.icon-button.active i.fa-retweet { @media screen and (max-width: 400px) { .onboarding-modal__page-one { flex-direction: column; + align-items: normal; } .onboarding-modal__page-one__elephant-friend { From 68dca26a5d36eacb7d7e691635a14b6562ba7cf1 Mon Sep 17 00:00:00 2001 From: unarist Date: Sun, 25 Jun 2017 19:49:53 +0900 Subject: [PATCH 07/37] Fix react-intl/locale-data import issue on production build (#3937) Webpack seems to fail to import `react-intl/locale-data/*.js` if those files has been proceed by babel, and this also breaks applying our translation. Note that this won't be a problem on English locale, because react-intl includes it as default and works fine without manually added locale-data. Also this issue seems to only occurs on production build, but I'm not sure about reason. --- config/webpack/loaders/babel.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/webpack/loaders/babel.js b/config/webpack/loaders/babel.js index ae65db9eb5..a1992a450a 100644 --- a/config/webpack/loaders/babel.js +++ b/config/webpack/loaders/babel.js @@ -1,7 +1,10 @@ module.exports = { test: /\.js$/, // include react-intl because transform-react-remove-prop-types needs to apply to it - exclude: /node_modules[\/\\](?!react-intl)/, + exclude: { + test: /node_modules/, + exclude: /react-intl[\/\\](?!locale-data)/, + }, loader: 'babel-loader', options: { forceEnv: process.env.NODE_ENV || 'development', From 3d4e21f1ecd001d82e8363eb7d4086c7fc6064ba Mon Sep 17 00:00:00 2001 From: unarist Date: Sun, 25 Jun 2017 19:52:42 +0900 Subject: [PATCH 08/37] Don't set ASSET_HOST on build:development (#3936) Setting ASSET_HOST to `http://0.0.0.0:8080` makes urls in manifest.json to be invalid, e.g. `http://0.0.0.0:8080/packs/application.js`. Anyway, we don't need set this on build:development because assets would be delivered from same origin in development (and w/o dev-server). --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8cbcea92b7..d1fd22fb57 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0", "scripts": { "postversion": "git push --tags", - "build:development": "cross-env RAILS_ENV=development ASSET_HOST=http://0.0.0.0:8080 ./bin/webpack", + "build:development": "cross-env RAILS_ENV=development ./bin/webpack", "build:production": "cross-env RAILS_ENV=production ./bin/webpack", "manage:translations": "node ./config/webpack/translationRunner.js", "start": "rimraf ./tmp/streaming && babel ./streaming/index.js --out-dir ./tmp && node ./tmp/streaming/index.js", From 099a3b4eaccc37338eda9f45fc26991ea7115200 Mon Sep 17 00:00:00 2001 From: PFM Date: Sun, 25 Jun 2017 23:02:56 +0900 Subject: [PATCH 09/37] Fix "undefined" in className (#3939) --- app/javascript/mastodon/components/permalink.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/permalink.js b/app/javascript/mastodon/components/permalink.js index 5d3e4738d0..0b7d0a65ad 100644 --- a/app/javascript/mastodon/components/permalink.js +++ b/app/javascript/mastodon/components/permalink.js @@ -25,7 +25,7 @@ export default class Permalink extends React.PureComponent { const { href, children, className, ...other } = this.props; return ( - + {children} ); From f7301bd5b94d3033b5dbb9ff65dd1ed8ac825ce5 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 25 Jun 2017 16:54:30 +0200 Subject: [PATCH 10/37] Add overview of active sessions (#3929) * Add overview of active sessions * Better display of browser/platform name * Improve how browser information is stored and displayed for sessions overview * Fix test --- Gemfile | 1 + Gemfile.lock | 2 + .../auth/registrations_controller.rb | 5 ++ app/helpers/settings_helper.rb | 12 +++++ app/javascript/styles/tables.scss | 12 +++++ app/models/session_activation.rb | 48 +++++++++++++------ app/models/user.rb | 6 ++- .../auth/registrations/_sessions.html.haml | 23 +++++++++ app/views/auth/registrations/edit.html.haml | 4 ++ config/initializers/devise.rb | 2 +- config/locales/en.yml | 37 ++++++++++++++ ..._add_description_to_session_activations.rb | 7 +++ db/schema.rb | 5 +- spec/rails_helper.rb | 2 +- yarn.lock | 11 +---- 15 files changed, 147 insertions(+), 30 deletions(-) create mode 100644 app/views/auth/registrations/_sessions.html.haml create mode 100644 db/migrate/20170624134742_add_description_to_session_activations.rb diff --git a/Gemfile b/Gemfile index 77fffe7a66..aecd82702d 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ gem 'paperclip-av-transcoder', '~> 0.6' gem 'addressable', '~> 2.5' gem 'bootsnap' +gem 'browser' gem 'cld3', '~> 3.1' gem 'devise', '~> 4.2' gem 'devise-two-factor', '~> 3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 00ce84556f..627a01787b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,6 +70,7 @@ GEM bootsnap (1.0.0) msgpack (~> 1.0) brakeman (3.6.2) + browser (2.4.0) builder (3.2.3) bullet (5.5.1) activesupport (>= 3.0.0) @@ -483,6 +484,7 @@ DEPENDENCIES binding_of_caller (~> 0.7) bootsnap brakeman (~> 3.6) + browser bullet (~> 5.5) bundler-audit (~> 0.5) capistrano (~> 3.8) diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index d385c08e13..60ace04d7b 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -5,6 +5,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] def destroy not_found @@ -41,4 +42,8 @@ class Auth::RegistrationsController < Devise::RegistrationsController def determine_layout %w(edit update).include?(action_name) ? 'admin' : 'auth' end + + def set_sessions + @sessions = current_user.session_activations + end end diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 172ef33cac..847eff2e7f 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -41,4 +41,16 @@ module SettingsHelper def hash_to_object(hash) HashObject.new(hash) end + + def session_device_icon(session) + device = session.detection.device + + if device.mobile? + 'mobile' + elsif device.tablet? + 'tablet' + else + 'desktop' + end + end end diff --git a/app/javascript/styles/tables.scss b/app/javascript/styles/tables.scss index f7def8cf37..6e54c59c01 100644 --- a/app/javascript/styles/tables.scss +++ b/app/javascript/styles/tables.scss @@ -42,6 +42,18 @@ strong { font-weight: 500; } + + &.inline-table { + td, + th { + padding: 8px 0; + } + + & > tbody > tr:nth-child(odd) > td, + & > tbody > tr:nth-child(odd) > th { + background: transparent; + } + } } samp { diff --git a/app/models/session_activation.rb b/app/models/session_activation.rb index 71e9f023c2..75339b5f75 100644 --- a/app/models/session_activation.rb +++ b/app/models/session_activation.rb @@ -8,31 +8,49 @@ # session_id :string not null # created_at :datetime not null # updated_at :datetime not null +# user_agent :string default(""), not null +# ip :inet # class SessionActivation < ApplicationRecord - LIMIT = Rails.configuration.x.max_session_activations - - def self.active?(id) - id && where(session_id: id).exists? + def detection + @detection ||= Browser.new(user_agent) end - def self.activate(id) - activation = create!(session_id: id) - purge_old - activation + def browser + detection.id end - def self.deactivate(id) - return unless id - where(session_id: id).destroy_all + def platform + detection.platform.id end - def self.purge_old - order('created_at desc').offset(LIMIT).destroy_all + before_save do + self.user_agent = '' if user_agent.nil? end - def self.exclusive(id) - where('session_id != ?', id).destroy_all + class << self + def active?(id) + id && where(session_id: id).exists? + end + + def activate(options = {}) + activation = create!(options) + purge_old + activation + end + + def deactivate(id) + return unless id + where(session_id: id).destroy_all + end + + def purge_old + order('created_at desc').offset(Rails.configuration.x.max_session_activations).destroy_all + end + + def exclusive(id) + where('session_id != ?', id).destroy_all + end end end diff --git a/app/models/user.rb b/app/models/user.rb index fccf1089bb..c31a0c6440 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -91,8 +91,10 @@ class User < ApplicationRecord settings.auto_play_gif end - def activate_session - session_activations.activate(SecureRandom.hex).session_id + def activate_session(request) + session_activations.activate(session_id: SecureRandom.hex, + user_agent: request.user_agent, + ip: request.ip).session_id end def exclusive_session(id) diff --git a/app/views/auth/registrations/_sessions.html.haml b/app/views/auth/registrations/_sessions.html.haml new file mode 100644 index 0000000000..11c0d4e315 --- /dev/null +++ b/app/views/auth/registrations/_sessions.html.haml @@ -0,0 +1,23 @@ +%h6= t 'sessions.title' +%p.muted-hint= t 'sessions.explanation' + +%table.table.inline-table + %thead + %tr + %th= t 'sessions.browser' + %th= t 'sessions.ip' + %th= t 'sessions.activity' + %tbody + - @sessions.each do |session| + %tr + %td + %span{ title: session.user_agent }= fa_icon session_device_icon(session) + = ' ' + = t 'sessions.description', browser: t("sessions.browsers.#{session.browser}"), platform: t("sessions.platforms.#{session.platform}") + %td + %samp= session.ip + %td + - if request.session['auth_id'] == session.session_id + = t 'sessions.current_session' + - else + %time.time-ago{ datetime: session.updated_at.iso8601, title: l(session.updated_at) }= l(session.updated_at) diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml index 38d4349cb6..fbc8d017b3 100644 --- a/app/views/auth/registrations/edit.html.haml +++ b/app/views/auth/registrations/edit.html.haml @@ -12,6 +12,10 @@ .actions = f.button :button, t('generic.save_changes'), type: :submit +%hr/ + += render 'sessions' + - if open_deletion? %hr/ diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 6d3a73ef67..d51471d308 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -1,6 +1,6 @@ Warden::Manager.after_set_user except: :fetch do |user, warden| SessionActivation.deactivate warden.raw_session['auth_id'] - warden.raw_session['auth_id'] = user.activate_session + warden.raw_session['auth_id'] = user.activate_session(warden.request) end Warden::Manager.after_fetch do |user, warden| diff --git a/config/locales/en.yml b/config/locales/en.yml index 0d33aae3f6..1d8e3f6b00 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -320,6 +320,43 @@ en: missing_resource: Could not find the required redirect URL for your account proceed: Proceed to follow prompt: 'You are going to follow:' + sessions: + activity: Last activity + browser: Browser + browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + firefox: Firefox + generic: Unknown browser + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo + current_session: Current session + description: "%{browser} on %{platform}" + explanation: These are the web browsers currently logged in to your Mastodon account. + ip: IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac + other: unknown platform + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + title: Sessions settings: authorized_apps: Authorized apps back: Back to Mastodon diff --git a/db/migrate/20170624134742_add_description_to_session_activations.rb b/db/migrate/20170624134742_add_description_to_session_activations.rb new file mode 100644 index 0000000000..9dbb155641 --- /dev/null +++ b/db/migrate/20170624134742_add_description_to_session_activations.rb @@ -0,0 +1,7 @@ +class AddDescriptionToSessionActivations < ActiveRecord::Migration[5.1] + def change + add_column :session_activations, :user_agent, :string, null: false, default: '' + add_column :session_activations, :ip, :inet + add_foreign_key :session_activations, :users, on_delete: :cascade + end +end diff --git a/db/schema.rb b/db/schema.rb index b6aceb9302..1e7d6c0b33 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: 20170623152212) do +ActiveRecord::Schema.define(version: 20170624134742) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -255,6 +255,8 @@ ActiveRecord::Schema.define(version: 20170623152212) do t.string "session_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "user_agent", default: "", null: false + t.inet "ip" t.index ["session_id"], name: "index_session_activations_on_session_id", unique: true t.index ["user_id"], name: "index_session_activations_on_user_id" end @@ -404,6 +406,7 @@ ActiveRecord::Schema.define(version: 20170623152212) do add_foreign_key "reports", "accounts", column: "action_taken_by_account_id", on_delete: :nullify add_foreign_key "reports", "accounts", column: "target_account_id", on_delete: :cascade add_foreign_key "reports", "accounts", on_delete: :cascade + add_foreign_key "session_activations", "users", on_delete: :cascade add_foreign_key "statuses", "accounts", column: "in_reply_to_account_id", on_delete: :nullify add_foreign_key "statuses", "accounts", on_delete: :cascade add_foreign_key "statuses", "statuses", column: "in_reply_to_id", on_delete: :nullify diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 31c94b1e4b..cfc9eec9ea 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -23,7 +23,7 @@ Devise::Test::ControllerHelpers.module_eval do original_sign_in(resource, scope: scope) SessionActivation.deactivate warden.raw_session["auth_id"] - warden.raw_session["auth_id"] = resource.activate_session + warden.raw_session["auth_id"] = resource.activate_session(warden.request) end end diff --git a/yarn.lock b/yarn.lock index ef870d7e2a..d1a1687a0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7184,16 +7184,7 @@ webpack-bundle-analyzer@^2.8.2: opener "^1.4.3" ws "^2.3.1" -webpack-dev-middleware@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.2.tgz#2e252ce1dfb020dbda1ccb37df26f30ab014dbd1" - dependencies: - memory-fs "~0.4.1" - mime "^1.3.4" - path-is-absolute "^1.0.0" - range-parser "^1.0.3" - -webpack-dev-middleware@^1.11.0: +webpack-dev-middleware@^1.10.2, webpack-dev-middleware@^1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.11.0.tgz#09691d0973a30ad1f82ac73a12e2087f0a4754f9" dependencies: From 87efa3872193084468c25f410f5cf9189c0b976e Mon Sep 17 00:00:00 2001 From: amazedkoumei Date: Mon, 26 Jun 2017 01:13:31 +0900 Subject: [PATCH 11/37] more free pgconfig by .env (#3909) * more free pgconfig for streaming by .env * fix wrong default values * database.yml read ENV as same as streaming server --- config/database.yml | 8 ++++++++ streaming/index.js | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/config/database.yml b/config/database.yml index 39393e93ad..079ea7b4a9 100644 --- a/config/database.yml +++ b/config/database.yml @@ -7,6 +7,10 @@ default: &default development: <<: *default database: mastodon_development + username: <%= ENV['DB_USER'] %> + password: <%= ENV['DB_PASS'] %> + host: <%= ENV['DB_HOST'] %> + port: <%= ENV['DB_PORT'] %> # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". @@ -14,6 +18,10 @@ development: test: <<: *default database: mastodon_test<%= ENV['TEST_ENV_NUMBER'] %> + username: <%= ENV['TEST_DB_USER'] %> + password: <%= ENV['TEST_DB_PASS'] %> + host: <%= ENV['TEST_DB_HOST'] %> + port: <%= ENV['TEST_DB_PORT'] %> production: <<: *default diff --git a/streaming/index.js b/streaming/index.js index 156e1d4bc5..701cb2f557 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -78,7 +78,11 @@ const startWorker = (workerId) => { const pgConfigs = { development: { - database: 'mastodon_development', + user: process.env.DB_USER || pg.defaults.user, + password: process.env.DB_PASS || pg.defaults.password, + database: process.env.DB_NAME || 'mastodon_development', + host: process.env.DB_HOST || pg.defaults.host, + port: process.env.DB_PORT || pg.defaults.port, max: 10, }, From 8f991831b8de01b69d6b0f069aa43d9326c93a1d Mon Sep 17 00:00:00 2001 From: "Akihiko Odaki (@fn_aki@pawoo.net)" Date: Mon, 26 Jun 2017 04:42:36 +0900 Subject: [PATCH 12/37] Cover Admin::DomainBlocksController more (#3329) Also domain_block fabricator now sets unique domains --- .../admin/domain_blocks_controller_spec.rb | 34 ++++++++++++++++--- spec/fabricators/domain_block_fabricator.rb | 2 +- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb index 0ca41d7d41..b9e73c04b1 100644 --- a/spec/controllers/admin/domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/domain_blocks_controller_spec.rb @@ -8,17 +8,30 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do end describe 'GET #index' do - it 'returns http success' do - get :index + around do |example| + default_per_page = DomainBlock.default_per_page + DomainBlock.paginates_per 1 + example.run + DomainBlock.paginates_per default_per_page + end + it 'renders domain blocks' do + 2.times { Fabricate(:domain_block) } + + get :index, params: { page: 2 } + + assigned = assigns(:domain_blocks) + expect(assigned.count).to eq 1 + expect(assigned.klass).to be DomainBlock expect(response).to have_http_status(:success) end end describe 'GET #new' do - it 'returns http success' do + it 'assigns a new domain block' do get :new + expect(assigns(:domain_block)).to be_instance_of(DomainBlock) expect(response).to have_http_status(:success) end end @@ -33,13 +46,25 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do end describe 'POST #create' do - it 'blocks the domain' do + it 'blocks the domain when succeeded to save' do allow(DomainBlockWorker).to receive(:perform_async).and_return(true) + post :create, params: { domain_block: { domain: 'example.com', severity: 'silence' } } expect(DomainBlockWorker).to have_received(:perform_async) + expect(flash[:notice]).to eq I18n.t('admin.domain_blocks.created_msg') expect(response).to redirect_to(admin_domain_blocks_path) end + + it 'renders new when failed to save' do + Fabricate(:domain_block, domain: 'example.com') + allow(DomainBlockWorker).to receive(:perform_async).and_return(true) + + post :create, params: { domain_block: { domain: 'example.com', severity: 'silence' } } + + expect(DomainBlockWorker).not_to have_received(:perform_async) + expect(response).to render_template :new + end end describe 'DELETE #destroy' do @@ -50,6 +75,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do delete :destroy, params: { id: domain_block.id, domain_block: { retroactive: '1' } } expect(service).to have_received(:call).with(domain_block, true) + expect(flash[:notice]).to eq I18n.t('admin.domain_blocks.destroyed_msg') expect(response).to redirect_to(admin_domain_blocks_path) end end diff --git a/spec/fabricators/domain_block_fabricator.rb b/spec/fabricators/domain_block_fabricator.rb index 563a0f65b6..cc1f928e58 100644 --- a/spec/fabricators/domain_block_fabricator.rb +++ b/spec/fabricators/domain_block_fabricator.rb @@ -1,3 +1,3 @@ Fabricator(:domain_block) do - domain "example.com" + domain { sequence(:domain) { |i| "#{i}#{Faker::Internet.domain_name}" } } end From 67243bda3157a9046148b41579f0b1e8cf4a7116 Mon Sep 17 00:00:00 2001 From: "Akihiko Odaki (@fn_aki@pawoo.net)" Date: Mon, 26 Jun 2017 04:42:55 +0900 Subject: [PATCH 13/37] Cover Auth::RegistrationsController more (#3353) --- .../auth/registrations_controller_spec.rb | 105 +++++++++++++++--- 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index df0a3bfa6b..97d2c53df7 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -3,37 +3,110 @@ require 'rails_helper' RSpec.describe Auth::RegistrationsController, type: :controller do render_views + shared_examples 'checks for enabled registrations' do |path| + around do |example| + open_registrations = Setting.open_registrations + example.run + Setting.open_registrations = open_registrations + end + + it 'redirects if it is in single user mode while it is open for registration' do + Fabricate(:account) + Setting.open_registrations = true + expect(Rails.configuration.x).to receive(:single_user_mode).and_return(true) + + get path + + expect(response).to redirect_to '/' + end + + it 'redirects if it is not open for registration while it is not in single user mode' do + Setting.open_registrations = false + expect(Rails.configuration.x).to receive(:single_user_mode).and_return(false) + + get path + + expect(response).to redirect_to '/' + end + end + + describe 'GET #edit' do + it 'returns http success' do + request.env["devise.mapping"] = Devise.mappings[:user] + sign_in(Fabricate(:user)) + get :edit + expect(response).to have_http_status(:success) + end + end + + describe 'GET #update' do + it 'returns http success' do + request.env["devise.mapping"] = Devise.mappings[:user] + sign_in(Fabricate(:user), scope: :user) + post :update + expect(response).to have_http_status(:success) + end + end + describe 'GET #new' do before do - Setting.open_registrations = true request.env["devise.mapping"] = Devise.mappings[:user] end - it 'returns http success' do - get :new - expect(response).to have_http_status(:success) + context do + around do |example| + open_registrations = Setting.open_registrations + example.run + Setting.open_registrations = open_registrations + end + + it 'returns http success' do + Setting.open_registrations = true + get :new + expect(response).to have_http_status(:success) + end end + + include_examples 'checks for enabled registrations', :new end describe 'POST #create' do let(:accept_language) { Rails.application.config.i18n.available_locales.sample.to_s } - before do - Setting.open_registrations = true - request.env["devise.mapping"] = Devise.mappings[:user] - request.headers["Accept-Language"] = accept_language - post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } } + before { request.env["devise.mapping"] = Devise.mappings[:user] } + + context do + around do |example| + open_registrations = Setting.open_registrations + example.run + Setting.open_registrations = open_registrations + end + + subject do + Setting.open_registrations = true + request.headers["Accept-Language"] = accept_language + post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } } + end + + it 'redirects to login page' do + subject + expect(response).to redirect_to new_user_session_path + end + + it 'creates user' do + subject + user = User.find_by(email: 'test@example.com') + expect(user).to_not be_nil + expect(user.locale).to eq(accept_language) + end end - it 'redirects to login page' do - expect(response).to redirect_to new_user_session_path + it 'does nothing if user already exists' do + Fabricate(:user, account: Fabricate(:account, username: 'test')) + subject end - it 'creates user' do - user = User.find_by(email: 'test@example.com') - expect(user).to_not be_nil - expect(user.locale).to eq(accept_language) - end + include_examples 'checks for enabled registrations', :create end describe 'DELETE #destroy' do From 4ce154009426038f0bb88d2e89b408ade928b1a2 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Sun, 25 Jun 2017 21:43:27 +0200 Subject: [PATCH 14/37] fix(features/compose): Handle external changes to the textarea (#3632) --- .../mastodon/features/compose/components/compose_form.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index c379c1855a..f7eeedc69f 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -67,6 +67,12 @@ export default class ComposeForm extends ImmutablePureComponent { } handleSubmit = () => { + if (this.props.text !== this.autosuggestTextarea.textarea.value) { + // Something changed the text inside the textarea (e.g. browser extensions like Grammarly) + // Update the state to match the current text + this.props.onChange(this.autosuggestTextarea.textarea.value); + } + this.props.onSubmit(); } From d821aba002002f79385abd7f052f68c383d5cd0c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 25 Jun 2017 22:13:02 +0200 Subject: [PATCH 15/37] Rename "Credentials" page to "Security" for clarity (#3941) * Rename "Credentials" page to "Security" for clarity * Change "security" icon from cog to lock --- config/locales/en.yml | 2 +- config/navigation.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 1d8e3f6b00..7238949dc9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -200,7 +200,7 @@ en: applications: invalid_url: The provided URL is invalid auth: - change_password: Credentials + change_password: Security delete_account: Delete account delete_account_html: If you wish to delete your account, you can proceed here. You will be asked for confirmation. didnt_get_confirmation: Didn't receive confirmation instructions? diff --git a/config/navigation.rb b/config/navigation.rb index d7508f0199..535d033f50 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -7,7 +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 :password, safe_join([fa_icon('cog fw'), t('auth.change_password')]), edit_user_registration_url, highlights_on: %r{/auth/edit|/settings/delete} + 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 settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url From 436ce03772c8c87a215cdcd88020edfb8c241d38 Mon Sep 17 00:00:00 2001 From: amazedkoumei Date: Mon, 26 Jun 2017 06:29:22 +0900 Subject: [PATCH 16/37] fix unnecessary variable (#3947) --- config/database.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/database.yml b/config/database.yml index 079ea7b4a9..f74635a369 100644 --- a/config/database.yml +++ b/config/database.yml @@ -18,10 +18,10 @@ development: test: <<: *default database: mastodon_test<%= ENV['TEST_ENV_NUMBER'] %> - username: <%= ENV['TEST_DB_USER'] %> - password: <%= ENV['TEST_DB_PASS'] %> - host: <%= ENV['TEST_DB_HOST'] %> - port: <%= ENV['TEST_DB_PORT'] %> + username: <%= ENV['DB_USER'] %> + password: <%= ENV['DB_PASS'] %> + host: <%= ENV['DB_HOST'] %> + port: <%= ENV['DB_PORT'] %> production: <<: *default From ed7dc1704dc3ce82567d9aac366b095f02ce181f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 25 Jun 2017 23:51:32 +0200 Subject: [PATCH 17/37] Bind web UI access tokens to sessions (#3940) * Add overview of active sessions * Better display of browser/platform name * Improve how browser information is stored and displayed for sessions overview * Fix test * Fix #2347 - Bind web UI access token to session When you logout, session also destroys the access token, so it's no longer valid. If access token is destroyed some other way, the session is also destroyed, requiring a re-login. Fix #1681 - Add scheduler to remove revoked access tokens and grants * Fix test --- app/controllers/application_controller.rb | 5 +++ app/controllers/home_controller.rb | 12 +---- app/models/session_activation.rb | 44 ++++++++++++++----- .../scheduler/doorkeeper_cleanup_scheduler.rb | 11 +++++ config/sidekiq.yml | 3 ++ ..._access_token_id_to_session_activations.rb | 6 +++ db/schema.rb | 4 +- 7 files changed, 63 insertions(+), 22 deletions(-) create mode 100644 app/workers/scheduler/doorkeeper_cleanup_scheduler.rb create mode 100644 db/migrate/20170625140443_add_access_token_id_to_session_activations.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9cb397aa82..865fcd1257 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base include UserTrackingConcern helper_method :current_account + helper_method :current_session helper_method :single_user_mode? rescue_from ActionController::RoutingError, with: :not_found @@ -68,6 +69,10 @@ class ApplicationController < ActionController::Base @current_account ||= current_user.try(:account) end + def current_session + @current_session ||= SessionActivation.find_by(session_id: session['auth_id']) + end + def cache_collection(raw, klass) return raw unless klass.respond_to?(:with_includes) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 1d41892cdd..6209a3ae93 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -5,7 +5,7 @@ class HomeController < ApplicationController def index @body_classes = 'app-body' - @token = find_or_create_access_token.token + @token = current_session.token @web_settings = Web::Setting.find_by(user: current_user)&.data || {} @admin = Account.find_local(Setting.site_contact_username) @streaming_api_base_url = Rails.configuration.x.streaming_api_base_url @@ -16,14 +16,4 @@ class HomeController < ApplicationController def authenticate_user! redirect_to(single_user_mode? ? account_path(Account.first) : about_path) unless user_signed_in? end - - def find_or_create_access_token - Doorkeeper::AccessToken.find_or_create_for( - Doorkeeper::Application.where(superapp: true).first, - current_user.id, - Doorkeeper::OAuth::Scopes.from_string('read write follow'), - Doorkeeper.configuration.access_token_expires_in, - Doorkeeper.configuration.refresh_token_enabled? - ) - end end diff --git a/app/models/session_activation.rb b/app/models/session_activation.rb index 75339b5f75..02a918e8ac 100644 --- a/app/models/session_activation.rb +++ b/app/models/session_activation.rb @@ -3,16 +3,23 @@ # # Table name: session_activations # -# id :integer not null, primary key -# user_id :integer not null -# session_id :string not null -# created_at :datetime not null -# updated_at :datetime not null -# user_agent :string default(""), not null -# ip :inet +# id :integer not null, primary key +# user_id :integer not null +# session_id :string not null +# created_at :datetime not null +# updated_at :datetime not null +# user_agent :string default(""), not null +# ip :inet +# access_token_id :integer # class SessionActivation < ApplicationRecord + belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', dependent: :destroy + + delegate :token, + to: :access_token, + allow_nil: true + def detection @detection ||= Browser.new(user_agent) end @@ -25,9 +32,8 @@ class SessionActivation < ApplicationRecord detection.platform.id end - before_save do - self.user_agent = '' if user_agent.nil? - end + before_create :assign_access_token + before_save :assign_user_agent class << self def active?(id) @@ -53,4 +59,22 @@ class SessionActivation < ApplicationRecord where('session_id != ?', id).destroy_all end end + + private + + def assign_user_agent + self.user_agent = '' if user_agent.nil? + end + + def assign_access_token + superapp = Doorkeeper::Application.find_by(superapp: true) + + return if superapp.nil? + + self.access_token = Doorkeeper::AccessToken.create!(application_id: superapp.id, + resource_owner_id: user_id, + scopes: 'read write follow', + expires_in: Doorkeeper.configuration.access_token_expires_in, + use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?) + end end diff --git a/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb b/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb new file mode 100644 index 0000000000..6488798cd8 --- /dev/null +++ b/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true +require 'sidekiq-scheduler' + +class Scheduler::DoorkeeperCleanupScheduler + include Sidekiq::Worker + + def perform + Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all + Doorkeeper::AccessGrant.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all + end +end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 6ed0aa4b54..78aaa311cf 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -15,3 +15,6 @@ feed_cleanup_scheduler: cron: '0 0 * * *' class: Scheduler::FeedCleanupScheduler + doorkeeper_cleanup_scheduler: + cron: '1 1 * * 0' + class: Scheduler::DoorkeeperCleanupScheduler diff --git a/db/migrate/20170625140443_add_access_token_id_to_session_activations.rb b/db/migrate/20170625140443_add_access_token_id_to_session_activations.rb new file mode 100644 index 0000000000..213a77a83d --- /dev/null +++ b/db/migrate/20170625140443_add_access_token_id_to_session_activations.rb @@ -0,0 +1,6 @@ +class AddAccessTokenIdToSessionActivations < ActiveRecord::Migration[5.1] + def change + add_column :session_activations, :access_token_id, :integer + add_foreign_key :session_activations, :oauth_access_tokens, column: :access_token_id, on_delete: :cascade + end +end diff --git a/db/schema.rb b/db/schema.rb index 1e7d6c0b33..159704c6a1 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: 20170624134742) do +ActiveRecord::Schema.define(version: 20170625140443) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -257,6 +257,7 @@ ActiveRecord::Schema.define(version: 20170624134742) do t.datetime "updated_at", null: false t.string "user_agent", default: "", null: false t.inet "ip" + t.integer "access_token_id" t.index ["session_id"], name: "index_session_activations_on_session_id", unique: true t.index ["user_id"], name: "index_session_activations_on_user_id" end @@ -406,6 +407,7 @@ ActiveRecord::Schema.define(version: 20170624134742) do add_foreign_key "reports", "accounts", column: "action_taken_by_account_id", on_delete: :nullify add_foreign_key "reports", "accounts", column: "target_account_id", on_delete: :cascade add_foreign_key "reports", "accounts", on_delete: :cascade + add_foreign_key "session_activations", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade add_foreign_key "session_activations", "users", on_delete: :cascade add_foreign_key "statuses", "accounts", column: "in_reply_to_account_id", on_delete: :nullify add_foreign_key "statuses", "accounts", on_delete: :cascade From 5e8d037e271bdd230fc7ab1e91bcee16ac87e0e1 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 25 Jun 2017 23:51:46 +0200 Subject: [PATCH 18/37] Fix #3910 - Require OTP authentication to disable 2FA (#3935) * Fix #3910 - Require OTP authentication to disable 2FA. Also, remove ability to generate new OTP backup codes *after* initial backup codes were handed out during activation * Restore recovery code re-generation * Improve display of some 2FA elements --- .../two_factor_authentications_controller.rb | 20 +++++++-- app/javascript/styles/admin.scss | 5 +++ app/javascript/styles/forms.scss | 1 - app/javascript/styles/lists.scss | 1 - .../recovery_codes/index.html.haml | 2 +- .../two_factor_authentications/show.html.haml | 44 +++++++++++-------- config/locales/ca.yml | 2 +- config/locales/de.yml | 2 +- config/locales/en.yml | 8 +++- config/locales/fa.yml | 2 +- config/locales/fr.yml | 2 +- config/locales/he.yml | 2 +- config/locales/id.yml | 2 +- config/locales/io.yml | 2 +- config/locales/ja.yml | 2 +- config/locales/nl.yml | 2 +- config/locales/no.yml | 2 +- config/locales/oc.yml | 6 +-- config/locales/pl.yml | 2 +- config/locales/pt-BR.yml | 2 +- config/locales/ru.yml | 2 +- config/locales/th.yml | 2 +- config/locales/tr.yml | 2 +- config/locales/uk.yml | 2 +- config/locales/zh-CN.yml | 2 +- config/locales/zh-HK.yml | 2 +- ..._factor_authentications_controller_spec.rb | 40 ++++++++++++++--- 27 files changed, 109 insertions(+), 54 deletions(-) diff --git a/app/controllers/settings/two_factor_authentications_controller.rb b/app/controllers/settings/two_factor_authentications_controller.rb index f66c3a9083..9834838817 100644 --- a/app/controllers/settings/two_factor_authentications_controller.rb +++ b/app/controllers/settings/two_factor_authentications_controller.rb @@ -7,7 +7,9 @@ module Settings before_action :authenticate_user! before_action :verify_otp_required, only: [:create] - def show; end + def show + @confirmation = Form::TwoFactorConfirmation.new + end def create current_user.otp_secret = User.generate_otp_secret(32) @@ -16,13 +18,23 @@ module Settings end def destroy - current_user.otp_required_for_login = false - current_user.save! - redirect_to settings_two_factor_authentication_path + if current_user.validate_and_consume_otp!(confirmation_params[:code]) + current_user.otp_required_for_login = false + current_user.save! + redirect_to settings_two_factor_authentication_path + else + flash.now[:alert] = I18n.t('two_factor_authentication.wrong_code') + @confirmation = Form::TwoFactorConfirmation.new + render :show + end end private + def confirmation_params + params.require(:form_two_factor_confirmation).permit(:code) + end + def verify_otp_required redirect_to settings_two_factor_authentication_path if current_user.otp_required_for_login? end diff --git a/app/javascript/styles/admin.scss b/app/javascript/styles/admin.scss index c2bfc10a04..3bc713566c 100644 --- a/app/javascript/styles/admin.scss +++ b/app/javascript/styles/admin.scss @@ -129,6 +129,11 @@ color: $ui-primary-color; } } + + .positive-hint { + color: $valid-value-color; + font-weight: 500; + } } .simple_form { diff --git a/app/javascript/styles/forms.scss b/app/javascript/styles/forms.scss index 059c4a7d83..7a181f36b5 100644 --- a/app/javascript/styles/forms.scss +++ b/app/javascript/styles/forms.scss @@ -358,7 +358,6 @@ code { } .user_filtered_languages { - & > label { font-family: inherit; font-size: 16px; diff --git a/app/javascript/styles/lists.scss b/app/javascript/styles/lists.scss index 47805663f0..6019cd8002 100644 --- a/app/javascript/styles/lists.scss +++ b/app/javascript/styles/lists.scss @@ -10,7 +10,6 @@ .recovery-codes { list-style: none; margin: 0 auto; - text-align: center; li { font-size: 125%; diff --git a/app/views/settings/two_factor_authentication/recovery_codes/index.html.haml b/app/views/settings/two_factor_authentication/recovery_codes/index.html.haml index 7d409826e4..d47ee840e1 100644 --- a/app/views/settings/two_factor_authentication/recovery_codes/index.html.haml +++ b/app/views/settings/two_factor_authentication/recovery_codes/index.html.haml @@ -1,7 +1,7 @@ - content_for :page_title do = t('settings.two_factor_authentication') -%p.hint= t('two_factor_authentication.recovery_instructions') +%p.hint= t('two_factor_authentication.recovery_instructions_html') %ol.recovery-codes - @recovery_codes.each do |code| diff --git a/app/views/settings/two_factor_authentications/show.html.haml b/app/views/settings/two_factor_authentications/show.html.haml index 88b5bd20e4..8ba42a1015 100644 --- a/app/views/settings/two_factor_authentications/show.html.haml +++ b/app/views/settings/two_factor_authentications/show.html.haml @@ -1,26 +1,34 @@ - content_for :page_title do = t('settings.two_factor_authentication') -.simple_form - %p.hint - = t('two_factor_authentication.description_html') +- if current_user.otp_required_for_login + %p.positive-hint + = fa_icon 'check' + = ' ' + = t 'two_factor_authentication.enabled' + + %hr/ + + = simple_form_for @confirmation, url: settings_two_factor_authentication_path, method: :delete do |f| + = f.input :code, hint: t('two_factor_authentication.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt') + + .actions + = f.button :button, t('two_factor_authentication.disable'), type: :submit + + %hr/ + + %h6= t('two_factor_authentication.recovery_codes') + %p.muted-hint + = t('two_factor_authentication.lost_recovery_codes') + = link_to t('two_factor_authentication.generate_recovery_codes'), + settings_two_factor_authentication_recovery_codes_path, + data: { method: :post } + +- else + .simple_form + %p.hint= t('two_factor_authentication.description_html') - - if current_user.otp_required_for_login - = link_to t('two_factor_authentication.disable'), - settings_two_factor_authentication_path, - data: { method: :delete }, - class: 'block-button' - - else = link_to t('two_factor_authentication.setup'), settings_two_factor_authentication_path, data: { method: :post }, class: 'block-button' - -- if current_user.otp_required_for_login - .simple_form - %p.hint - = t('two_factor_authentication.lost_recovery_codes') - = link_to t('two_factor_authentication.generate_recovery_codes'), - settings_two_factor_authentication_recovery_codes_path, - data: { method: :post }, - class: 'block-button' diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 24fc5690d0..2fbc63ef9b 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -360,7 +360,7 @@ ca: lost_recovery_codes: Els codis de recuperació et permeten recuperar l'accés al teu compte si perds el telèfon. Si has perdut els teus codis de recuperació els pots regenerar aquí. Els codis de recuperació anteriors seran anul·lats. manual_instructions: 'Si no pots escanejar el codi QR code i necessites introduir-lo manualment, aquí tens el secret en text plà:' recovery_codes_regenerated: Codis de recuperació regenerats amb èxit - recovery_instructions: Si alguna vegada perds l'accéss al telèfon pots utilitzar un dels codis de recuperació a continuació per recuperar l'accés al teu compte. Cal mantenir els codis de recuperació en lloc segur, per exemple imprimint-los i guardar-los amb altres documents importants. + recovery_instructions_html: Si alguna vegada perds l'accéss al telèfon pots utilitzar un dels codis de recuperació a continuació per recuperar l'accés al teu compte. Cal mantenir els codis de recuperació en lloc segur, per exemple imprimint-los i guardar-los amb altres documents importants. setup: Establir wrong_code: El codi introduït es invalid! Es correcta la hora del servidor i del dispositiu? users: diff --git a/config/locales/de.yml b/config/locales/de.yml index 72d60d2a0a..f2841d0b7f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -304,7 +304,7 @@ de: lost_recovery_codes: Wiederherstellungscodes erlauben dir, wieder den Zugang zu deinem Konto zu erlangen, falls du dein Telefon verlierst. Wenn du deine Wiederherstellungscodes verloren hast, kannst du sie hier regenerieren. Deine alten Wiederherstellungscodes werden damit ungültig gemacht. manual_instructions: 'Wenn du den QR-Code nicht einlesen kannst und ihn manuell eingeben musst, ist hier das Klartext-Geheimnis:' recovery_codes_regenerated: Wiederherstellungscodes erfolgreich regeneriert - recovery_instructions: Wenn du jemals den Zugang zu deinem Telefon verlierst, kannst du einen der Wiederherstellungscodes unten benutzen, um wieder auf dein Konto zugreifen zu können. Bewahre die Wiederherstellungscodes sicher auf, indem du sie beispielsweise ausdruckst und sie zusammen mit anderen wichtigen Dokumenten lagerst. + recovery_instructions_html: Wenn du jemals den Zugang zu deinem Telefon verlierst, kannst du einen der Wiederherstellungscodes unten benutzen, um wieder auf dein Konto zugreifen zu können. Bewahre die Wiederherstellungscodes sicher auf, indem du sie beispielsweise ausdruckst und sie zusammen mit anderen wichtigen Dokumenten lagerst. setup: Einrichten wrong_code: Der eingegebene Code war ungültig! Sind die Server- und die Gerätezeit korrekt? users: diff --git a/config/locales/en.yml b/config/locales/en.yml index 7238949dc9..9daaf53ecd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -391,13 +391,17 @@ en: description_html: If you enable two-factor authentication, logging in will require you to be in possession of your phone, which will generate tokens for you to enter. disable: Disable enable: Enable + enabled: Two-factor authentication is enabled enabled_success: Two-factor authentication successfully enabled - generate_recovery_codes: Generate Recovery Codes + generate_recovery_codes: Generate recovery codes instructions_html: "Scan this QR code into Google Authenticator or a similiar TOTP app on your phone. From now on, that app will generate tokens that you will have to enter when logging in." lost_recovery_codes: Recovery codes allow you to regain access to your account if you lose your phone. If you've lost your recovery codes, you can regenerate them here. Your old recovery codes will be invalidated. manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:' + recovery_codes: Backup recovery codes recovery_codes_regenerated: Recovery codes successfully regenerated - recovery_instructions: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe. (For example, you may print them and store them with other important documents.) + recovery_instructions_html: + If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe. + For example, you may print them and store them with other important documents. setup: Set up wrong_code: The entered code was invalid! Are server time and device time correct? users: diff --git a/config/locales/fa.yml b/config/locales/fa.yml index a65de23658..515443608e 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -334,7 +334,7 @@ fa: lost_recovery_codes: با کدهای بازیابی می‌توانید اگر تلفن خود را گم کردید به حساب خود دسترسی داشته باشید. اگر کدهای بازیابی خود را گم کردید، آن‌ها را این‌جا دوباره بسازید. کدهای بازیابی قبلی شما نامعتبر خواهند شد. manual_instructions: 'اگر نمی‌توانید کدها را اسکن کنید و باید آن‌ها را دستی وارد کنید، متن کد امنیتی این‌جاست:' recovery_codes_regenerated: کدهای بازیابی با موفقیت ساخته شدند - recovery_instructions: اگر تلفن خود را گم کردید، می‌توانید با یکی از کدهای بازیابی زیر کنترل حساب خود را به دست بگیرید. این کدها را در جای امنی نگه دارید، مثلاً آن‌ها را چاپ کنید و کنار سایر مدارک مهم خود قرار دهید + recovery_instructions_html: اگر تلفن خود را گم کردید، می‌توانید با یکی از کدهای بازیابی زیر کنترل حساب خود را به دست بگیرید. این کدها را در جای امنی نگه دارید، مثلاً آن‌ها را چاپ کنید و کنار سایر مدارک مهم خود قرار دهید setup: راه اندازی wrong_code: کدی که وارد کردید نامعتبر بود! آیا ساعت سرور و ساعت دستگاه شما درست تنظیم شده‌اند؟ users: diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 7f348986ed..0c3f3b1d53 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -300,7 +300,7 @@ fr: lost_recovery_codes: Les codes de récupération vous permettent de retrouver les accès à votre comptre si vous perdez votre téléphone. Si vous perdez vos codes de récupération, vous pouvez les générer à nouveau ici. Vos anciens codes de récupération seront invalidés. manual_instructions: 'Si vous ne pouvez pas scanner ce QR code et devez l''entrer manuellement, voici le secret en clair :' recovery_codes_regenerated: Codes de récupération régénérés avec succès - recovery_instructions: Si vous perdez l'accès à votre téléphone, vous pouvez utiliser un des codes de récupération ci-dessous pour récupérer l'accès à votre compte. Conservez les codes de récupération en toute sécurité, par exemple, en les imprimant et en les stockant avec vos autres documents importants. + recovery_instructions_html: Si vous perdez l'accès à votre téléphone, vous pouvez utiliser un des codes de récupération ci-dessous pour récupérer l'accès à votre compte. Conservez les codes de récupération en toute sécurité, par exemple, en les imprimant et en les stockant avec vos autres documents importants. setup: Installer wrong_code: Les codes entrés sont incorrects ! L'heure du serveur et celle de votre appareil sont-elles correctes ? users: diff --git a/config/locales/he.yml b/config/locales/he.yml index 7e3b40b1c1..ec7d972ec8 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -342,7 +342,7 @@ he: lost_recovery_codes: קודי האחזור מאפשרים אחזור גישה לחשבון במידה ומכשירך אבד. במידה וקודי האחזור אבדו, ניתן לייצרם מחדש כאן. תוקף קודי האחזור הישנים יפוג. manual_instructions: 'במידה ולא ניתן לסרוק את קוד ה-QR אלא יש צורך להקליד אותו ידנית, להלן סוד כמוס בלתי מוצפן:' recovery_codes_regenerated: קודי האחזור יוצרו בהצלחה - recovery_instructions: במידה והגישה למכשירך תאבד, ניתן לייצר קודי אחזור למטה על מנת לאחזר גישה לחשבונך בכל עת. נא לשמור על קודי הגישה במקום בטוח )לדוגמא על ידי הדפסתם ושמירתם עם מסמכים חשובים אחרים, או שימוש בתוכנה ייעודית לניהול סיסמאות וסודות( + recovery_instructions_html: במידה והגישה למכשירך תאבד, ניתן לייצר קודי אחזור למטה על מנת לאחזר גישה לחשבונך בכל עת. נא לשמור על קודי הגישה במקום בטוח )לדוגמא על ידי הדפסתם ושמירתם עם מסמכים חשובים אחרים, או שימוש בתוכנה ייעודית לניהול סיסמאות וסודות( setup: הכנה wrong_code: הקוד שהוזן שגוי! האם הזמן בשרת והזמן במכשירך נכונים? users: diff --git a/config/locales/id.yml b/config/locales/id.yml index 300612b310..fc4ffd046c 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -331,7 +331,7 @@ id: lost_recovery_codes: Kode pemulihan bisa anda gunakan untuk mendapatkan kembali akses pada akun anda jika anda kehilangan handphone anda. Jika anda kehilangan kode pemulihan, anda bisa membuatnya ulang disini. Kode pemulihan anda yang lama tidak akan bisa digunakan lagi. manual_instructions: 'Jika anda tidak bisa memindai kode QR dan harus memasukkannya secara manual, ini dia kode yang harus dimasukkan:' recovery_codes_regenerated: Kode Pemulihan berhasil dibuat ulang - recovery_instructions: Jika anda kehilangan akses pada handphone anda, anda bisa menggunakan kode pemulihan dibawah ini untuk mendapatkan kembali akses pada akun anda. Simpan kode pemulihan anda baik-baik, misalnya dengan mencetaknya atau menyimpannya bersama dokumen penting lainnya. + recovery_instructions_html: Jika anda kehilangan akses pada handphone anda, anda bisa menggunakan kode pemulihan dibawah ini untuk mendapatkan kembali akses pada akun anda. Simpan kode pemulihan anda baik-baik, misalnya dengan mencetaknya atau menyimpannya bersama dokumen penting lainnya. setup: Persiapan wrong_code: Kode yang dimasukkan tidak cocok! Apa waktu server dan waktu di handphone sudah cocok? users: diff --git a/config/locales/io.yml b/config/locales/io.yml index def5b95247..db430b0feb 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -303,7 +303,7 @@ io: lost_recovery_codes: Recovery codes allow you to regain access to your account if you lose your phone. If you've lost your recovery codes, you can regenerate them here. Your old recovery codes will be invalidated. manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:' recovery_codes_regenerated: Recovery codes successfully regenerated - recovery_instructions: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe, for example by printing them and storing them with other important documents. + recovery_instructions_html: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe, for example by printing them and storing them with other important documents. setup: Set up wrong_code: The entered code was invalid! Are server time and device time correct? users: diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 94f02e9405..80169339da 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -360,7 +360,7 @@ ja: lost_recovery_codes: リカバリーコードを使用すると携帯電話を紛失した場合でもアカウントにアクセスできるようになります。 リカバリーコードを紛失した場合もここで再生成することができますが、古いリカバリーコードは無効になります。 manual_instructions: 'QRコードがスキャンできず、手動での登録を希望の場合はこのシークレットコードを利用してください。:' recovery_codes_regenerated: リカバリーコードが再生成されました。 - recovery_instructions: 携帯電話を紛失した場合、以下の内どれかのリカバリーコードを使用してアカウントへアクセスすることができます。 リカバリーコードは印刷して安全に保管してください。 + recovery_instructions_html: 携帯電話を紛失した場合、以下の内どれかのリカバリーコードを使用してアカウントへアクセスすることができます。 リカバリーコードは印刷して安全に保管してください。 setup: 初期設定 wrong_code: コードが間違っています。サーバー上の時間とデバイス上の時間が一致していることを確認してください。 users: diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 15d963808a..d9b02e09cd 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -228,7 +228,7 @@ nl: lost_recovery_codes: Met herstelcodes kun je toegang tot jouw account krijgen wanneer je jouw telefoon bent kwijtgeraakt. Wanneer je jouw herstelcodes bent kwijtgeraakt, kan je ze hier opnieuw genereren. Jouw oude herstelcodes zijn daarna ongeldig. manual_instructions: 'Hieronder vind je de geheime code in platte tekst. Voor het geval je de QR-code niet kunt scannen en het handmatig moet invoeren.' recovery_codes_regenerated: Opnieuw genereren herstelcodes geslaagd - recovery_instructions: Wanneer je ooit de toegang verliest tot jouw telefoon, kan je met behulp van een van de herstelcodes hieronder opnieuw toegang krijgen tot jouw account. Zorg ervoor dat je de herstelcodes op een veilige plek bewaard. (Je kunt ze bijvoorbeeld printen en ze samen met andere belangrijke documenten bewaren.) + recovery_instructions_html: Wanneer je ooit de toegang verliest tot jouw telefoon, kan je met behulp van een van de herstelcodes hieronder opnieuw toegang krijgen tot jouw account. Zorg ervoor dat je de herstelcodes op een veilige plek bewaard. (Je kunt ze bijvoorbeeld printen en ze samen met andere belangrijke documenten bewaren.) setup: Instellen wrong_code: De ingevoerde code is ongeldig! Klopt de systeemtijd van de server en die van jouw apparaat? users: diff --git a/config/locales/no.yml b/config/locales/no.yml index 1cd6620b6d..f71c08c6af 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -335,7 +335,7 @@ lost_recovery_codes: Gjenopprettingskoder lar deg gjenoppnå tilgang til din konto hvis du mister din telefon. Hvis du har mistet gjenopprettingskodene, kan du regenerere dem her. Dine gamle gjenopprettingskoder vil bli ugyldige. manual_instructions: 'Hvis du ikke får scannet QR-koden må du skrive inn følgende kode manuelt:' recovery_codes_regenerated: Generering av gjenopprettingskoder vellykket - recovery_instructions: Hvis du skulle miste tilgang til telefonen din, kan du bruke en av gjenopprettingskodene nedenfor til å gjenopprette tilgang til din konto. Oppbevar gjenopprettingskodene sikkert, for eksempel ved å skrive dem ut og lagre dem sammen med andre viktige dokumenter. + recovery_instructions_html: Hvis du skulle miste tilgang til telefonen din, kan du bruke en av gjenopprettingskodene nedenfor til å gjenopprette tilgang til din konto. Oppbevar gjenopprettingskodene sikkert, for eksempel ved å skrive dem ut og lagre dem sammen med andre viktige dokumenter. setup: Sett opp wrong_code: Den angitte koden var ugyldig! Stemmer instansens tid overalt med enhetens tid? users: diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 3770c06711..c882b43a11 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -220,7 +220,7 @@ oc: - dv - ds abbr_month_names: - - + - - gen - feb - mar @@ -246,7 +246,7 @@ oc: long: Lo %B %d de %Y short: "%b %d" month_names: - - + - - de genièr - de febrièr - de març @@ -411,7 +411,7 @@ oc: lost_recovery_codes: Los còdi de recuperacion vos permeton d’accedir a vòstre compte se perdètz vòstre mobil. S’avètz perdut vòstres còdis de recuperacion los podètz tornar generar aquí. Los ancians còdis seràn pas mai valides. manual_instructions: 'Se podètz pas numerizar lo còdi QR e que vos cal picar lo còdi a la man, vaquí lo còdi en clar :' recovery_codes_regenerated: Los còdis de recuperacion son ben estats tornats generar - recovery_instructions: Se vos arriba de perdre vòstre mobil, podètz utilizar un dels còdis de recuperacion cai-jos per poder tornar accedir a vòstre compte. Gardatz los còdis en seguretat, per exemple, imprimissètz los e gardatz los amb vòstres documents importants. + recovery_instructions_html: Se vos arriba de perdre vòstre mobil, podètz utilizar un dels còdis de recuperacion cai-jos per poder tornar accedir a vòstre compte. Gardatz los còdis en seguretat, per exemple, imprimissètz los e gardatz los amb vòstres documents importants. setup: Paramètres wrong_code: Lo còdi picat es invalid ! L’ora es la bona sul servidor e lo mobil ? users: diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 7376c3e2bb..97d20aa41c 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -364,7 +364,7 @@ pl: lost_recovery_codes: Kody zapasowe pozwolą uzyskać dostęp do portalu, jeżeli utracisz dostęp do telefonu. Jeżeli utracisz dostęp do nich, możesz wygenerować je ponownie tutaj. Poprzednie zostaną unieważnione. manual_instructions: 'Jeżeli nie możesz zeskanować kodu QR, musisz wprowadzić ten kod ręcznie:' recovery_codes_regenerated: Pomyślnie wygenerowano ponownie kody zapasowe - recovery_instructions: Jeżeli kiedykolwiek utracisz dostęp do telefonu, możesz wykorzystać jeden z kodów zapasowych, aby odzyskać dostęp do konta. Trzymaj je w bezpiecznym miejscu. (Na przykład, wydrukuj je i przechowuj z ważnymu dokumentami.) + recovery_instructions_html: Jeżeli kiedykolwiek utracisz dostęp do telefonu, możesz wykorzystać jeden z kodów zapasowych, aby odzyskać dostęp do konta. Trzymaj je w bezpiecznym miejscu. (Na przykład, wydrukuj je i przechowuj z ważnymu dokumentami.) setup: Skonfiguruj wrong_code: Wprowadzony kod jest niepoprawny! Czy czas serwera i urządzenia jest poprawny? users: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index b6f5497bd0..973a8d401f 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -333,7 +333,7 @@ pt-BR: lost_recovery_codes: Códigos de recuperação permite que você recupere o acesso a sua conta se você perder seu telefone. Se você perder os códigos de recuperação, você pode regera-los aqui. Seus códigos antigos serão invalidados. manual_instructions: 'Se você não puder scanear o código QR e precisa digita-los manualmente, aqui está o segredo em texto.:' recovery_codes_regenerated: Códigos de recuperação foram gerados com sucesso - recovery_instructions: Se algum dia você perder o acesso ao seu telefone, você pode usar um dos códigos de abaixo para recupera o acesso a sua conta. Guarde os códigos de acesso em local seguro, por exemplo imprimindo ou guardados com documentos importantes. + recovery_instructions_html: Se algum dia você perder o acesso ao seu telefone, você pode usar um dos códigos de abaixo para recupera o acesso a sua conta. Guarde os códigos de acesso em local seguro, por exemplo imprimindo ou guardados com documentos importantes. setup: Configurar wrong_code: O código digitado é inválido! Os relógios do servidor e do dispositivo estão corretos? users: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index c16ab6869a..9cf067d884 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -332,7 +332,7 @@ ru: lost_recovery_codes: Коды восстановления позволяют вернуть доступ к аккаунту в случае утери телефона. Если Вы потеряли Ваши коды восстановления, вы можете заново сгенерировать их здесь. Ваши старые коды восстановления будут аннулированы. manual_instructions: 'Если Вы не можете отсканировать QR-код и хотите ввести его вручную, секрет представлен здесь открытым текстом:' recovery_codes_regenerated: Коды восстановления успешно сгенерированы - recovery_instructions: В случае утери доступа к Вашему телефону Вы можете использовать один из кодов восстановления, указанных ниже, чтобы вернуть доступ к аккаунту. Держите коды восстановления в безопасности, например, распечатав их и храня с другими важными документами. + recovery_instructions_html: В случае утери доступа к Вашему телефону Вы можете использовать один из кодов восстановления, указанных ниже, чтобы вернуть доступ к аккаунту. Держите коды восстановления в безопасности, например, распечатав их и храня с другими важными документами. setup: Настроить wrong_code: Введенный код неверен! Правильно ли установлены серверное время и время устройства? users: diff --git a/config/locales/th.yml b/config/locales/th.yml index 6ef4b67898..322e5e74bf 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -335,7 +335,7 @@ th: lost_recovery_codes: Recovery codes allow you to regain access to your account if you lose your phone. If you've lost your recovery codes, you can regenerate them here. Your old recovery codes will be invalidated. manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:' recovery_codes_regenerated: Recovery codes successfully regenerated - recovery_instructions: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe, for example by printing them and storing them with other important documents. + recovery_instructions_html: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe, for example by printing them and storing them with other important documents. setup: ตั้งค่า wrong_code: รหัสที่กรอกไม่ถูกต้อง! Are server time and device time correct? users: diff --git a/config/locales/tr.yml b/config/locales/tr.yml index a4c870b642..0e33e2efe7 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -333,7 +333,7 @@ tr: lost_recovery_codes: Kurtarma kodları telefonunuzu kaybettiğiniz durumlarda hesabınıza erişim yapabilmenize olanak tanır. Eğer kurtarma kodlarınızı kaybettiyseniz burada tekrar oluşturabilirsiniz. Eski kurtarma kodlarınız geçersiz hale gelecektir. manual_instructions: 'Eğer QR kodunu taratamıyorsanız ve elle giriş yapmanız gerekiyorsa buradaki gizli düz metni girebilirsiniz:' recovery_codes_regenerated: Kurtarma kodları başarıyla oluşturuldu - recovery_instructions: 'Eğer telefonunuza erişiminizi kaybederseniz, aşağıdaki kurtarma kodlarından birini kullanarak hesabınıza giriş yapabilirsiniz. Kurtarma kodlarınızı güvenli halde tutunuz. Örneğin: kodların çıktısını alıp diğer önemli belgeleriniz ile birlikte saklayabilirsiniz.' + recovery_instructions_html: 'Eğer telefonunuza erişiminizi kaybederseniz, aşağıdaki kurtarma kodlarından birini kullanarak hesabınıza giriş yapabilirsiniz. Kurtarma kodlarınızı güvenli halde tutunuz. Örneğin: kodların çıktısını alıp diğer önemli belgeleriniz ile birlikte saklayabilirsiniz.' setup: Kuruluma başla wrong_code: Girdiğiniz kod geçersiz! Telefonunuzun saati geri/ileri kalmış olabilir. users: diff --git a/config/locales/uk.yml b/config/locales/uk.yml index c1ec61cda3..1327c1a7b2 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -319,7 +319,7 @@ uk: lost_recovery_codes: Коди відновлення дозволяють повернути доступ до акаунту у випадку втрати телефону. Якщо Ви втратили Ваші коди відновлення, Ви можете знову згенерувати їх тут. Ваші старі коди відновлення будуть анульовані. manual_instructions: 'Якщо Ви не можете відсканувати QR-код та хочете ввести його вручну, секрет представлений тут відкритим текстом:' recovery_codes_regenerated: Коди відновлення успішно згенеровані - recovery_instructions: У випадку втрати доступу до Вашого телефона Ви можете використати один з кодів відновлення, вказаних нижче, щоб повернути доступ до акаунту. Тримайте коди відновлення у безпеці, наприклад, роздрукувавши їх та тримаючи їх з іншими важливими документами. + recovery_instructions_html: У випадку втрати доступу до Вашого телефона Ви можете використати один з кодів відновлення, вказаних нижче, щоб повернути доступ до акаунту. Тримайте коди відновлення у безпеці, наприклад, роздрукувавши їх та тримаючи їх з іншими важливими документами. setup: Налаштувати wrong_code: Введений код неправильний! Чи правильно встановлені серверний час та час пристрою? users: diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index 9bf338ea4d..6c8e9fc6d9 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -339,7 +339,7 @@ zh-CN: lost_recovery_codes: 如果你丢了手机,你可以用恢复代码重新访问你的账户。如果你丢了恢复代码,也可以在这里重新生成一个,不过以前的恢复代码就失效了。(废话) manual_instructions: 如果你无法扫描 QR 二维码,请手动输入这个文本密码︰ recovery_codes_regenerated: 已成功重新生成恢复代码 - recovery_instructions: 如果你的手机无法使用,你可以使用下面的任何恢复代码来恢复你的账号。请保管好你的恢复代码以防泄漏(例如你可以打印好它们并和重要文档一起保存)。 + recovery_instructions_html: 如果你的手机无法使用,你可以使用下面的任何恢复代码来恢复你的账号。请保管好你的恢复代码以防泄漏(例如你可以打印好它们并和重要文档一起保存)。 setup: 设置 wrong_code: 你输入的认证码并不正确!可能服务器时间和你手机不一致,请检查你手机的时钟,或与本站管理员联系。 users: diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 9a110f7dae..4d8262c5b7 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -334,7 +334,7 @@ zh-HK: lost_recovery_codes: 讓你可以在遺失電話時,使用備用驗證碼登入。如果你遺失了備用驗證碼,可以在這裏產生一批新的,舊有的備用驗證碼將會失效。 manual_instructions: 如果你無法掃描 QR 圖形碼,請手動輸入這個文字密碼︰ recovery_codes_regenerated: 成功產生新的備用驗證碼 - recovery_instructions: 如果你遺失了安裝認證器的裝置(如︰你的電話),你可以使用備用驗證碼進行登入。請確保將備用驗證碼收藏穩當,(如列印出來,和你其他重要文件一起存放) + recovery_instructions_html: 如果你遺失了安裝認證器的裝置(如︰你的電話),你可以使用備用驗證碼進行登入。請確保將備用驗證碼收藏穩當,(如列印出來,和你其他重要文件一起存放) setup: 設定 wrong_code: 你輸入的認證碼並不正確!可能伺服器時間和你手機不一致,請檢查你手機的時鐘,或與本站管理員聯絡。 users: diff --git a/spec/controllers/settings/two_factor_authentications_controller_spec.rb b/spec/controllers/settings/two_factor_authentications_controller_spec.rb index 4d1a01fcfa..6c49f6f0dd 100644 --- a/spec/controllers/settings/two_factor_authentications_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentications_controller_spec.rb @@ -79,13 +79,41 @@ describe Settings::TwoFactorAuthenticationsController do user.update(otp_required_for_login: true) end - it 'turns off otp requirement if signed in' do - sign_in user, scope: :user - post :destroy + context 'when signed in' do + before do + sign_in user, scope: :user + end - expect(response).to redirect_to(settings_two_factor_authentication_path) - user.reload - expect(user.otp_required_for_login).to eq(false) + it 'turns off otp requirement with correct code' do + expect_any_instance_of(User).to receive(:validate_and_consume_otp!) do |value, arg| + expect(value).to eq user + expect(arg).to eq '123456' + true + end + + post :destroy, params: { form_two_factor_confirmation: { code: '123456' } } + + expect(response).to redirect_to(settings_two_factor_authentication_path) + user.reload + expect(user.otp_required_for_login).to eq(false) + end + + it 'does not turn off otp if code is incorrect' do + expect_any_instance_of(User).to receive(:validate_and_consume_otp!) do |value, arg| + expect(value).to eq user + expect(arg).to eq '057772' + false + end + + post :destroy, params: { form_two_factor_confirmation: { code: '057772' } } + + user.reload + expect(user.otp_required_for_login).to eq(true) + end + + it 'raises ActionController::ParameterMissing if code is missing' do + expect { post :destroy }.to raise_error(ActionController::ParameterMissing) + end end it 'redirects if not signed in' do From c972e1ee1fdc7ebc8bd4ad764cdf189ca4874764 Mon Sep 17 00:00:00 2001 From: unarist Date: Mon, 26 Jun 2017 08:45:50 +0900 Subject: [PATCH 19/37] Ignore DB_NAME for development env on streaming as well as rails side (#3948) --- streaming/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streaming/index.js b/streaming/index.js index 701cb2f557..d69f8fa19a 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -80,7 +80,7 @@ const startWorker = (workerId) => { development: { user: process.env.DB_USER || pg.defaults.user, password: process.env.DB_PASS || pg.defaults.password, - database: process.env.DB_NAME || 'mastodon_development', + database: 'mastodon_development', host: process.env.DB_HOST || pg.defaults.host, port: process.env.DB_PORT || pg.defaults.port, max: 10, From e5563843a2c063ab0295f778428183361e0aa326 Mon Sep 17 00:00:00 2001 From: Takuya Yoshida Date: Mon, 26 Jun 2017 08:46:15 +0900 Subject: [PATCH 20/37] Re-fix errorMiddleware (#3922) --- streaming/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streaming/index.js b/streaming/index.js index d69f8fa19a..e641c65485 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -246,7 +246,7 @@ const startWorker = (workerId) => { accountFromRequest(req, next); }; - const errorMiddleware = (err, req, res, next) => { // eslint-disable-line no-unused-vars + const errorMiddleware = (err, req, res, {}) => { log.error(req.requestId, err.toString()); res.writeHead(err.statusCode || 500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: err.statusCode ? err.toString() : 'An unexpected error occurred' })); From 285038972b724e646aa1a3aa2096b161524bce09 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 26 Jun 2017 11:49:39 +0900 Subject: [PATCH 21/37] Stop using Babel with streaming server (#3950) --- package.json | 2 +- streaming/.babelrc | 15 --------------- streaming/index.js | 22 +++++++++++----------- 3 files changed, 12 insertions(+), 27 deletions(-) delete mode 100644 streaming/.babelrc diff --git a/package.json b/package.json index d1fd22fb57..4b967bad79 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "build:development": "cross-env RAILS_ENV=development ./bin/webpack", "build:production": "cross-env RAILS_ENV=production ./bin/webpack", "manage:translations": "node ./config/webpack/translationRunner.js", - "start": "rimraf ./tmp/streaming && babel ./streaming/index.js --out-dir ./tmp && node ./tmp/streaming/index.js", + "start": "node ./streaming/index.js", "storybook": "cross-env NODE_ENV=test start-storybook -s ./public -p 9001 -c storybook", "test": "npm run test:lint && npm run test:mocha", "test:lint": "eslint -c .eslintrc.yml --ext=js app/javascript/ config/webpack/ spec/javascript/ storybook/ streaming/", diff --git a/streaming/.babelrc b/streaming/.babelrc deleted file mode 100644 index dc1ec43039..0000000000 --- a/streaming/.babelrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "targets": { - "node": "current" - } - } - ] - ], - "plugins": [ - "transform-object-rest-spread" - ] -} diff --git a/streaming/index.js b/streaming/index.js index e641c65485..400456d243 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -1,14 +1,14 @@ -import os from 'os'; -import throng from 'throng'; -import dotenv from 'dotenv'; -import express from 'express'; -import http from 'http'; -import redis from 'redis'; -import pg from 'pg'; -import log from 'npmlog'; -import url from 'url'; -import WebSocket from 'uws'; -import uuid from 'uuid'; +const os = require('os'); +const throng = require('throng'); +const dotenv = require('dotenv'); +const express = require('express'); +const http = require('http'); +const redis = require('redis'); +const pg = require('pg'); +const log = require('npmlog'); +const url = require('url'); +const WebSocket = require('uws'); +const uuid = require('uuid'); const env = process.env.NODE_ENV || 'development'; From f53ed108b0c6218eef118bbd0b77078d36c46a96 Mon Sep 17 00:00:00 2001 From: Alda Marteau-Hardi Date: Mon, 26 Jun 2017 13:04:36 +0200 Subject: [PATCH 22/37] Translate pin/unpin and fix some inconsistencies in gender neutral strings (#3952) --- app/javascript/mastodon/locales/fr.json | 6 +++--- config/locales/fr.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 52d6b0f87c..1a69235c89 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -27,8 +27,8 @@ "column.notifications": "Notifications", "column.public": "Fil public global", "column_back_button.label": "Retour", - "column_header.pin": "Pin", - "column_header.unpin": "Unpin", + "column_header.pin": "Épingler", + "column_header.unpin": "Retirer", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Paramètres", "compose_form.lock_disclaimer": "Votre compte n'est pas {locked}. Tout le monde peut vous suivre et voir vos pouets restreints.", @@ -101,7 +101,7 @@ "notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?", "notifications.column_settings.alert": "Notifications locales", "notifications.column_settings.favourite": "Favoris :", - "notifications.column_settings.follow": "Nouveaux abonné⋅e⋅s :", + "notifications.column_settings.follow": "Nouveaux⋅elles abonn⋅é⋅s :", "notifications.column_settings.mention": "Mentions :", "notifications.column_settings.reblog": "Partages :", "notifications.column_settings.show": "Afficher dans la colonne", diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 0c3f3b1d53..dfe5ff990c 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -238,7 +238,7 @@ fr: mention: "%{name} vous a mentionné⋅e" new_followers_summary: one: Vous avez un⋅e nouvel⋅le abonné⋅e ! Youpi ! - other: Vous avez %{count} nouveaux abonné⋅es ! Incroyable ! + other: Vous avez %{count} nouveaux⋅elles abonné⋅e⋅s ! Incroyable ! subject: one: "Une nouvelle notification depuis votre dernière visite \U0001F418" other: "%{count} nouvelles notifications depuis votre dernière visite \U0001F418" From 7aeb9168b01971e4e2c4c9b757dff09638b171ac Mon Sep 17 00:00:00 2001 From: Daniel Hunsaker Date: Mon, 26 Jun 2017 05:15:24 -0600 Subject: [PATCH 23/37] Add .gitattributes file to avoid unwanted CRLF (#3954) When Windows checks out files, it defaults to changing line endings to CRLF. If these files are then copied to a Linux system to be run, and the endings aren't changed at some point in that process, things break. This file forces git to use LF for all text files on all systems (except the request testing specfiles) to prevent issues everywhere. --- .gitattributes | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..e69f2a0adf --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +* text=auto eol=lf +*.eot -text +*.gif -text +*.gz -text +*.ico -text +*.jpg -text +*.mp3 -text +*.ogg -text +*.png -text +*.ttf -text +*.webm -text +*.woff -text +*.woff2 -text +spec/fixtures/requests/** -text !eol From ae2b722f5510cc5ab54cd5e6419136f1a63892df Mon Sep 17 00:00:00 2001 From: m4sk1n Date: Mon, 26 Jun 2017 17:10:54 +0200 Subject: [PATCH 24/37] i18n: Warning to look into the spam folder (pl) (#3955) --- config/locales/devise.pl.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config/locales/devise.pl.yml b/config/locales/devise.pl.yml index d4ee1b6aea..792e0d81e4 100644 --- a/config/locales/devise.pl.yml +++ b/config/locales/devise.pl.yml @@ -3,8 +3,8 @@ pl: devise: confirmations: confirmed: Twój adres e-mail został poprawnie zweryfikowany. - send_instructions: W ciągu kilku minut otrzymasz wiadomosć e-mail z instrukcją jak potwierdzić Twój adres e-mail. - send_paranoid_instructions: Jeśli Twój adres e-mail już istnieje w naszej bazie danych, w ciągu kilku minut otrzymasz wiadomość e-mail z instrukcją jak potwierdzić Twój adres e-mail. + send_instructions: W ciągu kilku minut otrzymasz wiadomosć e-mail z instrukcją jak potwierdzić Twój adres e-mail. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. + send_paranoid_instructions: Jeśli Twój adres e-mail już istnieje w naszej bazie danych, w ciągu kilku minut otrzymasz wiadomość e-mail z instrukcją jak potwierdzić Twój adres e-mail. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. failure: already_authenticated: Jesteś już zalogowany/zalogowana. inactive: Twoje konto nie zostało jeszcze aktywowane. @@ -29,8 +29,8 @@ pl: success: Uwierzytelnienie przez %{kind} powiodło się. passwords: no_token: Dostęp do tej strony możliwy jest wyłącznie za pomocą odnośnika z e-maila z instrukcjami ustawienia nowego hasła. Jeśli skorzystałeś/aś z takiego odnośnika, upewnij się, że został wykorzystany/skopiowany cały odnośnik. - send_instructions: W ciągu kilku minut otrzymasz wiadomość e-mail z instrukcją ustawienia nowego hasła. - send_paranoid_instructions: Jeśli Twój adres e-mail już istnieje w naszej bazie danych, w ciągu kilku minut otrzymasz wiadomość e-mail zawierającą odnośnik pozwalający na ustawienie nowego hasła. + send_instructions: W ciągu kilku minut otrzymasz wiadomość e-mail z instrukcją ustawienia nowego hasła. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. + send_paranoid_instructions: Jeśli Twój adres e-mail już istnieje w naszej bazie danych, w ciągu kilku minut otrzymasz wiadomość e-mail zawierającą odnośnik pozwalający na ustawienie nowego hasła. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. updated: Twoje hasło zostało zmienione. Jesteś zalogowany/a. updated_not_active: Twoje hasło zostało zmienione. registrations: @@ -38,16 +38,16 @@ pl: signed_up: Twoje konto zostało utworzone. Witamy! signed_up_but_inactive: Twoje konto zostało utworzone. Nie mogliśmy Cię jednak zalogować, ponieważ konto nie zostało jeszcze aktywowane. signed_up_but_locked: Twoje konto zostało utworzone. Nie mogliśmy Cię jednak zalogować, ponieważ konto jest zablokowane. - signed_up_but_unconfirmed: Na Twój adres e-mail została wysłana wiadomosć z odnośnikiem potwierdzającym. Kliknij w odnośnik aby aktywować konto. - update_needs_confirmation: Konto zostało zaktualizowane, musimy jednak zweryfikować Twój nowy adres e-mail. Została na niego wysłana wiadomość z odnośnikiem potwierdzającym. + signed_up_but_unconfirmed: Na Twój adres e-mail została wysłana wiadomosć z odnośnikiem potwierdzającym. Kliknij w odnośnik aby aktywować konto. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. + update_needs_confirmation: Konto zostało zaktualizowane, musimy jednak zweryfikować Twój nowy adres e-mail. Została na niego wysłana wiadomość z odnośnikiem potwierdzającym. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. updated: Konto zostało zaktualizowane. sessions: already_signed_out: Zostałeś/aś wylogowany/a. signed_in: Zostałeś/aś zalogowany/a. signed_out: Zostałeś/aś wylogowany/a. unlocks: - send_instructions: W ciągu kilku minut otrzymasz wiadomość e-mail z instrukcjami odblokowania konta. - send_paranoid_instructions: Jeśli Twoje konto istnieje, instrukcje odblokowania go otrzymasz w wiadomości e-mail w ciągu kilku minut. + send_instructions: W ciągu kilku minut otrzymasz wiadomość e-mail z instrukcjami odblokowania konta. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. + send_paranoid_instructions: Jeśli Twoje konto istnieje, instrukcje odblokowania go otrzymasz w wiadomości e-mail w ciągu kilku minut. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. unlocked: Twoje konto zostało odblokowane. Zaloguj się aby kontynuować. errors: messages: From 646de92781ba9d211255a7eaf7ebca7b365612f6 Mon Sep 17 00:00:00 2001 From: m4sk1n Date: Mon, 26 Jun 2017 17:18:45 +0200 Subject: [PATCH 25/37] i18n: Updated Polish translation (#3956) * i18n: Updated Polish translation * Update pl.yml --- config/locales/pl.yml | 47 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 97d20aa41c..81eee8dc3e 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -200,7 +200,7 @@ pl: applications: invalid_url: Ten URL jest nieprawidłowy auth: - change_password: Uwierzytelnienie + change_password: Bezpieczeństwo delete_account: Usunięcie konta delete_account_html: Jeżeli próbowałeś usunąć konto, przejdź tutaj. Otrzymasz prośbę o potwierdzenie. didnt_get_confirmation: Nie otrzymałeś instrukcji weryfikacji? @@ -323,7 +323,44 @@ pl: acct: Podaj swój adres (nazwa@domena), z którego chcesz śledzić missing_resource: Nie udało się znaleźć adresu przekierowania z Twojej domeny proceed: Śledź - prompt: 'Śledzony będzie:' + prompt: 'Zamierzasz śledzić:' + sessions: + activity: Ostatnia aktywność + browser: Przeglądarka + browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + firefox: Firefox + generic: nieznana przeglądarka + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo + current_session: Obecna sesja + description: "%{browser} na %{platform}" + explanation: Przeglądarki z aktywną sesją Twojego konta. + ip: Adres IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: macOS + other: nieznana platforma + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + title: Sesje settings: authorized_apps: Uwierzytelnione aplikacje back: Powrót do Mastodona @@ -358,13 +395,17 @@ pl: description_html: Jeśli włączysz uwierzytelnianie dwuetapowe, logowanie się będzie wymagało podania tokenu wyświetlonego na Twoim telefonie. disable: Wyłącz enable: Włącz + enabled: Uwierzytelnianie dwuetapowe jest włączone enabled_success: Pomyślnie aktywowano uwierzytelnianie dwuetapowe generate_recovery_codes: Generuj kody zapasowe instructions_html: "Zeskanuj ten kod QR na swoim urządzeniu za pomocą Google Authenticator, FreeOTP lub podobnej aplikacji. Od teraz będzie ona generowała kody wymagane przy logowaniu." lost_recovery_codes: Kody zapasowe pozwolą uzyskać dostęp do portalu, jeżeli utracisz dostęp do telefonu. Jeżeli utracisz dostęp do nich, możesz wygenerować je ponownie tutaj. Poprzednie zostaną unieważnione. manual_instructions: 'Jeżeli nie możesz zeskanować kodu QR, musisz wprowadzić ten kod ręcznie:' + recovery_codes: Przywróć kody zapasowe recovery_codes_regenerated: Pomyślnie wygenerowano ponownie kody zapasowe - recovery_instructions_html: Jeżeli kiedykolwiek utracisz dostęp do telefonu, możesz wykorzystać jeden z kodów zapasowych, aby odzyskać dostęp do konta. Trzymaj je w bezpiecznym miejscu. (Na przykład, wydrukuj je i przechowuj z ważnymu dokumentami.) + recovery_instructions_html: + Jeżeli kiedykolwiek utracisz dostęp do telefonu, możesz wykorzystać jeden z kodów zapasowych, aby odzyskać dostęp do konta. Trzymaj je w bezpiecznym miejscu. + Na przykład, wydrukuj je i przechowuj z ważnymu dokumentami. setup: Skonfiguruj wrong_code: Wprowadzony kod jest niepoprawny! Czy czas serwera i urządzenia jest poprawny? users: From a91d968cab5120ca389fcc7d6788cafca85e69e7 Mon Sep 17 00:00:00 2001 From: ThibG Date: Mon, 26 Jun 2017 19:39:58 +0200 Subject: [PATCH 26/37] Raise an error if salmon request response is unsatisfactory (#3960) --- app/services/send_interaction_service.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/send_interaction_service.rb b/app/services/send_interaction_service.rb index 504f41c727..34c8f9e34a 100644 --- a/app/services/send_interaction_service.rb +++ b/app/services/send_interaction_service.rb @@ -13,7 +13,8 @@ class SendInteractionService < BaseService return if block_notification? envelope = salmon.pack(@xml, @source_account.keypair) - salmon.post(@target_account.salmon_url, envelope) + delivery = salmon.post(@target_account.salmon_url, envelope) + raise "Delivery failed for #{target_account.salmon_url}: HTTP #{delivery.code}" unless delivery.code > 199 && delivery.code < 300 end private From 42b82206322c73c4a4d7ac29ca9a781ab11e7b1a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 27 Jun 2017 00:04:00 +0200 Subject: [PATCH 27/37] Fix #1624 - Send e-mail notifications to admins about new reports (#3949) --- app/controllers/api/v1/reports_controller.rb | 3 +++ app/mailers/admin_mailer.rb | 13 +++++++++++++ app/mailers/application_mailer.rb | 8 ++++++++ app/mailers/notification_mailer.rb | 8 -------- app/views/admin_mailer/new_report.text.erb | 5 +++++ config/locales/en.yml | 8 +++++--- .../controllers/api/v1/reports_controller_spec.rb | 15 ++++++++++++--- 7 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 app/mailers/admin_mailer.rb create mode 100644 app/views/admin_mailer/new_report.text.erb diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb index 71df76e922..8e7070d077 100644 --- a/app/controllers/api/v1/reports_controller.rb +++ b/app/controllers/api/v1/reports_controller.rb @@ -17,6 +17,9 @@ class Api::V1::ReportsController < Api::BaseController status_ids: reported_status_ids, comment: report_params[:comment] ) + + User.admins.includes(:account).each { |u| AdminMailer.new_report(u.account, @report).deliver_later } + render :show end diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb new file mode 100644 index 0000000000..fc19a6d40b --- /dev/null +++ b/app/mailers/admin_mailer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class AdminMailer < ApplicationMailer + def new_report(recipient, report) + @report = report + @me = recipient + @instance = Rails.configuration.x.local_domain + + locale_for_account(@me) do + mail to: @me.user_email, subject: I18n.t('admin_mailer.new_report.subject', instance: @instance, id: @report.id) + end + end +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index e5dbfeeda9..2e730c19b0 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -4,4 +4,12 @@ class ApplicationMailer < ActionMailer::Base default from: ENV.fetch('SMTP_FROM_ADDRESS') { 'notifications@localhost' } layout 'mailer' helper :instance + + protected + + def locale_for_account(account) + I18n.with_locale(account.user_locale || I18n.default_locale) do + yield + end + end end diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index a944db137f..12b92bf451 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -67,12 +67,4 @@ class NotificationMailer < ApplicationMailer ) end end - - private - - def locale_for_account(account) - I18n.with_locale(account.user_locale || I18n.default_locale) do - yield - end - end end diff --git a/app/views/admin_mailer/new_report.text.erb b/app/views/admin_mailer/new_report.text.erb new file mode 100644 index 0000000000..6fa744bc3d --- /dev/null +++ b/app/views/admin_mailer/new_report.text.erb @@ -0,0 +1,5 @@ +<%= display_name(@me) %>, + +<%= raw t('admin_mailer.new_report.body', target: @report.target_account.acct, reporter: @report.account.acct) %> + +<%= raw t('application_mailer.view')%> <%= admin_report_url(@report) %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 9daaf53ecd..944c24c60c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -193,6 +193,10 @@ en: title: PubSubHubbub topic: Topic title: Administration + admin_mailer: + new_report: + body: "%{reporter} has reported %{target}" + subject: New report for %{instance} (#%{id}) application_mailer: settings: 'Change e-mail preferences: %{link}' signature: Mastodon notifications from %{instance} @@ -399,9 +403,7 @@ en: manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:' recovery_codes: Backup recovery codes recovery_codes_regenerated: Recovery codes successfully regenerated - recovery_instructions_html: - If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe. - For example, you may print them and store them with other important documents. + recovery_instructions_html: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe. For example, you may print them and store them with other important documents. setup: Set up wrong_code: The entered code was invalid! Are server time and device time correct? users: diff --git a/spec/controllers/api/v1/reports_controller_spec.rb b/spec/controllers/api/v1/reports_controller_spec.rb index 3df6cdfe74..471ea4e0bc 100644 --- a/spec/controllers/api/v1/reports_controller_spec.rb +++ b/spec/controllers/api/v1/reports_controller_spec.rb @@ -21,12 +21,21 @@ RSpec.describe Api::V1::ReportsController, type: :controller do end describe 'POST #create' do - it 'creates a report' do - status = Fabricate(:status) - post :create, params: { status_ids: [status.id], account_id: status.account.id, comment: 'reasons' } + let!(:status) { Fabricate(:status) } + let!(:admin) { Fabricate(:user, admin: true) } + before do + allow(AdminMailer).to receive(:new_report).and_return(double('email', deliver_later: nil)) + post :create, params: { status_ids: [status.id], account_id: status.account.id, comment: 'reasons' } + end + + it 'creates a report' do expect(status.reload.account.targeted_reports).not_to be_empty expect(response).to have_http_status(:success) end + + it 'sends e-mails to admins' do + expect(AdminMailer).to have_received(:new_report).with(admin.account, Report) + end end end From 98eaa2aa27100ac0037cced9d4d4c86bcb9396e7 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Tue, 27 Jun 2017 20:41:03 +0900 Subject: [PATCH 28/37] Update Rails to v5.1.2 (#3968) --- Gemfile.lock | 64 ++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 627a01787b..c19f31e010 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,40 +1,40 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.1.1) - actionpack (= 5.1.1) + actioncable (5.1.2) + actionpack (= 5.1.2) nio4r (~> 2.0) websocket-driver (~> 0.6.1) - actionmailer (5.1.1) - actionpack (= 5.1.1) - actionview (= 5.1.1) - activejob (= 5.1.1) + actionmailer (5.1.2) + actionpack (= 5.1.2) + actionview (= 5.1.2) + activejob (= 5.1.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.1) - actionview (= 5.1.1) - activesupport (= 5.1.1) + actionpack (5.1.2) + actionview (= 5.1.2) + activesupport (= 5.1.2) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.1) - activesupport (= 5.1.1) + actionview (5.1.2) + activesupport (= 5.1.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) active_record_query_trace (1.5.4) - activejob (5.1.1) - activesupport (= 5.1.1) + activejob (5.1.2) + activesupport (= 5.1.2) globalid (>= 0.3.6) - activemodel (5.1.1) - activesupport (= 5.1.1) - activerecord (5.1.1) - activemodel (= 5.1.1) - activesupport (= 5.1.1) + activemodel (5.1.2) + activesupport (= 5.1.2) + activerecord (5.1.2) + activemodel (= 5.1.2) + activesupport (= 5.1.2) arel (~> 8.0) - activesupport (5.1.1) + activesupport (5.1.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) @@ -298,17 +298,17 @@ GEM rack-test (0.6.3) rack (>= 1.0) rack-timeout (0.4.2) - rails (5.1.1) - actioncable (= 5.1.1) - actionmailer (= 5.1.1) - actionpack (= 5.1.1) - actionview (= 5.1.1) - activejob (= 5.1.1) - activemodel (= 5.1.1) - activerecord (= 5.1.1) - activesupport (= 5.1.1) + rails (5.1.2) + actioncable (= 5.1.2) + actionmailer (= 5.1.2) + actionpack (= 5.1.2) + actionview (= 5.1.2) + activejob (= 5.1.2) + activemodel (= 5.1.2) + activerecord (= 5.1.2) + activesupport (= 5.1.2) bundler (>= 1.3.0, < 2.0) - railties (= 5.1.1) + railties (= 5.1.2) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -324,9 +324,9 @@ GEM railties (~> 5.0) rails-settings-cached (0.6.5) rails (>= 4.2.0) - railties (5.1.1) - actionpack (= 5.1.1) - activesupport (= 5.1.1) + railties (5.1.2) + actionpack (= 5.1.2) + activesupport (= 5.1.2) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) From 8f2c91568c7ab552a87d02813e6b02be65f8707f Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Tue, 27 Jun 2017 20:43:53 +0900 Subject: [PATCH 29/37] Maintain aspect ratio for preview image (#3966) --- .../features/ui/components/image_loader.js | 122 ++++++++++++++---- app/javascript/styles/components.scss | 30 +++-- 2 files changed, 116 insertions(+), 36 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/image_loader.js b/app/javascript/mastodon/features/ui/components/image_loader.js index 5c3879970e..52c3a898b7 100644 --- a/app/javascript/mastodon/features/ui/components/image_loader.js +++ b/app/javascript/mastodon/features/ui/components/image_loader.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; export default class ImageLoader extends React.PureComponent { @@ -20,46 +21,121 @@ export default class ImageLoader extends React.PureComponent { error: false, } - componentWillMount() { - this._loadImage(this.props.src); + removers = []; + + get canvasContext() { + if (!this.canvas) { + return null; + } + this._canvasContext = this._canvasContext || this.canvas.getContext('2d'); + return this._canvasContext; } - componentWillReceiveProps(props) { - this._loadImage(props.src); + componentDidMount () { + this.loadImage(this.props); } - _loadImage(src) { + componentWillReceiveProps (nextProps) { + if (this.props.src !== nextProps.src) { + this.loadImage(nextProps); + } + } + + loadImage (props) { + this.removeEventListeners(); + this.setState({ loading: true, error: false }); + Promise.all([ + this.loadPreviewCanvas(props), + this.loadOriginalImage(props), + ]) + .then(() => { + this.setState({ loading: false, error: false }); + this.clearPreviewCanvas(); + }) + .catch(() => this.setState({ loading: false, error: true })); + } + + loadPreviewCanvas = ({ previewSrc, width, height }) => new Promise((resolve, reject) => { const image = new Image(); + const removeEventListeners = () => { + image.removeEventListener('error', handleError); + image.removeEventListener('load', handleLoad); + }; + const handleError = () => { + removeEventListeners(); + reject(); + }; + const handleLoad = () => { + removeEventListeners(); + this.canvasContext.drawImage(image, 0, 0, width, height); + resolve(); + }; + image.addEventListener('error', handleError); + image.addEventListener('load', handleLoad); + image.src = previewSrc; + this.removers.push(removeEventListeners); + }) - image.onerror = () => this.setState({ loading: false, error: true }); - image.onload = () => this.setState({ loading: false, error: false }); - - image.src = src; - - this.setState({ loading: true }); + clearPreviewCanvas () { + const { width, height } = this.canvas; + this.canvasContext.clearRect(0, 0, width, height); } - render() { - const { alt, src, previewSrc, width, height } = this.props; + loadOriginalImage = ({ src }) => new Promise((resolve, reject) => { + const image = new Image(); + const removeEventListeners = () => { + image.removeEventListener('error', handleError); + image.removeEventListener('load', handleLoad); + }; + const handleError = () => { + removeEventListeners(); + reject(); + }; + const handleLoad = () => { + removeEventListeners(); + resolve(); + }; + image.addEventListener('error', handleError); + image.addEventListener('load', handleLoad); + image.src = src; + this.removers.push(removeEventListeners); + }); + + removeEventListeners () { + this.removers.forEach(listeners => listeners()); + this.removers = []; + } + + setCanvasRef = c => { + this.canvas = c; + } + + render () { + const { alt, src, width, height } = this.props; const { loading } = this.state; + const className = classNames('image-loader', { + 'image-loader--loading': loading, + }); + return ( -
- {alt} + - {loading && + {!loading && ( - } + )}
); } diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index bb9723f5a4..91ebd91fd1 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -1099,20 +1099,22 @@ .image-loader { position: relative; -} -.image-loader__preview-img { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - filter: blur(2px); -} + &.image-loader--loading { + .image-loader__preview-canvas { + filter: blur(2px); + } + } -.media-modal img.image-loader__preview-img { - width: 100%; - height: 100%; + .image-loader__img { + position: absolute; + top: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; + background-image: none; + } } .navigation-bar { @@ -2933,6 +2935,7 @@ button.icon-button.active i.fa-retweet { position: relative; img, + canvas, video { max-width: 80vw; max-height: 80vh; @@ -2940,7 +2943,8 @@ button.icon-button.active i.fa-retweet { height: auto; } - img { + img, + canvas { display: block; background: url('../images/void.png') repeat; } From e2dd576a1b20c324c88afadaaa4f23e554b73ec7 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Tue, 27 Jun 2017 20:46:11 +0900 Subject: [PATCH 30/37] Update dependencies for Node.js (#3967) * Update @storybook/addon-actions to v3.1.6 * Update @storybook/react to v3.1.6 * Update babel-loader to v7.1.0 * Update babel-plugin-transform-react-remove-prop-types to v0.4.6 * Update enzyme to v2.9.1 * Update fsevents to v1.1.2 * Update intersection-observer to v0.3.2 * Update npmlog to v4.1.2 * Update pg to v6.4.0 * Update postcss-loader to v2.0.6 * Update rails-ujs to v5.1.2 * Update react to v15.6.1 * Update react-addons-shallow-compare to v15.6.0 * Update react-dom to v15.6.0 * Update react-notification to v6.7.1 * Update react-test-renderer to v15.6.1 * Update react-textarea-autosize to v5.0.7 * Update redux to v3.7.1 * Update resolve-url-loader to v2.1.0 * Update sass-loader to v6.0.6 * Update sinon to v2.3.5 * Update stringz to v0.2.2 * Update uuid to v3.1.0 * Update websocket.js to v0.1.12 * Update yargs to v8.0.2 * yarn upgrade --- package.json | 48 ++--- yarn.lock | 567 +++++++++++++++++++++++++++++---------------------- 2 files changed, 348 insertions(+), 267 deletions(-) diff --git a/package.json b/package.json index 4b967bad79..60f9af1e92 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "axios": "^0.16.2", "babel-cli": "^6.24.1", "babel-core": "^6.25.0", - "babel-loader": "^7.0.0", + "babel-loader": "^7.1.0", "babel-plugin-lodash": "^3.2.11", "babel-plugin-react-intl": "^2.3.1", "babel-plugin-react-transform": "^2.0.2", @@ -35,7 +35,7 @@ "babel-plugin-transform-object-rest-spread": "^6.23.0", "babel-plugin-transform-react-jsx-self": "^6.22.0", "babel-plugin-transform-react-jsx-source": "^6.22.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.5", + "babel-plugin-transform-react-remove-prop-types": "^0.4.6", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.5.2", "babel-preset-react": "^6.24.1", @@ -55,7 +55,7 @@ "glob": "^7.1.1", "http-link-header": "^0.8.0", "immutable": "^3.8.1", - "intersection-observer": "^0.3.0", + "intersection-observer": "^0.3.2", "intl": "^1.2.5", "intl-relativeformat": "^1.3.0", "is-nan": "^1.2.1", @@ -65,71 +65,71 @@ "marky": "^1.2.0", "mkdirp": "^0.5.1", "node-sass": "^4.5.2", - "npmlog": "^4.1.0", + "npmlog": "^4.1.2", "object-assign": "^4.1.1", "path-complete-extname": "^0.1.0", - "pg": "^6.2.4", - "postcss-loader": "^2.0.5", + "pg": "^6.4.0", + "postcss-loader": "^2.0.6", "postcss-smart-import": "^0.7.4", "precss": "^1.4.0", "prop-types": "^15.5.10", "punycode": "^2.1.0", - "rails-ujs": "^5.1.1", - "react": "^15.6.0", + "rails-ujs": "^5.1.2", + "react": "^15.6.1", "react-addons-perf": "^15.4.2", - "react-addons-shallow-compare": "^15.5.2", - "react-dom": "^15.6.0", + "react-addons-shallow-compare": "^15.6.0", + "react-dom": "^15.6.1", "react-immutable-proptypes": "^2.1.0", "react-immutable-pure-component": "^1.0.0", "react-intl": "^2.3.0", "react-motion": "^0.5.0", - "react-notification": "^6.7.0", + "react-notification": "^6.7.1", "react-redux": "^5.0.4", "react-redux-loading-bar": "^2.9.2", "react-router-dom": "^4.1.1", "react-router-scroll": "ytase/react-router-scroll#build", "react-simple-dropdown": "^3.0.0", "react-swipeable": "^4.0.1", - "react-textarea-autosize": "^5.0.6", + "react-textarea-autosize": "^5.0.7", "react-toggle": "^4.0.1", "redis": "^2.7.1", - "redux": "^3.6.0", + "redux": "^3.7.1", "redux-immutable": "^4.0.0", "redux-thunk": "^2.2.0", "requestidlecallback": "^0.3.0", "reselect": "^3.0.1", - "resolve-url-loader": "^2.0.2", + "resolve-url-loader": "^2.1.0", "rimraf": "^2.6.1", - "sass-loader": "^6.0.5", - "stringz": "^0.2.1", + "sass-loader": "^6.0.6", + "stringz": "^0.2.2", "style-loader": "^0.18.2", "throng": "^4.0.0", "tiny-queue": "^0.2.1", - "uuid": "^3.0.1", + "uuid": "^3.1.0", "uws": "^0.14.5", "webpack": "^3.0.0", "webpack-bundle-analyzer": "^2.8.2", "webpack-manifest-plugin": "^1.1.0", "webpack-merge": "^4.1.0", - "websocket.js": "^0.1.10" + "websocket.js": "^0.1.12" }, "devDependencies": { - "@storybook/addon-actions": "^3.1.2", - "@storybook/react": "^3.1.3", + "@storybook/addon-actions": "^3.1.6", + "@storybook/react": "^3.1.6", "babel-eslint": "^7.2.3", "chai": "^4.0.1", "chai-enzyme": "^0.7.1", - "enzyme": "^2.8.2", + "enzyme": "^2.9.1", "eslint": "^3.19.0", "eslint-plugin-jsx-a11y": "^4.0.0", "eslint-plugin-react": "^6.10.3", "jsdom": "^10.1.0", "mocha": "^3.4.1", "react-intl-translations-manager": "^5.0.0", - "react-test-renderer": "^15.5.4", - "sinon": "^2.3.4", + "react-test-renderer": "^15.6.1", + "sinon": "^2.3.5", "webpack-dev-server": "lencioni/webpack-dev-server#patch-1", - "yargs": "^8.0.1" + "yargs": "^8.0.2" }, "optionalDependencies": { "fsevents": "*" diff --git a/yarn.lock b/yarn.lock index d1a1687a0e..b8af49d629 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,37 +2,38 @@ # yarn lockfile v1 -"@storybook/addon-actions@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-3.1.2.tgz#0f28ae69277964d098fcd5e3f4a74b09a233a48c" +"@storybook/addon-actions@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-3.1.6.tgz#0cbf00ede57ff00d1dfe02e554043d6963940064" dependencies: - "@storybook/addons" "^3.1.2" + "@storybook/addons" "^3.1.6" deep-equal "^1.0.1" json-stringify-safe "^5.0.1" prop-types "^15.5.8" react-inspector "^2.0.0" + uuid "^3.1.0" -"@storybook/addon-links@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-3.1.2.tgz#43d34eb7ee8c056d13f854ec1fe9d1cd90889706" +"@storybook/addon-links@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-3.1.6.tgz#62c8a839e54ff0adb04c6023dae467b336ced5d9" dependencies: - "@storybook/addons" "^3.1.2" + "@storybook/addons" "^3.1.6" -"@storybook/addons@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-3.1.2.tgz#6ea3f1e14e303ff5ad481dd424211f8230df878b" +"@storybook/addons@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-3.1.6.tgz#29ef2348550f5a74d5e83dd75d04714cac751c39" -"@storybook/channel-postmessage@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-3.1.2.tgz#a8a6eec904c6d37b938db9f8251ee760b1c3556c" +"@storybook/channel-postmessage@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-3.1.6.tgz#867768a2ca2efbd796432300fe5e9b834d9c2ca5" dependencies: - "@storybook/channels" "^3.1.2" + "@storybook/channels" "^3.1.6" global "^4.3.2" json-stringify-safe "^5.0.1" -"@storybook/channels@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-3.1.2.tgz#56f9bb6acf0c2ff58123f1fccadc8498d5372b70" +"@storybook/channels@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-3.1.6.tgz#81d61591bf7613dd2bcd81d26da40aeaa2899034" "@storybook/react-fuzzy@^0.4.0": version "0.4.0" @@ -43,15 +44,15 @@ fuse.js "^3.0.1" prop-types "^15.5.9" -"@storybook/react@^3.1.3": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-3.1.3.tgz#e2d7d2ecf4d7ff3dfab1d7b324e2c19e04be5934" +"@storybook/react@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-3.1.6.tgz#9393bb987ff08ee5f49c4557d12eb84377dee5d2" dependencies: - "@storybook/addon-actions" "^3.1.2" - "@storybook/addon-links" "^3.1.2" - "@storybook/addons" "^3.1.2" - "@storybook/channel-postmessage" "^3.1.2" - "@storybook/ui" "^3.1.3" + "@storybook/addon-actions" "^3.1.6" + "@storybook/addon-links" "^3.1.6" + "@storybook/addons" "^3.1.6" + "@storybook/channel-postmessage" "^3.1.6" + "@storybook/ui" "^3.1.6" airbnb-js-shims "^1.1.1" autoprefixer "^7.1.1" babel-core "^6.24.1" @@ -72,6 +73,8 @@ express "^4.15.3" file-loader "^0.11.1" find-cache-dir "^1.0.0" + glamor "^2.20.25" + glamorous "^3.22.1" global "^4.3.2" json-loader "^0.5.4" json-stringify-safe "^5.0.1" @@ -94,9 +97,9 @@ webpack-dev-middleware "^1.10.2" webpack-hot-middleware "^2.18.0" -"@storybook/ui@^3.1.3": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-3.1.3.tgz#806c6bc4474b1437edf305c50b88662869ad5c31" +"@storybook/ui@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-3.1.6.tgz#5d47c6003a2d78c06ede43861089747d986d918e" dependencies: "@storybook/react-fuzzy" "^0.4.0" babel-runtime "^6.23.0" @@ -204,10 +207,12 @@ ajv@^4.7.0, ajv@^4.9.1: json-stable-stringify "^1.0.1" ajv@^5.0.0, ajv@^5.1.5: - version "5.1.5" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.1.5.tgz#8734931b601f00d4feef7c65738d77d1b65d1f68" + version "5.2.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.0.tgz#c1735024c5da2ef75cc190713073d44f098bf486" dependencies: co "^4.6.0" + fast-deep-equal "^0.1.0" + json-schema-traverse "^0.3.0" json-stable-stringify "^1.0.1" align-text@^0.1.1, align-text@^0.1.3: @@ -238,6 +243,10 @@ ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -397,8 +406,8 @@ async@^1.5.2: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" async@^2.1.2, async@^2.1.4, async@^2.1.5: - version "2.4.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" + version "2.5.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d" dependencies: lodash "^4.14.0" @@ -648,11 +657,11 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-loader@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.0.0.tgz#2e43a66bee1fff4470533d0402c8a4532fafbaf7" +babel-loader@^7.0.0, babel-loader@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.0.tgz#3fbf2581f085774bd9642dca9990e6d6c1491144" dependencies: - find-cache-dir "^0.1.1" + find-cache-dir "^1.0.0" loader-utils "^1.0.2" mkdirp "^0.5.1" @@ -1057,9 +1066,9 @@ babel-plugin-transform-react-jsx@6.24.1, babel-plugin-transform-react-jsx@^6.24. babel-plugin-syntax-jsx "^6.8.0" babel-runtime "^6.22.0" -babel-plugin-transform-react-remove-prop-types@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.5.tgz#79d1958437ae23d4fbc0b11d1a041498ddb23877" +babel-plugin-transform-react-remove-prop-types@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.6.tgz#c3d20ff4e97fb08fa63e86a97b2daab6ad365a19" dependencies: babel-traverse "^6.24.1" @@ -1316,8 +1325,8 @@ babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.25 to-fast-properties "^1.0.1" babylon@^6.17.0, babylon@^6.17.2: - version "6.17.3" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.3.tgz#1327d709950b558f204e5352587fd0290f8d8e48" + version "6.17.4" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" babylon@~5.8.3: version "5.8.38" @@ -1337,13 +1346,17 @@ balanced-match@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.2.1.tgz#7bc658b4bed61eee424ad74f75f5c3e2c4df3cc7" -balanced-match@^0.4.1, balanced-match@^0.4.2: +balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + base64-js@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" + version "1.2.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" batch@0.6.1: version "0.6.1" @@ -1370,8 +1383,8 @@ block-stream@*: inherits "~2.0.0" bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.6" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + version "4.11.7" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.7.tgz#ddb048e50d9482790094c13eb3fcfc833ce7ab46" bonjour@^3.5.0: version "3.5.0" @@ -1399,10 +1412,10 @@ bowser@^1.6.0: resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.0.tgz#169de4018711f994242bff9a8009e77a1f35e003" brace-expansion@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" dependencies: - balanced-match "^0.4.1" + balanced-match "^1.0.0" concat-map "0.0.1" braces@^1.8.2: @@ -1413,6 +1426,10 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +brcast@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/brcast/-/brcast-2.0.0.tgz#9e627ab82209895664c1d6c1f45cd8c43422e3f6" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -1480,11 +1497,11 @@ browserslist@^1.3.6, browserslist@^1.4.0, browserslist@^1.5.2, browserslist@^1.7 electron-to-chromium "^1.2.7" browserslist@^2.1.2, browserslist@^2.1.3: - version "2.1.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.1.4.tgz#cc526af4a1312b7d2e05653e56d0c8ab70c0e053" + version "2.1.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.1.5.tgz#e882550df3d1cd6d481c1a3e0038f2baf13a4711" dependencies: - caniuse-lite "^1.0.30000670" - electron-to-chromium "^1.3.11" + caniuse-lite "^1.0.30000684" + electron-to-chromium "^1.3.14" buffer-indexof@^1.0.0: version "1.1.0" @@ -1561,12 +1578,12 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000683" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000683.tgz#58b57ed1e0bb9da54eaf1462985147bbe16679fa" + version "1.0.30000696" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000696.tgz#e71f5c61e1f96c7a3af4e791ac5db55e11737604" -caniuse-lite@^1.0.30000670: - version "1.0.30000683" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000683.tgz#a7573707cf2acc9217ca6484d1dfbc9f13898364" +caniuse-lite@^1.0.30000670, caniuse-lite@^1.0.30000684: + version "1.0.30000696" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000696.tgz#30f2695d2a01a0dfd779a26ab83f4d134b3da5cc" case-sensitive-paths-webpack-plugin@^2.0.0: version "2.1.1" @@ -1662,8 +1679,8 @@ circular-json@^0.3.1: resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" clap@^1.0.9: - version "1.1.3" - resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.3.tgz#b3bd36e93dd4cbfb395a3c26896352445265c05b" + version "1.2.0" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.0.tgz#59c90fe3e137104746ff19469a27a634ff68c857" dependencies: chalk "^1.1.3" @@ -1697,14 +1714,13 @@ cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -clone-deep@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" +clone-deep@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.3.0.tgz#348c61ae9cdbe0edfe053d91ff4cc521d790ede8" dependencies: - for-own "^0.1.3" + for-own "^1.0.0" is-plain-object "^2.0.1" - kind-of "^3.0.2" - lazy-cache "^1.0.3" + kind-of "^3.2.2" shallow-clone "^0.1.2" clone@^1.0.2: @@ -1726,8 +1742,8 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" collapse-white-space@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.2.tgz#9c463fb9c6d190d2dcae21a356a01bcae9eeef6d" + version "1.0.3" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.3.tgz#4b906f670e5a963a87b76b0e1689643341b6023c" color-convert@^1.3.0: version "1.9.0" @@ -1771,12 +1787,18 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.9.0, commander@^2.8.1, commander@^2.9.0: +commander@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: graceful-readlink ">= 1.0.0" +commander@^2.8.1, commander@^2.9.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.10.0.tgz#e1f5d3245de246d1a5ca04702fa1ad1bd7e405fe" + dependencies: + graceful-readlink ">= 1.0.0" + common-tags@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0" @@ -1937,9 +1959,9 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@^15.5.2, create-react-class@^15.5.3: - version "15.5.3" - resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.5.3.tgz#fb0f7cae79339e9a179e194ef466efa3923820fe" +create-react-class@^15.5.2, create-react-class@^15.5.3, create-react-class@^15.6.0: + version "15.6.0" + resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4" dependencies: fbjs "^0.8.9" loose-envify "^1.3.1" @@ -2397,7 +2419,7 @@ ejs@^2.5.6: version "2.5.6" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88" -electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.11: +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.14: version "1.3.14" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz#64af0f9efd3c3c6acd57d71f83b49ca7ee9c4b43" @@ -2464,20 +2486,20 @@ entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" -enzyme@^2.8.2: - version "2.8.2" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.8.2.tgz#6c8bcb05012abc4aa4bc3213fb23780b9b5b1714" +enzyme@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.9.1.tgz#07d5ce691241240fb817bf2c4b18d6e530240df6" dependencies: cheerio "^0.22.0" function.prototype.name "^1.0.0" is-subset "^0.1.1" - lodash "^4.17.2" + lodash "^4.17.4" object-is "^1.0.1" object.assign "^4.0.4" - object.entries "^1.0.3" - object.values "^1.0.3" - prop-types "^15.5.4" - uuid "^2.0.3" + object.entries "^1.0.4" + object.values "^1.0.4" + prop-types "^15.5.10" + uuid "^3.0.1" errno@^0.1.3: version "0.1.4" @@ -2679,24 +2701,20 @@ esquery@^1.0.0: estraverse "^4.0.0" esrecurse@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" dependencies: - estraverse "~4.1.0" + estraverse "^4.1.0" object-assign "^4.0.1" estraverse@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" -estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" -estraverse@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" - esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -2820,10 +2838,18 @@ extsprintf@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" +fast-deep-equal@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-0.1.0.tgz#5c6f4599aba6b333ee3342e2ed978672f1001f8d" + fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" +fast-memoize@^2.2.7: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.2.7.tgz#f145c5c22039cedf0a1d4ff6ca592ad0268470ca" + fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" @@ -2840,7 +2866,7 @@ faye-websocket@~0.11.0: dependencies: websocket-driver ">=0.5.1" -fbjs@^0.8.4, fbjs@^0.8.9: +fbjs@^0.8.4, fbjs@^0.8.8, fbjs@^0.8.9: version "0.8.12" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04" dependencies: @@ -2902,14 +2928,6 @@ finalhandler@~1.0.3: statuses "~1.3.1" unpipe "~1.0.0" -find-cache-dir@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" - dependencies: - commondir "^1.0.1" - mkdirp "^0.5.1" - pkg-dir "^1.0.0" - find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" @@ -2945,8 +2963,8 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" follow-redirects@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.3.tgz#01abaeca85e3609837d9fcda3167a7e42fdaca21" + version "1.2.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.4.tgz#355e8f4d16876b43f577b0d5ce2668b9723214ea" dependencies: debug "^2.4.5" @@ -2962,12 +2980,18 @@ for-in@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" -for-own@^0.1.3, for-own@^0.1.4: +for-own@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" dependencies: for-in "^1.0.1" +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + dependencies: + for-in "^1.0.1" + foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" @@ -3036,11 +3060,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" fsevents@*, fsevents@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff" + version "1.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" dependencies: nan "^2.3.0" - node-pre-gyp "^0.6.29" + node-pre-gyp "^0.6.36" fstream-ignore@^1.0.5: version "1.0.5" @@ -3072,8 +3096,8 @@ function.prototype.name@^1.0.0: is-callable "^1.1.2" fuse.js@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.0.4.tgz#cd4e2ec63ac324e28f5d17fcc3d01584379d3717" + version "3.0.5" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.0.5.tgz#b58d85878802321de94461654947b93af1086727" fuzzysearch@^1.0.3: version "1.0.3" @@ -3137,6 +3161,25 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +glamor@^2.20.25: + version "2.20.25" + resolved "https://registry.yarnpkg.com/glamor/-/glamor-2.20.25.tgz#71b84b82b67a9327771ac59de53ee915d148a4a3" + dependencies: + babel-runtime "^6.18.0" + fbjs "^0.8.8" + object-assign "^4.1.0" + prop-types "^15.5.8" + +glamorous@^3.22.1: + version "3.23.4" + resolved "https://registry.yarnpkg.com/glamorous/-/glamorous-3.23.4.tgz#a1e5f8045c332850105777dea4d3b21c5bdc4796" + dependencies: + brcast "^2.0.0" + fast-memoize "^2.2.7" + html-tag-names "^1.1.1" + react-html-attributes "^1.3.0" + svg-tag-names "^1.1.0" + glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -3282,6 +3325,10 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -3299,10 +3346,11 @@ hash-base@^2.0.0: inherits "^2.0.1" hash.js@^1.0.0, hash.js@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" + version "1.1.2" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.2.tgz#bf5c887825cfe40b9efde7bf11bd2db26e6bf01b" dependencies: - inherits "^2.0.1" + inherits "^2.0.3" + minimalistic-assert "^1.0.0" hawk@~3.1.3: version "3.1.3" @@ -3314,8 +3362,8 @@ hawk@~3.1.3: sntp "1.x.x" history@^4.5.1, history@^4.6.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/history/-/history-4.6.1.tgz#911cf8eb65728555a94f2b12780a0c531a14d2fd" + version "4.6.3" + resolved "https://registry.yarnpkg.com/history/-/history-4.6.3.tgz#6d723a8712c581d6bef37e8c26f4aedc6eb86967" dependencies: invariant "^2.2.1" loose-envify "^1.2.0" @@ -3347,8 +3395,8 @@ home-or-tmp@^2.0.0: os-tmpdir "^1.0.1" hosted-git-info@^2.1.4: - version "2.4.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" hpack.js@^2.1.6: version "2.1.6" @@ -3363,6 +3411,10 @@ html-comment-regex@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" +html-element-attributes@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/html-element-attributes/-/html-element-attributes-1.3.0.tgz#f06ebdfce22de979db82020265cac541fb17d4fc" + html-encoding-sniffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz#79bf7a785ea495fe66165e734153f363ff5437da" @@ -3373,6 +3425,10 @@ html-entities@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" +html-tag-names@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-tag-names/-/html-tag-names-1.1.2.tgz#f65168964c5a9c82675efda882875dcb2a875c22" + html@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/html/-/html-1.0.0.tgz#a544fa9ea5492bfb3a2cca8210a10be7b5af1f61" @@ -3494,7 +3550,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -3507,8 +3563,8 @@ ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" inline-style-prefixer@^3.0.2: - version "3.0.5" - resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-3.0.5.tgz#0092881b3a2eadf1bd619dc43726557316f76042" + version "3.0.6" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-3.0.6.tgz#b27fe309b4168a31eaf38c8e8c60ab9e7c11731f" dependencies: bowser "^1.6.0" css-in-js-utils "^1.0.3" @@ -3541,9 +3597,9 @@ interpret@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" -intersection-observer@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.3.0.tgz#80d581c5507de1114d43a8591952927bb23480a7" +intersection-observer@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.3.2.tgz#9ed30021c08b29e9e8565c8d512ed84515727433" intl-format-cache@^2.0.5: version "2.0.5" @@ -3893,6 +3949,10 @@ json-loader@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -3952,7 +4012,7 @@ kind-of@^2.0.1: dependencies: is-buffer "^1.0.2" -kind-of@^3.0.2: +kind-of@^3.0.2, kind-of@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" dependencies: @@ -4023,7 +4083,7 @@ loader-utils@^0.2.16: json5 "^0.5.0" object-assign "^4.0.1" -loader-utils@^1.0.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.x: +loader-utils@^1.0.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" dependencies: @@ -4531,7 +4591,7 @@ node-libs-browser@^2.0.0: util "^0.10.3" vm-browserify "0.0.4" -node-pre-gyp@^0.6.29, node-pre-gyp@^0.6.4: +node-pre-gyp@^0.6.36, node-pre-gyp@^0.6.4: version "0.6.36" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" dependencies: @@ -4591,8 +4651,8 @@ nopt@^4.0.1: osenv "^0.1.4" normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.3.8" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb" + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" dependencies: hosted-git-info "^2.1.4" is-builtin-module "^1.0.0" @@ -4624,9 +4684,9 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2, npmlog@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5" +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2, npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" @@ -4648,8 +4708,8 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" "nwmatcher@>= 1.3.9 < 2.0.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.0.tgz#b4389362170e7ef9798c3c7716d80ebc0106fccf" + version "1.4.1" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.1.tgz#7ae9b07b0ea804db7e25f05cb5fe4097d4e4949f" oauth-sign@~0.8.1: version "0.8.2" @@ -4683,7 +4743,7 @@ object.assign@^4.0.4: function-bind "^1.1.0" object-keys "^1.0.10" -object.entries@^1.0.3: +object.entries@^1.0.3, object.entries@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f" dependencies: @@ -4706,7 +4766,7 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" -object.values@^1.0.3: +object.values@^1.0.3, object.values@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a" dependencies: @@ -4952,8 +5012,8 @@ pg-connection-string@0.1.3: resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" pg-pool@1.*: - version "1.7.1" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-1.7.1.tgz#421105cb7469979dcc48d6fc4fe3fe4659437437" + version "1.8.0" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-1.8.0.tgz#f7ec73824c37a03f076f51bfdf70e340147c4f37" dependencies: generic-pool "2.4.3" object-assign "4.1.0" @@ -4968,9 +5028,9 @@ pg-types@1.*: postgres-date "~1.0.0" postgres-interval "^1.1.0" -pg@^6.2.4: - version "6.2.4" - resolved "https://registry.yarnpkg.com/pg/-/pg-6.2.4.tgz#4f7ede70241e97506627d5d6078360701a647c45" +pg@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-6.4.0.tgz#cb76ba2e7c2eab89fc64bf7a9fe648ced72436dc" dependencies: buffer-writer "1.0.1" packet-reader "0.3.1" @@ -5014,12 +5074,6 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" - dependencies: - find-up "^1.0.0" - pkg-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" @@ -5164,7 +5218,7 @@ postcss-flexbugs-fixes@^3.0.0: dependencies: postcss "^6.0.1" -postcss-load-config@^1.x: +postcss-load-config@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a" dependencies: @@ -5187,14 +5241,14 @@ postcss-load-plugins@^2.3.0: cosmiconfig "^2.1.1" object-assign "^4.1.0" -postcss-loader@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.0.5.tgz#c19d3e8b83eb1ac316f5621ef4c0ef5b3d1b8b3a" +postcss-loader@^2.0.5, postcss-loader@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.0.6.tgz#8c7e0055a3df1889abc6bad52dd45b2f41bbc6fc" dependencies: - loader-utils "^1.x" - postcss "^6.x" - postcss-load-config "^1.x" - schema-utils "^0.x" + loader-utils "^1.1.0" + postcss "^6.0.2" + postcss-load-config "^1.2.0" + schema-utils "^0.3.0" postcss-media-minmax@^2.1.0: version "2.1.2" @@ -5380,10 +5434,10 @@ postcss-sass@^0.1.0: postcss "^5.2.6" postcss-scss@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-1.0.1.tgz#71e6baa2bc5688ffc5bca6abc4a8199badea8fb6" + version "1.0.2" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-1.0.2.tgz#ff45cf3354b879ee89a4eb68680f46ac9bb14f94" dependencies: - postcss "^6.0.1" + postcss "^6.0.3" postcss-selector-matches@^2.0.0: version "2.0.5" @@ -5467,13 +5521,13 @@ postcss@^5.0.0, postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0. source-map "^0.5.6" supports-color "^3.2.3" -postcss@^6.0.0, postcss@^6.0.1, postcss@^6.x: - version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2" +postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.2, postcss@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.3.tgz#b7f565b3d956fbb8565ca7c1e239d0506e427d8b" dependencies: chalk "^1.1.3" source-map "^0.5.6" - supports-color "^3.2.3" + supports-color "^4.0.0" postgres-array@~1.0.0: version "1.0.2" @@ -5557,12 +5611,12 @@ promise-each@^2.2.0: any-promise "^0.1.0" promise@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" dependencies: asap "~2.0.3" -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@~15.5.7: +prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9: version "15.5.10" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" dependencies: @@ -5643,9 +5697,9 @@ raf@^3.1.0: dependencies: performance-now "^2.1.0" -rails-ujs@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/rails-ujs/-/rails-ujs-5.1.1.tgz#0d6e660533141f1ac352830b31c5bfe1b58f79aa" +rails-ujs@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/rails-ujs/-/rails-ujs-5.1.2.tgz#94919e35e7fa07467223e9c81444704593559ef5" randomatic@^1.1.3: version "1.1.7" @@ -5680,16 +5734,16 @@ react-addons-perf@^15.4.2: fbjs "^0.8.4" object-assign "^4.1.0" -react-addons-shallow-compare@>=0.14.0, react-addons-shallow-compare@^15.5.2: - version "15.5.2" - resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.5.2.tgz#7cb0ee7acc8d7c93fcc202df0bf47ba916a7bdad" +react-addons-shallow-compare@>=0.14.0, react-addons-shallow-compare@^15.6.0: + version "15.6.0" + resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.6.0.tgz#b7a4e5ff9f2704c20cf686dd8a05dd08b26de252" dependencies: fbjs "^0.8.4" object-assign "^4.1.0" react-docgen@^2.15.0: - version "2.15.0" - resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-2.15.0.tgz#11aced462256e4862b14e6af6e46bdcefacb28bb" + version "2.16.0" + resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-2.16.0.tgz#03c9eba935de8031d791ab62657b7b6606ec5da6" dependencies: async "^2.1.4" babel-runtime "^6.9.2" @@ -5699,14 +5753,18 @@ react-docgen@^2.15.0: node-dir "^0.1.10" recast "^0.11.5" -react-dom@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.0.tgz#8bc23cb0c80e706355b76ca9f8ce47cf7bdfb6d1" +react-dom-factories@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.0.tgz#f43c05e5051b304f33251618d5bc859b29e46b6d" + +react-dom@^15.6.1: + version "15.6.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.1.tgz#2cb0ed4191038e53c209eb3a79a23e2a4cf99470" dependencies: fbjs "^0.8.9" loose-envify "^1.1.0" object-assign "^4.1.0" - prop-types "~15.5.7" + prop-types "^15.5.10" react-element-to-jsx-string@^5.0.0: version "5.0.7" @@ -5719,6 +5777,12 @@ react-element-to-jsx-string@^5.0.0: stringify-object "2.4.0" traverse "^0.6.6" +react-html-attributes@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-html-attributes/-/react-html-attributes-1.3.0.tgz#c97896e9cac47ad9c4e6618b835029a826f5d28c" + dependencies: + html-element-attributes "^1.0.0" + react-immutable-proptypes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#023d6f39bb15c97c071e9e60d00d136eac5fa0b4" @@ -5773,14 +5837,15 @@ react-komposer@^2.0.0: shallowequal "^0.2.2" react-modal@^1.7.6, react-modal@^1.7.7: - version "1.7.12" - resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-1.7.12.tgz#d3e29c07ddff8cd44c723a92dcd15e66a51a95bc" + version "1.9.7" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-1.9.7.tgz#07ef56790b953e3b98ef1e2989e347983c72871d" dependencies: create-react-class "^15.5.2" element-class "^0.2.0" exenv "1.2.0" lodash.assign "^4.2.0" prop-types "^15.5.7" + react-dom-factories "^1.0.0" react-motion@^0.5.0: version "0.5.0" @@ -5790,11 +5855,11 @@ react-motion@^0.5.0: prop-types "^15.5.8" raf "^3.1.0" -react-notification@^6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/react-notification/-/react-notification-6.7.0.tgz#58f16976b0850d6a6c70e078796b82032d5301e2" +react-notification@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/react-notification/-/react-notification-6.7.1.tgz#fec45cc6d369f4bbf7b4072fe58ddb3bc262c898" dependencies: - prop-types "^15.5.8" + prop-types "^15.5.10" react-redux-loading-bar@^2.9.2: version "2.9.2" @@ -5882,16 +5947,16 @@ react-swipeable@^4.0.1: dependencies: prop-types "^15.5.8" -react-test-renderer@^15.5.4: - version "15.5.4" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.5.4.tgz#d4ebb23f613d685ea8f5390109c2d20fbf7c83bc" +react-test-renderer@^15.6.1: + version "15.6.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.6.1.tgz#026f4a5bb5552661fd2cc4bbcd0d4bc8a35ebf7e" dependencies: fbjs "^0.8.9" object-assign "^4.1.0" -react-textarea-autosize@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-5.0.6.tgz#a3742e0a319484021b4dbfa1519df287768f2133" +react-textarea-autosize@^5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-5.0.7.tgz#cad511cf1111ab1482fbc8bd679d5d41e8e52b1f" dependencies: prop-types "^15.5.8" @@ -5902,8 +5967,8 @@ react-toggle@^4.0.1: classnames "^2.2.5" react-virtualized@^9.7.4: - version "9.7.6" - resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.7.6.tgz#6be9e7ae8b214a848116f1ebd48ff1aac26fcc2f" + version "9.8.0" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.8.0.tgz#7c1fe9b723ce39a1c4916cabe1c4f1bda5dbc04b" dependencies: babel-runtime "^6.11.6" classnames "^2.2.3" @@ -5911,15 +5976,15 @@ react-virtualized@^9.7.4: loose-envify "^1.3.0" prop-types "^15.5.4" -react@>=0.14.0, react@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/react/-/react-15.6.0.tgz#c23299b48e30ed302508ce89e1a02c919f826bce" +react@>=0.14.0, react@^15.6.1: + version "15.6.1" + resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df" dependencies: - create-react-class "^15.5.2" + create-react-class "^15.6.0" fbjs "^0.8.9" loose-envify "^1.1.0" object-assign "^4.1.0" - prop-types "^15.5.7" + prop-types "^15.5.10" read-cache@^1.0.0: version "1.0.0" @@ -5958,14 +6023,14 @@ read-pkg@^2.0.0: path-type "^2.0.0" readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9: - version "2.2.11" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a8088d34aa17c0f72" + version "2.3.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.2.tgz#5a04df05e4f57fe3f0dc68fdd11dc5c97c7e6f4d" dependencies: core-util-is "~1.0.0" - inherits "~2.0.1" + inherits "~2.0.3" isarray "~1.0.0" process-nextick-args "~1.0.6" - safe-buffer "~5.0.1" + safe-buffer "~5.1.0" string_decoder "~1.0.0" util-deprecate "~1.0.1" @@ -6046,14 +6111,14 @@ redux-thunk@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5" -redux@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d" +redux@^3.6.0, redux@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.1.tgz#bfc535c757d3849562ead0af18ac52122cd7268e" dependencies: lodash "^4.2.1" lodash-es "^4.2.1" loose-envify "^1.1.0" - symbol-observable "^1.0.2" + symbol-observable "^1.0.3" regenerate@^1.2.1: version "1.3.2" @@ -6206,9 +6271,9 @@ resolve-pathname@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.1.0.tgz#e8358801b86b83b17560d4e3c382d7aef2100944" -resolve-url-loader@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-2.0.3.tgz#f54cd1b040e8f0ab72b2cb32c9fbb8544152d9e9" +resolve-url-loader@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-2.1.0.tgz#27c95cc16a4353923fdbdc2dbaf5eef22232c477" dependencies: adjust-sourcemap-loader "^1.1.0" camelcase "^4.0.0" @@ -6285,9 +6350,9 @@ safe-buffer@5.0.1, safe-buffer@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" -safe-buffer@^5.0.1, safe-buffer@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" samsam@1.x, samsam@^1.1.3: version "1.2.1" @@ -6302,21 +6367,21 @@ sass-graph@^2.1.1: scss-tokenizer "^0.2.3" yargs "^7.0.0" -sass-loader@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.5.tgz#a847910f36442aa56c5985879d54eb519e24a328" +sass-loader@^6.0.6: + version "6.0.6" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.6.tgz#e9d5e6c1f155faa32a4b26d7a9b7107c225e40f9" dependencies: async "^2.1.5" - clone-deep "^0.2.4" + clone-deep "^0.3.0" loader-utils "^1.0.1" lodash.tail "^4.1.1" - pify "^2.3.0" + pify "^3.0.0" sax@^1.2.1, sax@~1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" -schema-utils@^0.3.0, schema-utils@^0.x: +schema-utils@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf" dependencies: @@ -6466,9 +6531,9 @@ signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -sinon@^2.3.4: - version "2.3.4" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.3.4.tgz#466ad8d1bae86d6db51aa218b92e997bc3e5db88" +sinon@^2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.3.5.tgz#9a2fc0ff8d526da716f30953aa2c65d518917f6c" dependencies: diff "^3.1.0" formatio "1.2.0" @@ -6667,8 +6732,8 @@ stream-browserify@^2.0.1: readable-stream "^2.0.2" stream-http@^2.3.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.1.tgz#546a51741ad5a6b07e9e31b0b10441a917df528a" + version "2.7.2" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" @@ -6693,11 +6758,11 @@ string-width@^1.0.1, string-width@^1.0.2: strip-ansi "^3.0.0" string-width@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" + version "2.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.0.tgz#030664561fc146c9423ec7d978fe2457437fe6d0" dependencies: is-fullwidth-code-point "^2.0.0" - strip-ansi "^3.0.0" + strip-ansi "^4.0.0" string.prototype.padend@^3.0.0: version "3.0.0" @@ -6720,10 +6785,10 @@ string_decoder@^0.10.25: resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" string_decoder@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.2.tgz#b29e1f4e1125fa97a10382b8a533737b7491e179" + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" dependencies: - safe-buffer "~5.0.1" + safe-buffer "~5.1.0" stringify-object@2.4.0: version "2.4.0" @@ -6736,9 +6801,9 @@ stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" -stringz@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/stringz/-/stringz-0.2.1.tgz#9f134564e086b4a35f99b6efc1dd88a8d9cb7e4e" +stringz@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stringz/-/stringz-0.2.2.tgz#0c23c48c4933928be4fee8e2c83f71c3b1e077ba" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" @@ -6746,6 +6811,12 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -6805,6 +6876,16 @@ supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.2.3: dependencies: has-flag "^1.0.0" +supports-color@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.0.0.tgz#33a7c680aa512c9d03ef929cacbb974d203d2790" + dependencies: + has-flag "^2.0.0" + +svg-tag-names@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/svg-tag-names/-/svg-tag-names-1.1.1.tgz#9641b29ef71025ee094c7043f7cdde7d99fbd50a" + svgo@^0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" @@ -6817,7 +6898,7 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" -symbol-observable@^1.0.2: +symbol-observable@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" @@ -6979,8 +7060,8 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" ua-parser-js@^0.7.9: - version "0.7.12" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" + version "0.7.13" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.13.tgz#cd9dd2f86493b3f44dbeeef3780fda74c5ee14be" uglify-js@^2.8.27, uglify-js@^2.8.29: version "2.8.29" @@ -7040,8 +7121,8 @@ urix@^0.1.0, urix@~0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" url-loader@^0.5.8: - version "0.5.8" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.8.tgz#b9183b1801e0f847718673673040bc9dc1c715c5" + version "0.5.9" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.9.tgz#cc8fea82c7b906e7777019250869e569e995c295" dependencies: loader-utils "^1.0.2" mime "1.3.x" @@ -7091,13 +7172,13 @@ utils-merge@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" -uuid@^2.0.2, uuid@^2.0.3: +uuid@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" -uuid@^3.0.0, uuid@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" +uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" uws@^0.14.5: version "0.14.5" @@ -7325,9 +7406,9 @@ websocket-extensions@>=0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" -websocket.js@^0.1.10: - version "0.1.10" - resolved "https://registry.yarnpkg.com/websocket.js/-/websocket.js-0.1.10.tgz#6bad57a24d0210561018294b49234d21fa9fd7ee" +websocket.js@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/websocket.js/-/websocket.js-0.1.12.tgz#46c980787c57ebc8edcf44a0263e5d639367b85b" dependencies: backoff "^2.4.1" @@ -7490,9 +7571,9 @@ yargs@^7.0.0: y18n "^3.2.1" yargs-parser "^5.0.0" -yargs@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.1.tgz#420ef75e840c1457a80adcca9bc6fa3849de51aa" +yargs@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" dependencies: camelcase "^4.1.0" cliui "^3.2.0" From be92babd008a7356738dec1aa7f36500638d00e5 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Tue, 27 Jun 2017 13:46:37 +0200 Subject: [PATCH 31/37] Responsive images in media gallery (#3963) * feat(components/media_gallery): Responsive images * fix(components/media_gallery): Link to image URL --- .../mastodon/components/media_gallery.js | 18 ++++++++++++++---- app/javascript/styles/components.scss | 11 ++++++++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index 78ff351308..2cb1ce51c8 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -85,14 +85,24 @@ class Item extends React.PureComponent { let thumbnail = ''; if (attachment.get('type') === 'image') { + const previewUrl = attachment.get('preview_url'); + const previewWidth = attachment.getIn(['meta', 'small', 'width']); + + const originalUrl = attachment.get('url'); + const originalWidth = attachment.getIn(['meta', 'original', 'width']); + + const srcSet = `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w`; + const sizes = `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw`; + thumbnail = ( - + > + + ); } else if (attachment.get('type') === 'gifv') { const autoPlay = !isIOS() && this.props.autoPlayGif; diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 91ebd91fd1..84732ed4a9 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -3453,10 +3453,15 @@ button.icon-button.active i.fa-retweet { background-repeat: no-repeat; background-size: cover; cursor: zoom-in; - display: block; - height: 100%; + display: flex; + align-items: center; text-decoration: none; - width: 100%; + height: 100%; + + &, + img { + width: 100%; + } } .media-gallery__gifv { From da9317fa56d2b38e74781aa3a8dc6920298f824d Mon Sep 17 00:00:00 2001 From: Debanshu Kundu Date: Tue, 27 Jun 2017 17:48:53 +0530 Subject: [PATCH 32/37] #1456 Added rake task to add a user. (#1482) --- lib/tasks/mastodon.rake | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 6c7326b74c..0e182c7556 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -42,6 +42,37 @@ namespace :mastodon do end end + desc 'Add a user by providing their email, username and initial password.' \ + 'The user will receive a confirmation email, then they must reset their password before logging in.' + task add_user: :environment do + print 'Enter email: ' + email = STDIN.gets.chomp + + print 'Enter username: ' + username = STDIN.gets.chomp + + print 'Create user and send them confirmation mail [y/N]: ' + confirm = STDIN.gets.chomp + puts + + if confirm.casecmp?('y') + password = SecureRandom.hex + user = User.new(email: email, password: password, account_attributes: { username: username }) + if user.save + puts 'User added and confirmation mail sent to user\'s email address.' + puts "Here is the random password generated for the user: #{password}" + else + puts 'Following errors occured while creating new user:' + user.errors.each do |key, val| + puts "#{key}: #{val}" + end + end + else + puts 'Aborted by user.' + end + puts + end + namespace :media do desc 'Removes media attachments that have not been assigned to any status for longer than a day' task clear: :environment do From 16d0aed403485e5a98afd9cbf7e35bb929443731 Mon Sep 17 00:00:00 2001 From: Midgard Date: Tue, 27 Jun 2017 14:22:36 +0200 Subject: [PATCH 33/37] Use instance name in email notifications instead of "Mastodon" (#3763) * Use instance name in "password changed" mail instead of "Mastodon". Fixes tootsuite#2620. * Use instance name in password reset mail instead of "Mastodon". --- app/views/user_mailer/password_change.en.html.erb | 2 +- app/views/user_mailer/password_change.en.text.erb | 2 +- app/views/user_mailer/reset_password_instructions.en.html.erb | 2 +- app/views/user_mailer/reset_password_instructions.en.text.erb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/user_mailer/password_change.en.html.erb b/app/views/user_mailer/password_change.en.html.erb index a1bc77463f..414e05a298 100644 --- a/app/views/user_mailer/password_change.en.html.erb +++ b/app/views/user_mailer/password_change.en.html.erb @@ -1,3 +1,3 @@

Hello <%= @resource.email %>!

-

We're contacting you to notify you that your password on Mastodon has been changed.

+

We're contacting you to notify you that your password on <%= @instance %> has been changed.

diff --git a/app/views/user_mailer/password_change.en.text.erb b/app/views/user_mailer/password_change.en.text.erb index 27581e6043..3ae461c97a 100644 --- a/app/views/user_mailer/password_change.en.text.erb +++ b/app/views/user_mailer/password_change.en.text.erb @@ -1,3 +1,3 @@ Hello <%= @resource.email %>! -We're contacting you to notify you that your password on Mastodon has been changed. +We're contacting you to notify you that your password on <%= @instance %> has been changed. diff --git a/app/views/user_mailer/reset_password_instructions.en.html.erb b/app/views/user_mailer/reset_password_instructions.en.html.erb index 643b43319c..cfb129e224 100644 --- a/app/views/user_mailer/reset_password_instructions.en.html.erb +++ b/app/views/user_mailer/reset_password_instructions.en.html.erb @@ -1,6 +1,6 @@

Hello <%= @resource.email %>!

-

Someone has requested a link to change your password on Mastodon. You can do this through the link below.

+

Someone has requested a link to change your password on <%= @instance %>. You can do this through the link below.

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

diff --git a/app/views/user_mailer/reset_password_instructions.en.text.erb b/app/views/user_mailer/reset_password_instructions.en.text.erb index fe73b0165c..7ed22dc2cd 100644 --- a/app/views/user_mailer/reset_password_instructions.en.text.erb +++ b/app/views/user_mailer/reset_password_instructions.en.text.erb @@ -1,6 +1,6 @@ Hello <%= @resource.email %>! -Someone has requested a link to change your password on Mastodon. You can do this through the link below. +Someone has requested a link to change your password on <%= @instance %>. You can do this through the link below. <%= edit_password_url(@resource, reset_password_token: @token) %> From 12e7c81dd8739a0f83513054c0fda22e098e2458 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 27 Jun 2017 18:07:21 +0200 Subject: [PATCH 34/37] Turn report screen into a modal (#3965) --- app/javascript/mastodon/actions/reports.js | 18 ++- .../mastodon/components/status_action_bar.js | 1 - .../account_timeline/components/header.js | 1 - .../features/status/components/action_bar.js | 1 - .../features/ui/components/modal_root.js | 2 + .../components/report_modal.js} | 72 +++++------- app/javascript/mastodon/features/ui/index.js | 3 - .../mastodon/locales/defaultMessages.json | 17 +++ app/javascript/mastodon/locales/en.json | 4 +- app/javascript/styles/components.scss | 104 +++++++----------- 10 files changed, 98 insertions(+), 125 deletions(-) rename app/javascript/mastodon/features/{report/index.js => ui/components/report_modal.js} (60%) diff --git a/app/javascript/mastodon/actions/reports.js b/app/javascript/mastodon/actions/reports.js index 9b632be745..b19a07285b 100644 --- a/app/javascript/mastodon/actions/reports.js +++ b/app/javascript/mastodon/actions/reports.js @@ -1,4 +1,5 @@ import api from '../api'; +import { openModal, closeModal } from './modal'; export const REPORT_INIT = 'REPORT_INIT'; export const REPORT_CANCEL = 'REPORT_CANCEL'; @@ -11,10 +12,14 @@ export const REPORT_STATUS_TOGGLE = 'REPORT_STATUS_TOGGLE'; export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE'; export function initReport(account, status) { - return { - type: REPORT_INIT, - account, - status, + return dispatch => { + dispatch({ + type: REPORT_INIT, + account, + status, + }); + + dispatch(openModal('REPORT')); }; }; @@ -40,7 +45,10 @@ export function submitReport() { account_id: getState().getIn(['reports', 'new', 'account_id']), status_ids: getState().getIn(['reports', 'new', 'status_ids']), comment: getState().getIn(['reports', 'new', 'comment']), - }).then(response => dispatch(submitReportSuccess(response.data))).catch(error => dispatch(submitReportFail(error))); + }).then(response => { + dispatch(closeModal()); + dispatch(submitReportSuccess(response.data)); + }).catch(error => dispatch(submitReportFail(error))); }; }; diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index edb2d6eb0a..fd7c990544 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -87,7 +87,6 @@ export default class StatusActionBar extends ImmutablePureComponent { handleReport = () => { this.props.onReport(this.props.status); - this.context.router.history.push('/report'); } handleConversationMuteClick = () => { diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js index 7f80e39e8d..167a2097e5 100644 --- a/app/javascript/mastodon/features/account_timeline/components/header.js +++ b/app/javascript/mastodon/features/account_timeline/components/header.js @@ -38,7 +38,6 @@ export default class Header extends ImmutablePureComponent { handleReport = () => { this.props.onReport(this.props.account); - this.context.router.history.push('/report'); } handleMute = () => { diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index 29080529df..5e150842e2 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -56,7 +56,6 @@ export default class ActionBar extends React.PureComponent { handleReport = () => { this.props.onReport(this.props.status); - this.context.router.history.push('/report'); } render () { diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js index 2e4f9876d9..48b048eb7e 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.js +++ b/app/javascript/mastodon/features/ui/components/modal_root.js @@ -5,6 +5,7 @@ import OnboardingModal from './onboarding_modal'; import VideoModal from './video_modal'; import BoostModal from './boost_modal'; import ConfirmationModal from './confirmation_modal'; +import ReportModal from './report_modal'; import TransitionMotion from 'react-motion/lib/TransitionMotion'; import spring from 'react-motion/lib/spring'; @@ -14,6 +15,7 @@ const MODAL_COMPONENTS = { 'VIDEO': VideoModal, 'BOOST': BoostModal, 'CONFIRM': ConfirmationModal, + 'REPORT': ReportModal, }; export default class ModalRoot extends React.PureComponent { diff --git a/app/javascript/mastodon/features/report/index.js b/app/javascript/mastodon/features/ui/components/report_modal.js similarity index 60% rename from app/javascript/mastodon/features/report/index.js rename to app/javascript/mastodon/features/ui/components/report_modal.js index bfb09e193d..c989d2c9b3 100644 --- a/app/javascript/mastodon/features/report/index.js +++ b/app/javascript/mastodon/features/ui/components/report_modal.js @@ -1,19 +1,17 @@ import React from 'react'; import { connect } from 'react-redux'; -import { changeReportComment, submitReport } from '../../actions/reports'; -import { refreshAccountTimeline } from '../../actions/timelines'; +import { changeReportComment, submitReport } from '../../../actions/reports'; +import { refreshAccountTimeline } from '../../../actions/timelines'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import Column from '../ui/components/column'; -import Button from '../../components/button'; -import { makeGetAccount } from '../../selectors'; +import { makeGetAccount } from '../../../selectors'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; -import StatusCheckBox from './containers/status_check_box_container'; +import StatusCheckBox from '../../report/containers/status_check_box_container'; import Immutable from 'immutable'; -import ColumnBackButtonSlim from '../../components/column_back_button_slim'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import Button from '../../../components/button'; const messages = defineMessages({ - heading: { id: 'report.heading', defaultMessage: 'New report' }, placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' }, submit: { id: 'report.submit', defaultMessage: 'Submit' }, }); @@ -37,11 +35,7 @@ const makeMapStateToProps = () => { @connect(makeMapStateToProps) @injectIntl -export default class Report extends React.PureComponent { - - static contextTypes = { - router: PropTypes.object, - }; +export default class ReportModal extends ImmutablePureComponent { static propTypes = { isSubmitting: PropTypes.bool, @@ -52,17 +46,15 @@ export default class Report extends React.PureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount () { - if (!this.props.account) { - this.context.router.history.replace('/'); - } + handleCommentChange = (e) => { + this.props.dispatch(changeReportComment(e.target.value)); + } + + handleSubmit = () => { + this.props.dispatch(submitReport()); } componentDidMount () { - if (!this.props.account) { - return; - } - this.props.dispatch(refreshAccountTimeline(this.props.account.get('id'))); } @@ -72,15 +64,6 @@ export default class Report extends React.PureComponent { } } - handleCommentChange = (e) => { - this.props.dispatch(changeReportComment(e.target.value)); - } - - handleSubmit = () => { - this.props.dispatch(submitReport()); - this.context.router.history.replace('/'); - } - render () { const { account, comment, intl, statusIds, isSubmitting } = this.props; @@ -89,36 +72,33 @@ export default class Report extends React.PureComponent { } return ( - - +
+
+ {account.get('acct')} }} /> +
-
-
- - {account.get('acct')} -
- -
+
+
{statusIds.map(statusId => )}
-
+